agmem 0.1.6__py3-none-any.whl → 0.2.1__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agmem
3
- Version: 0.1.6
3
+ Version: 0.2.1
4
4
  Summary: Agentic Memory Version Control System - Git for AI agent memories
5
5
  Home-page: https://github.com/vivek-tiwari-vt/agmem
6
6
  Author: agmem Team
@@ -10,7 +10,7 @@ Project-URL: Homepage, https://github.com/vivek-tiwari-vt/agmem
10
10
  Project-URL: Documentation, https://github.com/vivek-tiwari-vt/agmem#readme
11
11
  Project-URL: Repository, https://github.com/vivek-tiwari-vt/agmem
12
12
  Project-URL: Bug Tracker, https://github.com/vivek-tiwari-vt/agmem/issues
13
- Keywords: ai,agent,memory,version-control,git,vcs,llm,merkle,audit,encryption,differential-privacy,trust,multi-agent
13
+ Keywords: ai,agent,memory,version-control,git,vcs,llm,merkle,audit,encryption,differential-privacy,trust,multi-agent,health-monitoring,delta-encoding,ipfs,federated
14
14
  Classifier: Development Status :: 3 - Alpha
15
15
  Classifier: Intended Audience :: Developers
16
16
  Classifier: License :: OSI Approved :: MIT License
@@ -71,6 +71,10 @@ Provides-Extra: ipfs
71
71
  Requires-Dist: requests>=2.28.0; extra == "ipfs"
72
72
  Provides-Extra: ipfs-daemon
73
73
  Requires-Dist: ipfshttpclient>=0.8.0; extra == "ipfs-daemon"
74
+ Provides-Extra: coordinator
75
+ Requires-Dist: fastapi>=0.100.0; extra == "coordinator"
76
+ Requires-Dist: uvicorn[standard]>=0.22.0; extra == "coordinator"
77
+ Requires-Dist: pydantic>=2.0.0; extra == "coordinator"
74
78
  Provides-Extra: all
75
79
  Requires-Dist: mcp>=1.0.0; extra == "all"
76
80
  Requires-Dist: cryptography>=41.0.0; extra == "all"
@@ -88,6 +92,7 @@ Requires-Dist: networkx>=3.0; extra == "all"
88
92
  Requires-Dist: tiktoken>=0.5.0; extra == "all"
89
93
  Requires-Dist: presidio-analyzer>=2.2.0; extra == "all"
90
94
  Requires-Dist: requests>=2.28.0; extra == "all"
95
+ Requires-Dist: pydantic>=2.0.0; extra == "all"
91
96
  Dynamic: author
92
97
  Dynamic: home-page
93
98
  Dynamic: license-file
@@ -132,13 +137,15 @@ agmem solves all of these problems with a familiar Git-like interface.
132
137
  - ✅ **Tamper-evident audit trail** — Append-only hash-chained log (init, add, commit, checkout, merge, push, pull, config); `agmem audit` and `agmem audit --verify`
133
138
  - ✅ **Multi-agent trust** — Trust store (full / conditional / untrusted) per public key; applied on pull/merge; clone copies remote keys
134
139
  - ✅ **Conflict resolution** — `agmem resolve` with ours/theirs/both; conflicts persisted in `.mem/merge/`; path-safe
135
- - ✅ **Differential privacy** — Epsilon/delta budget in `.mem/privacy_budget.json`; `--private` on `agmem distill` and `agmem garden`; noise applied to counts and frontmatter
140
+ - ✅ **Differential privacy** — Epsilon/delta budget in `.mem/privacy_budget.json`; `--private` on `agmem distill` and `agmem garden`; noise applies to fact-level data only (metadata fields excluded)
136
141
  - ✅ **Pack files & GC** — `agmem gc [--repack]` (reachable from refs, prune loose, optional pack file + index); ObjectStore reads from pack when loose missing
137
142
  - ✅ **Multi-provider LLM** — OpenAI and Anthropic via `memvcs.core.llm`; config/repo or env; used by gardener, distiller, consistency, merge
138
143
  - ✅ **Temporal querying** — Point-in-time and range queries in temporal index; frontmatter timestamps
139
- - ✅ **Federated collaboration** — `agmem federated push|pull`; real summaries (topic counts, fact hashes); optional DP on outbound; coordinator API in docs/FEDERATED.md
140
- - ✅ **Zero-knowledge proofs** — `agmem prove` (hash/signature-based): keyword containment (Merkle set membership), memory freshness (signed timestamp)
141
- - ✅ **Daemon health** — Periodic Merkle verification in daemon loop; safe auto-remediation hooks
144
+ - ✅ **Federated collaboration** — `agmem federated push|pull`; protocol-compliant summaries (agent_id, timestamp, topic_counts, fact_hashes); optional DP on outbound; coordinator API in docs/FEDERATED.md
145
+ - ✅ **Zero-knowledge proofs** — `agmem prove` (hash/signature-based): keyword containment (Merkle set membership), memory freshness (signed timestamp). **Note:** Current implementation is proof-of-knowledge with known limitations; see docs for migration to true zk-SNARKs.
146
+ - ✅ **Daemon health** — 4-point health monitoring (storage, redundancy, staleness, graph consistency) with periodic checks; visible warnings and JSON reports
147
+ - ✅ **Delta encoding** — 5-10x compression for similar objects using Levenshtein distance and SequenceMatcher; enabled in GC repack with multi-tier similarity filtering
148
+ - ✅ **Performance safeguards** — Multi-tier similarity filter (length ratio + SimHash) avoids O(n²×m²) worst-case comparisons
142
149
  - ✅ **GPU acceleration** — Vector store detects GPU for embedding model when available
143
150
  - ✅ **Optional** — `serve`, `daemon` (watch + auto-commit), `garden` (episode archival), MCP server; install extras as needed
144
151
 
@@ -469,8 +476,8 @@ The following 18 capabilities are implemented (or stubbed) per the agmem feature
469
476
 
470
477
  | # | Feature | Description |
471
478
  |---|---------|-------------|
472
- | **7** | **Differential privacy** | Epsilon/delta budget per repo in `.mem/privacy_budget.json`. **Usage:** `agmem distill --private`, `agmem garden --private`; blocks when budget exceeded. Config: `differential_privacy.max_epsilon`, `delta`. |
473
- | **8** | **Zero-knowledge proofs** | zk-SNARK-style proofs for keyword containment and memory freshness. **Command:** `agmem prove --memory <path> --property keyword|freshness --value <v> [-o out]` (stub). |
479
+ | **7** | **Differential privacy** | Epsilon/delta budget per repo in `.mem/privacy_budget.json`. **Usage:** `agmem distill --private`, `agmem garden --private`; blocks when budget exceeded. Config: `differential_privacy.max_epsilon`, `delta`. **Note:** Now correctly applied to actual facts during extraction, not metadata counts. |
480
+ | **8** | **Cryptographic proofs (proof-of-knowledge)** | Hash/signature-based proofs for keyword containment (Merkle set membership) and memory freshness (signed timestamp). **Command:** `agmem prove --memory <path> --property keyword\|freshness --value <v> [-o out]`. **IMPORTANT:** These are proof-of-knowledge, not true zero-knowledge proofs. Keyword proof leaks word count and allows verifier to test other words. Freshness proof relies on forgeable filesystem mtime. See `memvcs/core/zk_proofs.py` for details and migration path to zk-SNARKs. |
474
481
 
475
482
  ### Tier 4 — Storage and distribution
476
483
 
@@ -1,6 +1,6 @@
1
- agmem-0.1.6.dist-info/licenses/LICENSE,sha256=X_S6RBErW-F0IDbM3FAEoDB-zxExFnl2m8640rTXphM,1067
2
- memvcs/__init__.py,sha256=pheWPxubHVcp2N6vk6M7hGXgkJQ06KajbWgCpOlUSJ8,193
3
- memvcs/cli.py,sha256=YF06oMNjKWUmiNahILmfjrIXgoXzU-5BJFmbunSb8Sc,6075
1
+ agmem-0.2.1.dist-info/licenses/LICENSE,sha256=X_S6RBErW-F0IDbM3FAEoDB-zxExFnl2m8640rTXphM,1067
2
+ memvcs/__init__.py,sha256=PwF2IkjOfw5nZCDcZdsNKns-h-FEvRahAqNd37Ti8_8,193
3
+ memvcs/cli.py,sha256=WPjhbevcOc_w_7SEXV5oitbEA5kYY5lHWgyTOq6x8sU,6075
4
4
  memvcs/commands/__init__.py,sha256=A2D6xWaO6epU7iV4QSvqvF5TspnwRyDN7NojmGatPrE,510
5
5
  memvcs/commands/add.py,sha256=k9eM7qf2NFvneiJkFQNiAYFB2GgKmyPw_NXmkCxblQE,8736
6
6
  memvcs/commands/audit.py,sha256=E6m54B726tqDQR3rrgRXWrjE-seu2UocqrFxN1aHkY4,1680
@@ -11,10 +11,10 @@ memvcs/commands/checkout.py,sha256=xaYZSbCQ-MyLWPtwA2FdH6WqGMI3oF3R2JmCufGBVFg,3
11
11
  memvcs/commands/clean.py,sha256=e0OhSQdHfFnOPTRbyKbM8IcX4yJD5n_kaBKjIeoaRBo,1973
12
12
  memvcs/commands/clone.py,sha256=aB0LcugIWJE9IEez6y70KlpZu4eIF2EdXZxE24jXyac,3260
13
13
  memvcs/commands/commit.py,sha256=W4ulVZuEETJh1SHpscaQfNjyQMqeIE0AYZIbMbTrsq4,6801
14
- memvcs/commands/daemon.py,sha256=fV6aIz8bFP9VwB_MLudAb_lhhhBxSe2aV-Wjqe-nvPw,10708
14
+ memvcs/commands/daemon.py,sha256=FlPBh41v-S__QhC68FzRYbsumARQgLbP2904k7jaZ6k,12522
15
15
  memvcs/commands/decay.py,sha256=QcgOTMJZxrfw_AOz94YHA3LGoNXRMDn69TxWlUrpSw4,2421
16
16
  memvcs/commands/diff.py,sha256=KcgD57_fae4uvQ8G9ZbXmLpAYYIDiWiBuVcjsDtyE1U,5480
17
- memvcs/commands/distill.py,sha256=reOldqg0lMgqIlpYEIKYfN_TxNwsjU9RnI8Uah1VqTQ,3088
17
+ memvcs/commands/distill.py,sha256=eO9n2KxBaIhdvKBnqcrgYH5aoJ7dR5C2WNge_z6ITlw,3330
18
18
  memvcs/commands/federated.py,sha256=Zj4kxHnjdIs1xu4v7B8XosQXNYK8Alv4I0kJQpmJe6Y,1840
19
19
  memvcs/commands/fsck.py,sha256=AdJBMLA2myQ0cJJcjUgsYptsE3qvX4JQc9UAwVmSHlA,7772
20
20
  memvcs/commands/garden.py,sha256=8JiLe3JRkOhY-N-h-IDuvdJiECiSElnUzXVtxtU2QgY,4050
@@ -46,30 +46,37 @@ memvcs/commands/timeline.py,sha256=JkuhsQ-6wPWbsjlbJb_qM4mEkxkxcWWzniXXQB4Qtec,4
46
46
  memvcs/commands/tree.py,sha256=vdULq4vIXA_4gNfMnHn_Y78BwE0sJoeTBOnFJR3WsZ4,4927
47
47
  memvcs/commands/verify.py,sha256=04CVW5NYWkUlPJ5z1Kci6dfQFM6UmPTGZh9ZextFLMc,3887
48
48
  memvcs/commands/when.py,sha256=bxG_tEYnZNBTl2IPkoxpc2LUEbO_5ev1hRvEzxQQDmc,4773
49
+ memvcs/coordinator/__init__.py,sha256=XJEXEXJFvvhtRInPeyAC9bFNXGbshSrtuK6wZo3wS6g,139
50
+ memvcs/coordinator/server.py,sha256=M0wnww0EbtxuDaunP29LJDCnsTm1mcOn7h_fqZbQy5c,7550
49
51
  memvcs/core/__init__.py,sha256=dkIC-4tS0GhwV2mZIbofEe8xR8uiFwrxslGf1aXwhYg,493
50
52
  memvcs/core/access_index.py,sha256=HhacnzSUASzRV2jhDHkwRFoPS3rtqh9n9yE1VV7JXpk,5596
51
53
  memvcs/core/audit.py,sha256=8APkm9Spl_-1rIdyRQz1elyxOeK3nlpwm0CLkpLlhTE,3732
54
+ memvcs/core/compression_metrics.py,sha256=0JrbkCGr0hnaKlmPLqv5WVLwO3emOEz2iFhdMTDNTNY,9835
52
55
  memvcs/core/compression_pipeline.py,sha256=Vzr5v_0pgAG20C8znC0-Ho5fEwBoaTOLddxMTldd64M,5564
53
56
  memvcs/core/config_loader.py,sha256=j-jgLDp2TRzWN9ZEZebfWSfatevBNYs0FEb3ud1SIR8,8277
54
57
  memvcs/core/consistency.py,sha256=YOG8xhqZLKZCLbai2rdcP0KxYPNGFv5RRMwrQ6qCeyc,7462
55
58
  memvcs/core/constants.py,sha256=WUjAb50BFcF0mbFi_GNteDLCxLihmViBm9Fb-JMPmbM,220
56
59
  memvcs/core/crypto_verify.py,sha256=DTuC7Kfx6z2b8UWOWziBTqP633LrjXbdtGmBBqrJTF0,10424
57
60
  memvcs/core/decay.py,sha256=ROGwnqngs7eJNkbKmwyOdij607m73vpmoJqzrIDLBzk,6581
61
+ memvcs/core/delta.py,sha256=obXzojUSc2HaEUqH3L_1LF-GcJ63Wr_yYvIPM8iyeSg,7865
58
62
  memvcs/core/diff.py,sha256=koEHTLciIUxYKVJVuvmY0GDXMgDgGZP_qg5RayhF-iE,13226
59
- memvcs/core/distiller.py,sha256=ZOmrwYYhOla8rZncQP_0y0Ab9jCl3GjtdoH82HkXlsw,12621
63
+ memvcs/core/distiller.py,sha256=wwY3xQVRBjVfxnOUIwMsQCSeQ2tlG68w2-KiCwkF9yo,13844
60
64
  memvcs/core/encryption.py,sha256=epny_nlW6ylllv1qxs1mAcFq-PrLIisgfot4llOoAqw,5289
61
- memvcs/core/federated.py,sha256=vUYMZ0xv80hqGDRKq645Od1i8N33l-pIAkklJbJUlVg,5445
62
- memvcs/core/gardener.py,sha256=lBWkyE72O-JMiHM-oqrnex9k_xSv7FvztjkOdLdB0Kk,18610
65
+ memvcs/core/fast_similarity.py,sha256=phgjxkSchJg7om9AFFSMbtP6bSidyRy-vVrR3XyMmDQ,13934
66
+ memvcs/core/federated.py,sha256=qwvfhNgga-lHadbinAfKPI4oAl0RMn5ab01ChmQTP1s,5863
67
+ memvcs/core/gardener.py,sha256=bpoJbK6PJ6nvK3ytj23jpMUBUB7Nn_fB80Ap1E7-Nv8,17041
63
68
  memvcs/core/hooks.py,sha256=XF9z8J5sWjAcuOyWQ2nuvEzK0UV8s4ThrcltaBZttzw,5448
64
69
  memvcs/core/ipfs_remote.py,sha256=xmEO14bn_7Ej-W5jhx2QJyBd-ljj9S2COOxMmcZBiTs,6643
65
70
  memvcs/core/knowledge_graph.py,sha256=GY27e1rgraF2zMpz_jsumdUtpgTRk48yH5CAEQ3TDl4,16416
66
71
  memvcs/core/merge.py,sha256=x2eSaxr4f63Eq00FCJ6DDe2TZU8H5yHQpzKzMhYsaFw,19871
67
72
  memvcs/core/objects.py,sha256=Xgw1IpQnJLCG5o_7gDHVQ-TNGR9CSpDYWRXzLgLSuec,11006
68
- memvcs/core/pack.py,sha256=nTzpPNNk47e7_oN3z7bjaichpzI7q-ql2E8eI2UuGyM,9828
73
+ memvcs/core/pack.py,sha256=jtbeBh625K6nshPgBGf7zelU-BhvK5-t5NYBJPoYfgs,15961
69
74
  memvcs/core/pii_scanner.py,sha256=T6gQ1APFrSDk980fjnv4ZMF-UztbJgmUFSwGrwWixEw,10802
70
75
  memvcs/core/privacy_budget.py,sha256=fOPlxoKEAmsKtda-OJCrSaKjTyw7ekcqdN7KfRBw1CY,2113
76
+ memvcs/core/privacy_validator.py,sha256=g3l1zxSIxkjMYJMwL5yfuDY5FFjmkm6HZ2Wo4xBiEkQ,6795
77
+ memvcs/core/protocol_builder.py,sha256=b_5FphgmMdp7qP34ws3U2agXEoeYzTBjSgsQqd2Jx6Y,7713
71
78
  memvcs/core/refs.py,sha256=4Nx2ZVRa_DzfUZ4O1AwzOHEjoGAEICJKqSd9GxaiD_g,16754
72
- memvcs/core/remote.py,sha256=1PINc6qYBIHRkNLMS8MLWM5DJIrX81uIfRrV6fXwwro,19495
79
+ memvcs/core/remote.py,sha256=sZbAO9JEaDJM96PylB0CjpmR5UxWYdoXlq86sj3R2gU,22228
73
80
  memvcs/core/repository.py,sha256=NzC2UFPv6ePxi5lfiSKyZFLclH4bJpWJz88pY7tDiv4,20605
74
81
  memvcs/core/schema.py,sha256=_CrEWCdArc0yDJ04GT7fyvjHqkal7gegdFSsFOjVpBc,15287
75
82
  memvcs/core/staging.py,sha256=dptdGi_74lhDkcGqGVU39ZyTkb25j-Rnkz0GWi83W1k,7221
@@ -77,7 +84,7 @@ memvcs/core/temporal_index.py,sha256=81hZHlVElp2UpXjseFVCdDUwxGM45zIU-y1dDlOhFHI
77
84
  memvcs/core/test_runner.py,sha256=7-0jCvji63JRbVfy3LNQWIQ7VL5weulOoG7SY1-YJbw,11496
78
85
  memvcs/core/trust.py,sha256=msx80Cl3bxyQTY8mFUKWY9P6l3zb1s8FafympgHwtpo,3494
79
86
  memvcs/core/vector_store.py,sha256=yUAp5BlaAtjkrtsdY1I-vmAp_YIFgJykBoNlp5hcg0I,11063
80
- memvcs/core/zk_proofs.py,sha256=j9AyHucYe9tOSrlxDeUMGgpRHMvNFOl8s4Q0AQHLKP0,5514
87
+ memvcs/core/zk_proofs.py,sha256=tvJnj5oLTNQ_wFIGcMuuVF5faigIX_32U_HojNMoNp0,7623
81
88
  memvcs/core/llm/__init__.py,sha256=vnjtE9Xlv9a2pZV88DMT9JaINkZ30hC9VLPL5lJRlps,236
82
89
  memvcs/core/llm/anthropic_provider.py,sha256=O1eaCb9r464ajLJz-Gy8lGxBie5ojRUZ_5HdgRXO5KY,1540
83
90
  memvcs/core/llm/base.py,sha256=qPzg3KPAMeoyWGc_2JoVR4-plpdft5Rc2g9uO-Z4fJQ,623
@@ -88,6 +95,8 @@ memvcs/core/storage/base.py,sha256=IK4To8Cb-LHv5ltlaQLdB6LE-69euFK3hNqBtMCe7-g,9
88
95
  memvcs/core/storage/gcs.py,sha256=-cWuGw1jkFh-Xig-Abmwr9HGwjW5lWQJuF2xcAR1l78,10632
89
96
  memvcs/core/storage/local.py,sha256=JAik9nta6RMe4mD7aMtgdFi8M4iZCeTqiP8pPisaO6U,6028
90
97
  memvcs/core/storage/s3.py,sha256=tY5rfz8FfkRRNaHOPX7Wk6yXdBBBhKV0Ju2qnBtHxeU,13814
98
+ memvcs/health/__init__.py,sha256=YuxF8hVHJHNilAvVa0maptFLBWm4hcBymMjpA2dFJVU,546
99
+ memvcs/health/monitor.py,sha256=2JQqkR6n0e5L-gsU97FEB3rxjrCNAaGRNFpa7LKZtOg,15545
91
100
  memvcs/integrations/__init__.py,sha256=hVtJoFaXt6ErAZwctcSBDZLXRHFs1CNgtltIBQiroQ0,103
92
101
  memvcs/integrations/mcp_server.py,sha256=PxBYJnbzPs6bcFH6EmH5jQqbu_9Vy5eSAA8ruWTn2Q4,9061
93
102
  memvcs/integrations/web_ui/__init__.py,sha256=MQIfgDKDgPctlcTUjwkwueS_MDsDssVRmIUnpECGS0k,51
@@ -99,8 +108,8 @@ memvcs/retrieval/recaller.py,sha256=8KY-XjMUz5_vcKf46zI64uk1DEM__u7wM92ShukOtsY,
99
108
  memvcs/retrieval/strategies.py,sha256=26yxQQubQfjxWQXknfVMxuzPHf2EcZxJg_B99BEdl5c,11458
100
109
  memvcs/utils/__init__.py,sha256=8psUzz4Ntv2GzbRebkeVsoyC6Ck-FIwi0_lfYdj5oho,185
101
110
  memvcs/utils/helpers.py,sha256=37zg_DcQ2y99b9NSLqxFkglHe13rJXKhFDpEbQ7iLhM,4121
102
- agmem-0.1.6.dist-info/METADATA,sha256=5Pwa47IVpid1YYz60_uGxR1xz_uzPJ6HmYVPnv1v3P4,41042
103
- agmem-0.1.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
104
- agmem-0.1.6.dist-info/entry_points.txt,sha256=at7eWycgjqOo1wbUMECnXUsNo3gpCkJTU71OzrGLHu0,42
105
- agmem-0.1.6.dist-info/top_level.txt,sha256=HtMMsKuwLKLOdgF1GxqQztqFM54tTJctVdJuOec6B-4,7
106
- agmem-0.1.6.dist-info/RECORD,,
111
+ agmem-0.2.1.dist-info/METADATA,sha256=6UV86NAOpGnnqpRJJE_9XkU-7j2aoLSIf3TB1oQ3dC0,42320
112
+ agmem-0.2.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
113
+ agmem-0.2.1.dist-info/entry_points.txt,sha256=at7eWycgjqOo1wbUMECnXUsNo3gpCkJTU71OzrGLHu0,42
114
+ agmem-0.2.1.dist-info/top_level.txt,sha256=HtMMsKuwLKLOdgF1GxqQztqFM54tTJctVdJuOec6B-4,7
115
+ agmem-0.2.1.dist-info/RECORD,,
memvcs/__init__.py CHANGED
@@ -4,6 +4,6 @@ agmem - Agentic Memory Version Control System
4
4
  A Git-inspired version control system for AI agent memory artifacts.
5
5
  """
6
6
 
7
- __version__ = "0.1.6"
7
+ __version__ = "0.2.1"
8
8
  __author__ = "agmem Team"
9
9
  __license__ = "MIT"
memvcs/cli.py CHANGED
@@ -141,7 +141,7 @@ For more information: https://github.com/vivek-tiwari-vt/agmem
141
141
  """,
142
142
  )
143
143
 
144
- parser.add_argument("--version", "-v", action="version", version="%(prog)s 0.1.0")
144
+ parser.add_argument("--version", "-v", action="version", version="%(prog)s 0.2.1")
145
145
 
146
146
  parser.add_argument("--verbose", action="store_true", help="Enable verbose output")
147
147
 
memvcs/commands/daemon.py CHANGED
@@ -227,11 +227,12 @@ class DaemonCommand:
227
227
  while running:
228
228
  time.sleep(1)
229
229
 
230
- # Periodic health check (Merkle/signature, optional). Alert only; no destructive action.
230
+ # Periodic health check (Merkle/signature + operational metrics). Alert only; no destructive action.
231
231
  if (
232
232
  health_check_interval
233
233
  and (time.time() - last_health_check) >= health_check_interval
234
234
  ):
235
+ # Cryptographic integrity check
235
236
  try:
236
237
  from ..core.crypto_verify import verify_commit, load_public_key
237
238
 
@@ -251,6 +252,41 @@ class DaemonCommand:
251
252
  sys.stderr.write("Run 'agmem fsck' for safe integrity check.\n")
252
253
  except Exception:
253
254
  pass
255
+
256
+ # Operational health checks (storage, redundancy, stale memory, graph consistency)
257
+ try:
258
+ from ..health.monitor import HealthMonitor
259
+
260
+ health_monitor = HealthMonitor(repo.root)
261
+ report = health_monitor.perform_all_checks()
262
+
263
+ if report.get("warnings"):
264
+ for warning in report["warnings"]:
265
+ sys.stderr.write(f"Health warning: {warning}\n")
266
+
267
+ # Log summary metrics
268
+ if report.get("storage"):
269
+ storage = report["storage"]
270
+ if "total_size_mb" in storage:
271
+ sys.stderr.write(
272
+ f"Storage: {storage['total_size_mb']:.1f}MB "
273
+ f"({storage['packed_objects']} packed objects)\n"
274
+ )
275
+ if report.get("redundancy"):
276
+ red = report["redundancy"]
277
+ if "redundancy_percentage" in red:
278
+ sys.stderr.write(
279
+ f"Redundancy: {red['redundancy_percentage']:.1f}%\n"
280
+ )
281
+ if report.get("stale_memory"):
282
+ stale = report["stale_memory"]
283
+ if "stale_percentage" in stale:
284
+ sys.stderr.write(
285
+ f"Stale memory: {stale['stale_percentage']:.1f}%\n"
286
+ )
287
+ except Exception:
288
+ pass
289
+
254
290
  last_health_check = time.time()
255
291
 
256
292
  if handler.pending:
@@ -46,6 +46,11 @@ class DistillCommand:
46
46
  action="store_true",
47
47
  help="Use differential privacy (spend epsilon from budget)",
48
48
  )
49
+ parser.add_argument(
50
+ "--no-compress",
51
+ action="store_true",
52
+ help="Disable compression pipeline preprocessing",
53
+ )
49
54
 
50
55
  @staticmethod
51
56
  def execute(args) -> int:
@@ -73,6 +78,7 @@ class DistillCommand:
73
78
  source_dir=args.source,
74
79
  target_dir=args.target,
75
80
  create_safety_branch=not args.no_branch,
81
+ use_compression_pipeline=not getattr(args, "no_compress", False),
76
82
  use_dp=use_dp,
77
83
  dp_epsilon=dp_epsilon,
78
84
  dp_delta=dp_delta,
@@ -0,0 +1,5 @@
1
+ """Coordinator package for federated collaboration."""
2
+
3
+ from .server import app, FASTAPI_AVAILABLE
4
+
5
+ __all__ = ["app", "FASTAPI_AVAILABLE"]
@@ -0,0 +1,239 @@
1
+ """
2
+ Minimal Federated Coordinator Server for agmem.
3
+
4
+ Implements the coordinator API from docs/FEDERATED.md:
5
+ - POST /push: Accept agent summaries
6
+ - GET /pull: Return merged summaries
7
+
8
+ This is a reference implementation. For production:
9
+ - Add authentication (API keys, OAuth)
10
+ - Use persistent storage (PostgreSQL, Redis)
11
+ - Add rate limiting
12
+ - Enable HTTPS
13
+ - Scale horizontally
14
+
15
+ Install: pip install "agmem[coordinator]"
16
+ Run: uvicorn memvcs.coordinator.server:app --host 0.0.0.0 --port 8000
17
+ """
18
+
19
+ from datetime import datetime, timezone
20
+ from typing import Dict, List, Optional, Any
21
+ from pathlib import Path
22
+ import json
23
+ import hashlib
24
+ import re
25
+
26
+ try:
27
+ from fastapi import FastAPI, HTTPException, Request
28
+ from fastapi.responses import JSONResponse
29
+ from pydantic import BaseModel, Field
30
+
31
+ FASTAPI_AVAILABLE = True
32
+ except ImportError:
33
+ FASTAPI_AVAILABLE = False
34
+
35
+ # Stub for when FastAPI not installed
36
+ class BaseModel:
37
+ pass
38
+
39
+ def Field(*args, **kwargs):
40
+ return None
41
+
42
+
43
+ def _get_version() -> str:
44
+ """Get agmem version from pyproject.toml. Falls back to 0.2.1 if not found."""
45
+ try:
46
+ pyproject_path = Path(__file__).parent.parent.parent / "pyproject.toml"
47
+ if pyproject_path.exists():
48
+ content = pyproject_path.read_text()
49
+ match = re.search(r'version\s*=\s*"([^"]+)"', content)
50
+ if match:
51
+ return match.group(1)
52
+ except Exception:
53
+ pass
54
+ return "0.2.1"
55
+
56
+
57
+ # Storage: In-memory for simplicity (use Redis/PostgreSQL for production)
58
+ summaries_store: Dict[str, List[Dict[str, Any]]] = {}
59
+ _version = _get_version()
60
+ metadata_store: Dict[str, Any] = {
61
+ "coordinator_version": _version,
62
+ "started_at": datetime.now(timezone.utc).isoformat(),
63
+ "total_pushes": 0,
64
+ "total_agents": 0,
65
+ }
66
+
67
+
68
+ class AgentSummary(BaseModel):
69
+ """Agent summary for federated push."""
70
+
71
+ agent_id: str = Field(..., description="Unique agent identifier")
72
+ timestamp: str = Field(..., description="ISO 8601 timestamp")
73
+ topic_counts: Dict[str, int] = Field(default_factory=dict, description="Topic -> count")
74
+ fact_hashes: List[str] = Field(default_factory=list, description="SHA-256 hashes of facts")
75
+ metadata: Optional[Dict[str, Any]] = Field(default=None, description="Optional metadata")
76
+
77
+
78
+ class PushRequest(BaseModel):
79
+ """Request body for /push endpoint."""
80
+
81
+ summary: AgentSummary
82
+
83
+
84
+ class PullResponse(BaseModel):
85
+ """Response body for /pull endpoint."""
86
+
87
+ merged_topic_counts: Dict[str, int]
88
+ unique_fact_hashes: List[str]
89
+ contributing_agents: int
90
+ last_updated: str
91
+ metadata: Optional[Dict[str, Any]] = None
92
+
93
+
94
+ if FASTAPI_AVAILABLE:
95
+ app = FastAPI(
96
+ title="agmem Federated Coordinator",
97
+ description="Minimal coordinator for federated agent memory collaboration",
98
+ version=_version,
99
+ )
100
+
101
+ @app.get("/")
102
+ async def root():
103
+ """Health check and API info."""
104
+ return {
105
+ "service": "agmem-coordinator",
106
+ "version": metadata_store["coordinator_version"],
107
+ "status": "running",
108
+ "endpoints": {
109
+ "push": "POST /push",
110
+ "pull": "GET /pull",
111
+ "health": "GET /health",
112
+ },
113
+ "started_at": metadata_store["started_at"],
114
+ "total_pushes": metadata_store["total_pushes"],
115
+ "total_agents": metadata_store["total_agents"],
116
+ }
117
+
118
+ @app.get("/health")
119
+ async def health():
120
+ """Health check endpoint."""
121
+ return {
122
+ "status": "healthy",
123
+ "timestamp": datetime.now(timezone.utc).isoformat(),
124
+ }
125
+
126
+ @app.post("/push", response_model=Dict[str, Any])
127
+ async def push(request: PushRequest):
128
+ """
129
+ Accept agent summary and store it.
130
+
131
+ Returns:
132
+ Confirmation with push timestamp
133
+ """
134
+ summary = request.summary
135
+
136
+ # Validate timestamp
137
+ try:
138
+ datetime.fromisoformat(summary.timestamp.replace("Z", "+00:00"))
139
+ except ValueError:
140
+ raise HTTPException(
141
+ status_code=400, detail="Invalid timestamp format (expected ISO 8601)"
142
+ )
143
+
144
+ # Store summary by agent_id
145
+ if summary.agent_id not in summaries_store:
146
+ summaries_store[summary.agent_id] = []
147
+ metadata_store["total_agents"] += 1
148
+
149
+ summaries_store[summary.agent_id].append(summary.dict())
150
+ metadata_store["total_pushes"] += 1
151
+
152
+ return {
153
+ "status": "accepted",
154
+ "agent_id": summary.agent_id,
155
+ "timestamp": datetime.now(timezone.utc).isoformat(),
156
+ "message": f"Summary from {summary.agent_id} stored successfully",
157
+ }
158
+
159
+ @app.get("/pull", response_model=PullResponse)
160
+ async def pull():
161
+ """
162
+ Return merged summaries from all agents.
163
+
164
+ Returns:
165
+ Aggregated topic counts, unique fact hashes, contributing agent count
166
+ """
167
+ if not summaries_store:
168
+ return PullResponse(
169
+ merged_topic_counts={},
170
+ unique_fact_hashes=[],
171
+ contributing_agents=0,
172
+ last_updated=datetime.now(timezone.utc).isoformat(),
173
+ )
174
+
175
+ # Merge topic counts across all agents
176
+ merged_topics: Dict[str, int] = {}
177
+ all_fact_hashes = set()
178
+ latest_timestamp = None
179
+
180
+ for agent_id, summaries in summaries_store.items():
181
+ for summary in summaries:
182
+ # Aggregate topic counts
183
+ for topic, count in summary.get("topic_counts", {}).items():
184
+ merged_topics[topic] = merged_topics.get(topic, 0) + count
185
+
186
+ # Collect unique fact hashes
187
+ for fact_hash in summary.get("fact_hashes", []):
188
+ all_fact_hashes.add(fact_hash)
189
+
190
+ # Track latest update
191
+ ts = summary.get("timestamp")
192
+ if ts:
193
+ if latest_timestamp is None or ts > latest_timestamp:
194
+ latest_timestamp = ts
195
+
196
+ return PullResponse(
197
+ merged_topic_counts=merged_topics,
198
+ unique_fact_hashes=sorted(list(all_fact_hashes)),
199
+ contributing_agents=len(summaries_store),
200
+ last_updated=latest_timestamp or datetime.now(timezone.utc).isoformat(),
201
+ metadata={
202
+ "total_facts": len(all_fact_hashes),
203
+ "total_topics": len(merged_topics),
204
+ },
205
+ )
206
+
207
+ @app.delete("/admin/reset")
208
+ async def admin_reset(request: Request):
209
+ """
210
+ Admin endpoint to reset all stored data.
211
+ In production, protect this with authentication!
212
+ """
213
+ summaries_store.clear()
214
+ metadata_store["total_pushes"] = 0
215
+ metadata_store["total_agents"] = 0
216
+ metadata_store["started_at"] = datetime.now(timezone.utc).isoformat()
217
+
218
+ return {
219
+ "status": "reset",
220
+ "timestamp": datetime.now(timezone.utc).isoformat(),
221
+ }
222
+
223
+ else:
224
+ # Stub when FastAPI not available
225
+ app = None
226
+ print("FastAPI not available. Install with: pip install 'agmem[coordinator]'")
227
+
228
+
229
+ if __name__ == "__main__":
230
+ if not FASTAPI_AVAILABLE:
231
+ print("Error: FastAPI not installed")
232
+ print("Install with: pip install 'fastapi[all]' uvicorn")
233
+ exit(1)
234
+
235
+ import uvicorn
236
+
237
+ print("Starting agmem Federated Coordinator...")
238
+ print("API docs: http://localhost:8000/docs")
239
+ uvicorn.run(app, host="0.0.0.0", port=8000)