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
@@ -12,17 +12,20 @@ from pathlib import Path
12
12
 
13
13
  class StorageError(Exception):
14
14
  """Base exception for storage operations."""
15
+
15
16
  pass
16
17
 
17
18
 
18
19
  class LockError(StorageError):
19
20
  """Exception raised when a lock cannot be acquired."""
21
+
20
22
  pass
21
23
 
22
24
 
23
25
  @dataclass
24
26
  class FileInfo:
25
27
  """Information about a file in storage."""
28
+
26
29
  path: str
27
30
  size: int
28
31
  modified: Optional[str] = None # ISO 8601 timestamp
@@ -32,184 +35,184 @@ class FileInfo:
32
35
  class StorageAdapter(ABC):
33
36
  """
34
37
  Abstract base class for storage adapters.
35
-
38
+
36
39
  All storage backends (local filesystem, S3, GCS, etc.) must implement
37
40
  this interface to provide consistent access to storage operations.
38
41
  """
39
-
42
+
40
43
  @abstractmethod
41
44
  def read_file(self, path: str) -> bytes:
42
45
  """
43
46
  Read a file's contents.
44
-
47
+
45
48
  Args:
46
49
  path: Path to the file (relative to storage root)
47
-
50
+
48
51
  Returns:
49
52
  File contents as bytes
50
-
53
+
51
54
  Raises:
52
55
  StorageError: If file doesn't exist or can't be read
53
56
  """
54
57
  pass
55
-
58
+
56
59
  @abstractmethod
57
60
  def write_file(self, path: str, data: bytes) -> None:
58
61
  """
59
62
  Write data to a file.
60
-
63
+
61
64
  Args:
62
65
  path: Path to the file (relative to storage root)
63
66
  data: Data to write
64
-
67
+
65
68
  Raises:
66
69
  StorageError: If file can't be written
67
70
  """
68
71
  pass
69
-
72
+
70
73
  @abstractmethod
71
74
  def exists(self, path: str) -> bool:
72
75
  """
73
76
  Check if a path exists.
74
-
77
+
75
78
  Args:
76
79
  path: Path to check
77
-
80
+
78
81
  Returns:
79
82
  True if path exists, False otherwise
80
83
  """
81
84
  pass
82
-
85
+
83
86
  @abstractmethod
84
87
  def delete(self, path: str) -> bool:
85
88
  """
86
89
  Delete a file.
87
-
90
+
88
91
  Args:
89
92
  path: Path to the file
90
-
93
+
91
94
  Returns:
92
95
  True if deleted, False if not found
93
96
  """
94
97
  pass
95
-
98
+
96
99
  @abstractmethod
97
100
  def list_dir(self, path: str = "") -> List[FileInfo]:
98
101
  """
99
102
  List contents of a directory.
100
-
103
+
101
104
  Args:
102
105
  path: Directory path (empty for root)
103
-
106
+
104
107
  Returns:
105
108
  List of FileInfo objects for directory contents
106
109
  """
107
110
  pass
108
-
111
+
109
112
  @abstractmethod
110
113
  def makedirs(self, path: str) -> None:
111
114
  """
112
115
  Create directory and any necessary parent directories.
113
-
116
+
114
117
  Args:
115
118
  path: Directory path to create
116
119
  """
117
120
  pass
118
-
121
+
119
122
  @abstractmethod
120
123
  def is_dir(self, path: str) -> bool:
121
124
  """
122
125
  Check if path is a directory.
123
-
126
+
124
127
  Args:
125
128
  path: Path to check
126
-
129
+
127
130
  Returns:
128
131
  True if path is a directory
129
132
  """
130
133
  pass
131
-
134
+
132
135
  # Lock management methods
133
-
136
+
134
137
  @abstractmethod
135
138
  def acquire_lock(self, lock_name: str, timeout: int = 30) -> bool:
136
139
  """
137
140
  Acquire a distributed lock.
138
-
141
+
139
142
  Args:
140
143
  lock_name: Name of the lock to acquire
141
144
  timeout: Maximum seconds to wait for lock
142
-
145
+
143
146
  Returns:
144
147
  True if lock acquired successfully
145
-
148
+
146
149
  Raises:
147
150
  LockError: If lock cannot be acquired within timeout
148
151
  """
149
152
  pass
150
-
153
+
151
154
  @abstractmethod
152
155
  def release_lock(self, lock_name: str) -> None:
153
156
  """
154
157
  Release a distributed lock.
155
-
158
+
156
159
  Args:
157
160
  lock_name: Name of the lock to release
158
161
  """
159
162
  pass
160
-
163
+
161
164
  @abstractmethod
162
165
  def is_locked(self, lock_name: str) -> bool:
163
166
  """
164
167
  Check if a lock is currently held.
165
-
168
+
166
169
  Args:
167
170
  lock_name: Name of the lock to check
168
-
171
+
169
172
  Returns:
170
173
  True if lock is held
171
174
  """
172
175
  pass
173
-
176
+
174
177
  # Convenience methods (can be overridden for efficiency)
175
-
176
- def read_text(self, path: str, encoding: str = 'utf-8') -> str:
178
+
179
+ def read_text(self, path: str, encoding: str = "utf-8") -> str:
177
180
  """Read file as text."""
178
181
  return self.read_file(path).decode(encoding)
179
-
180
- def write_text(self, path: str, text: str, encoding: str = 'utf-8') -> None:
182
+
183
+ def write_text(self, path: str, text: str, encoding: str = "utf-8") -> None:
181
184
  """Write text to file."""
182
185
  self.write_file(path, text.encode(encoding))
183
-
186
+
184
187
  def walk(self, path: str = "") -> Iterator[tuple]:
185
188
  """
186
189
  Walk through directory tree.
187
-
190
+
188
191
  Yields:
189
192
  Tuples of (dirpath, dirnames, filenames)
190
193
  """
191
194
  contents = self.list_dir(path)
192
-
195
+
193
196
  dirs = []
194
197
  files = []
195
-
198
+
196
199
  for item in contents:
197
200
  if item.is_dir:
198
- dirs.append(item.path.split('/')[-1])
201
+ dirs.append(item.path.split("/")[-1])
199
202
  else:
200
- files.append(item.path.split('/')[-1])
201
-
203
+ files.append(item.path.split("/")[-1])
204
+
202
205
  yield (path, dirs, files)
203
-
206
+
204
207
  for dirname in dirs:
205
208
  subpath = f"{path}/{dirname}" if path else dirname
206
209
  yield from self.walk(subpath)
207
-
210
+
208
211
  def copy_file(self, src: str, dst: str) -> None:
209
212
  """Copy a file within storage."""
210
213
  data = self.read_file(src)
211
214
  self.write_file(dst, data)
212
-
215
+
213
216
  def move_file(self, src: str, dst: str) -> None:
214
217
  """Move a file within storage."""
215
218
  self.copy_file(src, dst)
@@ -219,14 +222,14 @@ class StorageAdapter(ABC):
219
222
  class CachingStorageAdapter(StorageAdapter):
220
223
  """
221
224
  Storage adapter that caches remote operations locally.
222
-
225
+
223
226
  Used for cloud storage backends to minimize network requests.
224
227
  """
225
-
228
+
226
229
  def __init__(self, remote: StorageAdapter, cache_dir: str):
227
230
  """
228
231
  Initialize caching adapter.
229
-
232
+
230
233
  Args:
231
234
  remote: Remote storage adapter
232
235
  cache_dir: Local directory for caching
@@ -235,35 +238,35 @@ class CachingStorageAdapter(StorageAdapter):
235
238
  self.cache_dir = Path(cache_dir)
236
239
  self.cache_dir.mkdir(parents=True, exist_ok=True)
237
240
  self._dirty: set = set() # Paths that need to be pushed
238
-
241
+
239
242
  def _cache_path(self, path: str) -> Path:
240
243
  """Get local cache path for a remote path."""
241
244
  return self.cache_dir / path
242
-
245
+
243
246
  def read_file(self, path: str) -> bytes:
244
247
  """Read from cache, fetching from remote if needed."""
245
248
  cache_path = self._cache_path(path)
246
-
249
+
247
250
  if not cache_path.exists():
248
251
  # Fetch from remote
249
252
  data = self.remote.read_file(path)
250
253
  cache_path.parent.mkdir(parents=True, exist_ok=True)
251
254
  cache_path.write_bytes(data)
252
-
255
+
253
256
  return cache_path.read_bytes()
254
-
257
+
255
258
  def write_file(self, path: str, data: bytes) -> None:
256
259
  """Write to cache and mark as dirty."""
257
260
  cache_path = self._cache_path(path)
258
261
  cache_path.parent.mkdir(parents=True, exist_ok=True)
259
262
  cache_path.write_bytes(data)
260
263
  self._dirty.add(path)
261
-
264
+
262
265
  def exists(self, path: str) -> bool:
263
266
  """Check if path exists in cache or remote."""
264
267
  cache_path = self._cache_path(path)
265
268
  return cache_path.exists() or self.remote.exists(path)
266
-
269
+
267
270
  def delete(self, path: str) -> bool:
268
271
  """Delete from cache and remote."""
269
272
  cache_path = self._cache_path(path)
@@ -271,39 +274,39 @@ class CachingStorageAdapter(StorageAdapter):
271
274
  cache_path.unlink()
272
275
  self._dirty.discard(path)
273
276
  return self.remote.delete(path)
274
-
277
+
275
278
  def list_dir(self, path: str = "") -> List[FileInfo]:
276
279
  """List directory from remote."""
277
280
  return self.remote.list_dir(path)
278
-
281
+
279
282
  def makedirs(self, path: str) -> None:
280
283
  """Create directory in cache."""
281
284
  cache_path = self._cache_path(path)
282
285
  cache_path.mkdir(parents=True, exist_ok=True)
283
-
286
+
284
287
  def is_dir(self, path: str) -> bool:
285
288
  """Check if path is directory."""
286
289
  cache_path = self._cache_path(path)
287
290
  if cache_path.exists():
288
291
  return cache_path.is_dir()
289
292
  return self.remote.is_dir(path)
290
-
293
+
291
294
  def acquire_lock(self, lock_name: str, timeout: int = 30) -> bool:
292
295
  """Acquire lock on remote."""
293
296
  return self.remote.acquire_lock(lock_name, timeout)
294
-
297
+
295
298
  def release_lock(self, lock_name: str) -> None:
296
299
  """Release lock on remote."""
297
300
  self.remote.release_lock(lock_name)
298
-
301
+
299
302
  def is_locked(self, lock_name: str) -> bool:
300
303
  """Check if lock is held on remote."""
301
304
  return self.remote.is_locked(lock_name)
302
-
305
+
303
306
  def sync_to_remote(self) -> int:
304
307
  """
305
308
  Push all dirty files to remote.
306
-
309
+
307
310
  Returns:
308
311
  Number of files synced
309
312
  """
@@ -315,14 +318,14 @@ class CachingStorageAdapter(StorageAdapter):
315
318
  count += 1
316
319
  self._dirty.discard(path)
317
320
  return count
318
-
321
+
319
322
  def sync_from_remote(self, paths: Optional[List[str]] = None) -> int:
320
323
  """
321
324
  Pull files from remote to cache.
322
-
325
+
323
326
  Args:
324
327
  paths: Specific paths to sync, or None for all
325
-
328
+
326
329
  Returns:
327
330
  Number of files synced
328
331
  """
@@ -345,14 +348,15 @@ class CachingStorageAdapter(StorageAdapter):
345
348
  cache_path.parent.mkdir(parents=True, exist_ok=True)
346
349
  cache_path.write_bytes(data)
347
350
  return len(paths)
348
-
351
+
349
352
  def get_dirty_paths(self) -> List[str]:
350
353
  """Get list of paths that need to be pushed."""
351
354
  return list(self._dirty)
352
-
355
+
353
356
  def clear_cache(self) -> None:
354
357
  """Clear the local cache."""
355
358
  import shutil
359
+
356
360
  if self.cache_dir.exists():
357
361
  shutil.rmtree(self.cache_dir)
358
362
  self.cache_dir.mkdir(parents=True, exist_ok=True)