agmem 0.1.1__py3-none-any.whl → 0.1.3__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.
Files changed (100) hide show
  1. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/METADATA +157 -16
  2. agmem-0.1.3.dist-info/RECORD +105 -0
  3. memvcs/__init__.py +1 -1
  4. memvcs/cli.py +45 -31
  5. memvcs/commands/__init__.py +9 -9
  6. memvcs/commands/add.py +83 -76
  7. memvcs/commands/audit.py +59 -0
  8. memvcs/commands/blame.py +46 -53
  9. memvcs/commands/branch.py +13 -33
  10. memvcs/commands/checkout.py +27 -32
  11. memvcs/commands/clean.py +18 -23
  12. memvcs/commands/clone.py +11 -1
  13. memvcs/commands/commit.py +40 -39
  14. memvcs/commands/daemon.py +109 -76
  15. memvcs/commands/decay.py +77 -0
  16. memvcs/commands/diff.py +56 -57
  17. memvcs/commands/distill.py +90 -0
  18. memvcs/commands/federated.py +53 -0
  19. memvcs/commands/fsck.py +86 -61
  20. memvcs/commands/garden.py +40 -35
  21. memvcs/commands/gc.py +51 -0
  22. memvcs/commands/graph.py +41 -48
  23. memvcs/commands/init.py +16 -24
  24. memvcs/commands/log.py +25 -40
  25. memvcs/commands/merge.py +69 -27
  26. memvcs/commands/pack.py +129 -0
  27. memvcs/commands/prove.py +66 -0
  28. memvcs/commands/pull.py +31 -1
  29. memvcs/commands/push.py +4 -2
  30. memvcs/commands/recall.py +145 -0
  31. memvcs/commands/reflog.py +13 -22
  32. memvcs/commands/remote.py +1 -0
  33. memvcs/commands/repair.py +66 -0
  34. memvcs/commands/reset.py +23 -33
  35. memvcs/commands/resolve.py +130 -0
  36. memvcs/commands/resurrect.py +82 -0
  37. memvcs/commands/search.py +3 -4
  38. memvcs/commands/serve.py +2 -1
  39. memvcs/commands/show.py +66 -36
  40. memvcs/commands/stash.py +34 -34
  41. memvcs/commands/status.py +27 -35
  42. memvcs/commands/tag.py +23 -47
  43. memvcs/commands/test.py +30 -44
  44. memvcs/commands/timeline.py +111 -0
  45. memvcs/commands/tree.py +26 -27
  46. memvcs/commands/verify.py +110 -0
  47. memvcs/commands/when.py +115 -0
  48. memvcs/core/access_index.py +167 -0
  49. memvcs/core/audit.py +124 -0
  50. memvcs/core/config_loader.py +3 -1
  51. memvcs/core/consistency.py +214 -0
  52. memvcs/core/crypto_verify.py +280 -0
  53. memvcs/core/decay.py +185 -0
  54. memvcs/core/diff.py +158 -143
  55. memvcs/core/distiller.py +277 -0
  56. memvcs/core/encryption.py +169 -0
  57. memvcs/core/federated.py +86 -0
  58. memvcs/core/gardener.py +176 -145
  59. memvcs/core/hooks.py +48 -14
  60. memvcs/core/ipfs_remote.py +39 -0
  61. memvcs/core/knowledge_graph.py +135 -138
  62. memvcs/core/llm/__init__.py +10 -0
  63. memvcs/core/llm/anthropic_provider.py +50 -0
  64. memvcs/core/llm/base.py +27 -0
  65. memvcs/core/llm/factory.py +30 -0
  66. memvcs/core/llm/openai_provider.py +36 -0
  67. memvcs/core/merge.py +260 -170
  68. memvcs/core/objects.py +110 -101
  69. memvcs/core/pack.py +92 -0
  70. memvcs/core/pii_scanner.py +147 -146
  71. memvcs/core/privacy_budget.py +63 -0
  72. memvcs/core/refs.py +132 -115
  73. memvcs/core/remote.py +38 -0
  74. memvcs/core/repository.py +254 -164
  75. memvcs/core/schema.py +155 -113
  76. memvcs/core/staging.py +60 -65
  77. memvcs/core/storage/__init__.py +20 -18
  78. memvcs/core/storage/base.py +74 -70
  79. memvcs/core/storage/gcs.py +70 -68
  80. memvcs/core/storage/local.py +42 -40
  81. memvcs/core/storage/s3.py +105 -110
  82. memvcs/core/temporal_index.py +121 -0
  83. memvcs/core/test_runner.py +101 -93
  84. memvcs/core/trust.py +103 -0
  85. memvcs/core/vector_store.py +56 -36
  86. memvcs/core/zk_proofs.py +26 -0
  87. memvcs/integrations/mcp_server.py +1 -3
  88. memvcs/integrations/web_ui/server.py +25 -26
  89. memvcs/retrieval/__init__.py +22 -0
  90. memvcs/retrieval/base.py +54 -0
  91. memvcs/retrieval/pack.py +128 -0
  92. memvcs/retrieval/recaller.py +105 -0
  93. memvcs/retrieval/strategies.py +314 -0
  94. memvcs/utils/__init__.py +3 -3
  95. memvcs/utils/helpers.py +52 -52
  96. agmem-0.1.1.dist-info/RECORD +0 -67
  97. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/WHEEL +0 -0
  98. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/entry_points.txt +0 -0
  99. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/licenses/LICENSE +0 -0
  100. {agmem-0.1.1.dist-info → agmem-0.1.3.dist-info}/top_level.txt +0 -0
memvcs/core/hooks.py CHANGED
@@ -16,15 +16,16 @@ PII_SEVERITY_HIGH = "high"
16
16
  @dataclass
17
17
  class HookResult:
18
18
  """Result of running hooks."""
19
+
19
20
  success: bool
20
21
  errors: List[str] = field(default_factory=list)
21
22
  warnings: List[str] = field(default_factory=list)
22
-
23
+
23
24
  def add_error(self, message: str):
24
25
  """Add an error and mark as failed."""
25
26
  self.errors.append(message)
26
27
  self.success = False
27
-
28
+
28
29
  def add_warning(self, message: str):
29
30
  """Add a warning (doesn't affect success)."""
30
31
  self.warnings.append(message)
@@ -39,6 +40,7 @@ def _pii_staged_files_to_scan(repo, staged_files: Dict[str, Any]) -> Dict[str, A
39
40
  """Return staged files to scan for PII (excludes allowlisted paths)."""
40
41
  try:
41
42
  from .config_loader import load_agmem_config, pii_enabled, pii_allowlist
43
+
42
44
  config = load_agmem_config(getattr(repo, "root", None))
43
45
  except ImportError:
44
46
  return staged_files
@@ -58,6 +60,7 @@ def _run_pii_hook(repo, staged_files: Dict[str, Any], result: HookResult) -> Non
58
60
  """Run PII scanner on staged files; high severity → error, else → warning."""
59
61
  try:
60
62
  from .pii_scanner import PIIScanner
63
+
61
64
  to_scan = _pii_staged_files_to_scan(repo, staged_files)
62
65
  pii_result = PIIScanner.scan_staged_files(repo, to_scan)
63
66
  if not pii_result.has_issues:
@@ -77,11 +80,11 @@ def _run_pii_hook(repo, staged_files: Dict[str, Any], result: HookResult) -> Non
77
80
  def run_pre_commit_hooks(repo, staged_files: Dict[str, Any]) -> HookResult:
78
81
  """
79
82
  Run all pre-commit hooks on staged files.
80
-
83
+
81
84
  Args:
82
85
  repo: Repository instance
83
86
  staged_files: Dict of staged files with their info
84
-
87
+
85
88
  Returns:
86
89
  HookResult with success status and any errors/warnings
87
90
  """
@@ -93,42 +96,42 @@ def run_pre_commit_hooks(repo, staged_files: Dict[str, Any]) -> HookResult:
93
96
  result.add_error(error)
94
97
  for warning in file_type_result.warnings:
95
98
  result.add_warning(warning)
96
-
99
+
97
100
  return result
98
101
 
99
102
 
100
103
  def validate_file_types(repo, staged_files: Dict[str, Any]) -> HookResult:
101
104
  """
102
105
  Validate that staged files are allowed types.
103
-
106
+
104
107
  Args:
105
108
  repo: Repository instance
106
109
  staged_files: Dict of staged files
107
-
110
+
108
111
  Returns:
109
112
  HookResult with validation status
110
113
  """
111
114
  result = HookResult(success=True)
112
-
115
+
113
116
  # Get config for allowed extensions
114
117
  config = repo.get_config()
115
- allowed_extensions = config.get('allowed_extensions', ['.md', '.txt', '.json', '.yaml', '.yml'])
116
-
118
+ allowed_extensions = config.get("allowed_extensions", [".md", ".txt", ".json", ".yaml", ".yml"])
119
+
117
120
  for filepath in staged_files.keys():
118
121
  path = Path(filepath)
119
122
  ext = path.suffix.lower()
120
-
123
+
121
124
  # Skip files without extensions (might be valid)
122
125
  if not ext:
123
126
  continue
124
-
127
+
125
128
  # Check if extension is allowed
126
129
  if ext not in allowed_extensions:
127
130
  result.add_warning(
128
131
  f"File '{filepath}' has extension '{ext}' which may not be optimal for memory storage. "
129
132
  f"Recommended: {', '.join(allowed_extensions)}"
130
133
  )
131
-
134
+
132
135
  return result
133
136
 
134
137
 
@@ -139,7 +142,7 @@ _registered_hooks: List[Callable] = []
139
142
  def register_hook(hook_fn: Callable):
140
143
  """
141
144
  Register a custom pre-commit hook.
142
-
145
+
143
146
  Args:
144
147
  hook_fn: Function that takes (repo, staged_files) and returns HookResult
145
148
  """
@@ -149,3 +152,34 @@ def register_hook(hook_fn: Callable):
149
152
  def get_registered_hooks() -> List[Callable]:
150
153
  """Get all registered hooks."""
151
154
  return _registered_hooks.copy()
155
+
156
+
157
+ def compute_suggested_importance(
158
+ repo: Any,
159
+ staged_files: Dict[str, Any],
160
+ message: str,
161
+ metadata: Optional[Dict[str, Any]] = None,
162
+ ) -> float:
163
+ """
164
+ Compute suggested importance score from heuristics.
165
+
166
+ Scoring factors: user emphasis in message, source authority (auto_commit), etc.
167
+
168
+ Returns:
169
+ Float 0.0-1.0; default 0.5 if no heuristics match.
170
+ """
171
+ metadata = metadata or {}
172
+ message_lower = message.lower()
173
+
174
+ # auto_commit or gardener → lower authority
175
+ if metadata.get("auto_commit") or metadata.get("gardener"):
176
+ return 0.5
177
+
178
+ # User emphasis heuristics
179
+ if "important" in message_lower or "important:" in message_lower:
180
+ return 0.8
181
+ if "remember" in message_lower or "remember this" in message_lower:
182
+ return 0.7
183
+
184
+ # Default
185
+ return 0.5
@@ -0,0 +1,39 @@
1
+ """
2
+ IPFS remote for agmem (stub).
3
+
4
+ Push/pull via CIDs; pinning; gateway fallback when daemon unavailable.
5
+ Requires optional ipfs extra (ipfshttpclient or gateway requests).
6
+ """
7
+
8
+ from pathlib import Path
9
+ from typing import Optional, Set
10
+
11
+ from .objects import ObjectStore
12
+ from .remote import _collect_objects_from_commit
13
+
14
+
15
+ def push_to_ipfs(
16
+ objects_dir: Path,
17
+ branch: str,
18
+ commit_hash: str,
19
+ gateway_url: str = "https://ipfs.io",
20
+ ) -> Optional[str]:
21
+ """Push branch objects to IPFS and return root CID. Stub: returns None until IPFS client added."""
22
+ return None
23
+
24
+
25
+ def pull_from_ipfs(
26
+ objects_dir: Path,
27
+ cid: str,
28
+ gateway_url: str = "https://ipfs.io",
29
+ ) -> bool:
30
+ """Pull objects by CID from IPFS into objects_dir. Stub: returns False until IPFS client added."""
31
+ return False
32
+
33
+
34
+ def parse_ipfs_url(url: str) -> Optional[str]:
35
+ """Parse ipfs://<cid> or ipfs://<cid>/path. Returns CID or None."""
36
+ if not url.startswith("ipfs://"):
37
+ return None
38
+ rest = url[7:].lstrip("/")
39
+ return rest.split("/")[0] or None