claude-memory-agent 2.0.0

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. package/.env.example +107 -0
  2. package/README.md +200 -0
  3. package/agent_card.py +512 -0
  4. package/bin/cli.js +181 -0
  5. package/bin/postinstall.js +216 -0
  6. package/config.py +104 -0
  7. package/dashboard.html +2689 -0
  8. package/hooks/README.md +196 -0
  9. package/hooks/__pycache__/auto-detect-response.cpython-312.pyc +0 -0
  10. package/hooks/__pycache__/auto_capture.cpython-312.pyc +0 -0
  11. package/hooks/__pycache__/session_end.cpython-312.pyc +0 -0
  12. package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
  13. package/hooks/auto-detect-response.py +348 -0
  14. package/hooks/auto_capture.py +255 -0
  15. package/hooks/detect-correction.py +173 -0
  16. package/hooks/grounding-hook.py +348 -0
  17. package/hooks/log-tool-use.py +234 -0
  18. package/hooks/log-user-request.py +208 -0
  19. package/hooks/pre-tool-decision.py +218 -0
  20. package/hooks/problem-detector.py +343 -0
  21. package/hooks/session_end.py +192 -0
  22. package/hooks/session_start.py +227 -0
  23. package/install.py +887 -0
  24. package/main.py +2859 -0
  25. package/manager.py +997 -0
  26. package/package.json +55 -0
  27. package/requirements.txt +8 -0
  28. package/run_server.py +136 -0
  29. package/services/__init__.py +50 -0
  30. package/services/__pycache__/__init__.cpython-312.pyc +0 -0
  31. package/services/__pycache__/agent_registry.cpython-312.pyc +0 -0
  32. package/services/__pycache__/auth.cpython-312.pyc +0 -0
  33. package/services/__pycache__/auto_inject.cpython-312.pyc +0 -0
  34. package/services/__pycache__/claude_md_sync.cpython-312.pyc +0 -0
  35. package/services/__pycache__/cleanup.cpython-312.pyc +0 -0
  36. package/services/__pycache__/compaction_flush.cpython-312.pyc +0 -0
  37. package/services/__pycache__/confidence.cpython-312.pyc +0 -0
  38. package/services/__pycache__/daily_log.cpython-312.pyc +0 -0
  39. package/services/__pycache__/database.cpython-312.pyc +0 -0
  40. package/services/__pycache__/embeddings.cpython-312.pyc +0 -0
  41. package/services/__pycache__/insights.cpython-312.pyc +0 -0
  42. package/services/__pycache__/llm_analyzer.cpython-312.pyc +0 -0
  43. package/services/__pycache__/memory_md_sync.cpython-312.pyc +0 -0
  44. package/services/__pycache__/retry_queue.cpython-312.pyc +0 -0
  45. package/services/__pycache__/timeline.cpython-312.pyc +0 -0
  46. package/services/__pycache__/vector_index.cpython-312.pyc +0 -0
  47. package/services/__pycache__/websocket.cpython-312.pyc +0 -0
  48. package/services/agent_registry.py +753 -0
  49. package/services/auth.py +331 -0
  50. package/services/auto_inject.py +250 -0
  51. package/services/claude_md_sync.py +275 -0
  52. package/services/cleanup.py +667 -0
  53. package/services/compaction_flush.py +447 -0
  54. package/services/confidence.py +301 -0
  55. package/services/daily_log.py +333 -0
  56. package/services/database.py +2485 -0
  57. package/services/embeddings.py +358 -0
  58. package/services/insights.py +632 -0
  59. package/services/llm_analyzer.py +595 -0
  60. package/services/memory_md_sync.py +409 -0
  61. package/services/retry_queue.py +453 -0
  62. package/services/timeline.py +579 -0
  63. package/services/vector_index.py +398 -0
  64. package/services/websocket.py +257 -0
  65. package/skills/__init__.py +6 -0
  66. package/skills/__pycache__/__init__.cpython-312.pyc +0 -0
  67. package/skills/__pycache__/admin.cpython-312.pyc +0 -0
  68. package/skills/__pycache__/checkpoint.cpython-312.pyc +0 -0
  69. package/skills/__pycache__/claude_md.cpython-312.pyc +0 -0
  70. package/skills/__pycache__/cleanup.cpython-312.pyc +0 -0
  71. package/skills/__pycache__/grounding.cpython-312.pyc +0 -0
  72. package/skills/__pycache__/insights.cpython-312.pyc +0 -0
  73. package/skills/__pycache__/natural_language.cpython-312.pyc +0 -0
  74. package/skills/__pycache__/retrieve.cpython-312.pyc +0 -0
  75. package/skills/__pycache__/search.cpython-312.pyc +0 -0
  76. package/skills/__pycache__/state.cpython-312.pyc +0 -0
  77. package/skills/__pycache__/store.cpython-312.pyc +0 -0
  78. package/skills/__pycache__/summarize.cpython-312.pyc +0 -0
  79. package/skills/__pycache__/timeline.cpython-312.pyc +0 -0
  80. package/skills/__pycache__/verification.cpython-312.pyc +0 -0
  81. package/skills/admin.py +469 -0
  82. package/skills/checkpoint.py +198 -0
  83. package/skills/claude_md.py +363 -0
  84. package/skills/cleanup.py +241 -0
  85. package/skills/grounding.py +801 -0
  86. package/skills/insights.py +231 -0
  87. package/skills/natural_language.py +277 -0
  88. package/skills/retrieve.py +67 -0
  89. package/skills/search.py +213 -0
  90. package/skills/state.py +182 -0
  91. package/skills/store.py +179 -0
  92. package/skills/summarize.py +588 -0
  93. package/skills/timeline.py +387 -0
  94. package/skills/verification.py +391 -0
  95. package/start_daemon.py +155 -0
  96. package/test_automation.py +221 -0
  97. package/test_complete.py +338 -0
  98. package/test_full.py +322 -0
  99. package/update_system.py +817 -0
  100. package/verify_db.py +134 -0
@@ -0,0 +1,387 @@
1
+ """Timeline skills for session event tracking."""
2
+ from typing import Dict, Any, Optional, List
3
+ from services.database import DatabaseService
4
+ from services.embeddings import EmbeddingService
5
+ from services.timeline import TimelineService
6
+
7
+
8
+ async def timeline_log(
9
+ db: DatabaseService,
10
+ embeddings: EmbeddingService,
11
+ session_id: str,
12
+ event_type: str,
13
+ summary: str,
14
+ details: Optional[str] = None,
15
+ project_path: Optional[str] = None,
16
+ parent_event_id: Optional[int] = None,
17
+ root_event_id: Optional[int] = None,
18
+ entities: Optional[Dict[str, List[str]]] = None,
19
+ status: str = "completed",
20
+ outcome: Optional[str] = None,
21
+ confidence: Optional[float] = None,
22
+ is_anchor: bool = False
23
+ ) -> Dict[str, Any]:
24
+ """
25
+ Log an event to the session timeline.
26
+
27
+ Args:
28
+ db: Database service instance
29
+ embeddings: Embedding service instance
30
+ session_id: The session ID
31
+ event_type: Type of event:
32
+ - 'user_request': User asks for something
33
+ - 'clarification': User clarifies or corrects
34
+ - 'action': Claude takes an action (file edit, command)
35
+ - 'decision': Explicit choice made
36
+ - 'observation': Something Claude noticed
37
+ - 'error': Error encountered
38
+ - 'checkpoint': Session milestone
39
+ summary: Brief description (<200 chars)
40
+ details: Full context (optional)
41
+ project_path: Project path (optional)
42
+ parent_event_id: ID of parent event (causal chain)
43
+ root_event_id: ID of root user request
44
+ entities: Dict of entity references {"files": [], "functions": [], etc.}
45
+ status: Event status (pending, in_progress, completed, failed)
46
+ outcome: Result or error message
47
+ confidence: Confidence level 0-1
48
+ is_anchor: Whether this is a verified fact
49
+
50
+ Returns:
51
+ Dict with event info
52
+ """
53
+ timeline = TimelineService(db, embeddings)
54
+
55
+ event_id = await timeline.log_event(
56
+ session_id=session_id,
57
+ event_type=event_type,
58
+ summary=summary,
59
+ details=details,
60
+ project_path=project_path,
61
+ parent_event_id=parent_event_id,
62
+ root_event_id=root_event_id,
63
+ entities=entities,
64
+ status=status,
65
+ outcome=outcome,
66
+ confidence=confidence,
67
+ is_anchor=is_anchor
68
+ )
69
+
70
+ return {
71
+ "success": True,
72
+ "event_id": event_id,
73
+ "session_id": session_id,
74
+ "event_type": event_type,
75
+ "message": f"Event logged: {summary[:50]}..."
76
+ }
77
+
78
+
79
+ async def timeline_get(
80
+ db: DatabaseService,
81
+ session_id: str,
82
+ limit: int = 20,
83
+ event_type: Optional[str] = None,
84
+ since_event_id: Optional[int] = None,
85
+ anchors_only: bool = False,
86
+ include_state: bool = True,
87
+ include_checkpoint: bool = True
88
+ ) -> Dict[str, Any]:
89
+ """
90
+ Retrieve timeline events for a session.
91
+
92
+ Args:
93
+ db: Database service instance
94
+ session_id: The session ID
95
+ limit: Max events to return (default 20)
96
+ event_type: Filter by event type
97
+ since_event_id: Only events after this ID
98
+ anchors_only: Only return verified facts
99
+ include_state: Include current session state
100
+ include_checkpoint: Include latest checkpoint
101
+
102
+ Returns:
103
+ Dict with events, state, and checkpoint
104
+ """
105
+ result = {
106
+ "success": True,
107
+ "session_id": session_id,
108
+ "events": [],
109
+ "state": None,
110
+ "checkpoint": None
111
+ }
112
+
113
+ # Get events
114
+ result["events"] = await db.get_timeline_events(
115
+ session_id=session_id,
116
+ limit=limit,
117
+ event_type=event_type,
118
+ since_event_id=since_event_id,
119
+ anchors_only=anchors_only
120
+ )
121
+
122
+ # Get state
123
+ if include_state:
124
+ result["state"] = await db.get_or_create_session_state(session_id)
125
+
126
+ # Get checkpoint
127
+ if include_checkpoint:
128
+ result["checkpoint"] = await db.get_latest_checkpoint(session_id)
129
+
130
+ result["event_count"] = len(result["events"])
131
+
132
+ return result
133
+
134
+
135
+ async def timeline_search(
136
+ db: DatabaseService,
137
+ embeddings: EmbeddingService,
138
+ query: str,
139
+ session_id: Optional[str] = None,
140
+ limit: int = 10,
141
+ threshold: float = 0.5
142
+ ) -> Dict[str, Any]:
143
+ """
144
+ Semantic search across timeline events.
145
+
146
+ Args:
147
+ db: Database service instance
148
+ embeddings: Embedding service instance
149
+ query: Search query
150
+ session_id: Limit to specific session (optional)
151
+ limit: Max results (default 10)
152
+ threshold: Minimum similarity (default 0.5)
153
+
154
+ Returns:
155
+ Dict with matching events
156
+ """
157
+ timeline = TimelineService(db, embeddings)
158
+
159
+ events = await timeline.search_events(
160
+ query=query,
161
+ session_id=session_id,
162
+ limit=limit,
163
+ threshold=threshold
164
+ )
165
+
166
+ return {
167
+ "success": True,
168
+ "query": query,
169
+ "session_id": session_id,
170
+ "events": events,
171
+ "count": len(events),
172
+ "message": f"Found {len(events)} matching events"
173
+ }
174
+
175
+
176
+ async def timeline_chain(
177
+ db: DatabaseService,
178
+ session_id: str,
179
+ root_event_id: int,
180
+ include_details: bool = False
181
+ ) -> Dict[str, Any]:
182
+ """
183
+ Get the full causal chain for a user request.
184
+
185
+ Shows the complete timeline:
186
+ user_request → thinking → decision → action → outcome
187
+
188
+ Args:
189
+ db: Database service instance
190
+ session_id: The session ID
191
+ root_event_id: The root user_request event ID
192
+ include_details: Whether to include full event details
193
+
194
+ Returns:
195
+ Dict with the causal chain as a tree structure
196
+ """
197
+ # Get all events linked to this root
198
+ events = await db.get_timeline_events(
199
+ session_id=session_id,
200
+ limit=100 # Get all related events
201
+ )
202
+
203
+ # Filter events linked to this root
204
+ chain_events = [
205
+ e for e in events
206
+ if e.get("root_event_id") == root_event_id or e.get("id") == root_event_id
207
+ ]
208
+
209
+ # Sort by sequence number
210
+ chain_events.sort(key=lambda x: x.get("sequence_num", 0))
211
+
212
+ # Build tree structure
213
+ def build_tree(parent_id: Optional[int]) -> List[Dict]:
214
+ children = []
215
+ for event in chain_events:
216
+ if event.get("parent_event_id") == parent_id or (parent_id is None and event.get("id") == root_event_id):
217
+ node = {
218
+ "id": event.get("id"),
219
+ "type": event.get("event_type"),
220
+ "summary": event.get("summary", "")[:100],
221
+ "sequence": event.get("sequence_num"),
222
+ }
223
+ if include_details:
224
+ node["details"] = event.get("details")
225
+ node["created_at"] = event.get("created_at")
226
+
227
+ # Recursively add children
228
+ node["children"] = build_tree(event.get("id"))
229
+ children.append(node)
230
+ return children
231
+
232
+ tree = build_tree(None)
233
+
234
+ # Also create a flat timeline view
235
+ flat_timeline = []
236
+ for event in chain_events:
237
+ flat_timeline.append({
238
+ "seq": event.get("sequence_num"),
239
+ "type": event.get("event_type"),
240
+ "summary": event.get("summary", "")[:80]
241
+ })
242
+
243
+ return {
244
+ "success": True,
245
+ "session_id": session_id,
246
+ "root_event_id": root_event_id,
247
+ "tree": tree,
248
+ "flat_timeline": flat_timeline,
249
+ "event_count": len(chain_events),
250
+ "message": f"Chain has {len(chain_events)} events"
251
+ }
252
+
253
+
254
+ async def timeline_log_batch(
255
+ db: DatabaseService,
256
+ embeddings: EmbeddingService,
257
+ session_id: str,
258
+ events: List[Dict[str, Any]],
259
+ project_path: Optional[str] = None,
260
+ parent_event_id: Optional[int] = None,
261
+ root_event_id: Optional[int] = None
262
+ ) -> Dict[str, Any]:
263
+ """
264
+ Log multiple events to the session timeline in a single batch operation.
265
+
266
+ This is the efficient way to log multiple events - use this instead of
267
+ calling timeline_log() multiple times. It reduces HTTP overhead and
268
+ database transactions.
269
+
270
+ Args:
271
+ db: Database service instance
272
+ embeddings: Embedding service instance
273
+ session_id: The session ID
274
+ events: List of event dicts, each containing:
275
+ - event_type: Type of event (required)
276
+ - 'user_request': User asks for something
277
+ - 'clarification': User clarifies or corrects
278
+ - 'action': Claude takes an action (file edit, command)
279
+ - 'decision': Explicit choice made
280
+ - 'observation': Something Claude noticed
281
+ - 'thinking': Claude's reasoning process
282
+ - 'outcome': Result of a request
283
+ - 'error': Error encountered
284
+ - 'checkpoint': Session milestone
285
+ - summary: Brief description (<200 chars, required)
286
+ - details: Full context (optional)
287
+ - entities: Dict of entity references (optional)
288
+ - status: Event status - pending, in_progress, completed, failed (optional)
289
+ - outcome: Result or error message (optional)
290
+ - confidence: Confidence level 0-1 (optional)
291
+ - is_anchor: Whether this is a verified fact (optional)
292
+ project_path: Project path for all events (optional)
293
+ parent_event_id: ID of parent event for causal chain (optional)
294
+ root_event_id: ID of root user request for causal chain (optional)
295
+
296
+ Returns:
297
+ Dict with event IDs and summary
298
+ """
299
+ if not events:
300
+ return {
301
+ "success": True,
302
+ "event_ids": [],
303
+ "session_id": session_id,
304
+ "events_logged": 0,
305
+ "message": "No events to log"
306
+ }
307
+
308
+ timeline = TimelineService(db, embeddings)
309
+
310
+ event_ids = await timeline.log_events_batch(
311
+ session_id=session_id,
312
+ events=events,
313
+ project_path=project_path,
314
+ parent_event_id=parent_event_id,
315
+ root_event_id=root_event_id,
316
+ generate_embeddings=True
317
+ )
318
+
319
+ # Summarize what was logged
320
+ event_types = {}
321
+ for event in events:
322
+ et = event.get("event_type", "unknown")
323
+ event_types[et] = event_types.get(et, 0) + 1
324
+
325
+ type_summary = ", ".join(f"{count} {etype}" for etype, count in event_types.items())
326
+
327
+ return {
328
+ "success": True,
329
+ "event_ids": event_ids,
330
+ "session_id": session_id,
331
+ "events_logged": len(event_ids),
332
+ "event_types": event_types,
333
+ "message": f"Batch logged {len(event_ids)} events: {type_summary}"
334
+ }
335
+
336
+
337
+ async def timeline_auto_detect(
338
+ db: DatabaseService,
339
+ embeddings: EmbeddingService,
340
+ session_id: str,
341
+ response_text: str,
342
+ project_path: Optional[str] = None,
343
+ parent_event_id: Optional[int] = None,
344
+ root_event_id: Optional[int] = None
345
+ ) -> Dict[str, Any]:
346
+ """
347
+ Auto-detect and log decisions/observations from a response.
348
+
349
+ Args:
350
+ db: Database service instance
351
+ embeddings: Embedding service instance
352
+ session_id: The session ID
353
+ response_text: Claude's response text to analyze
354
+ project_path: Project path (optional)
355
+ parent_event_id: Parent event ID for causal chain (optional)
356
+ root_event_id: Root user request event ID (optional)
357
+
358
+ Returns:
359
+ Dict with detected and logged events
360
+ """
361
+ timeline = TimelineService(db, embeddings)
362
+
363
+ # Detect patterns
364
+ decisions = timeline.detect_decisions(response_text)
365
+ observations = timeline.detect_observations(response_text)
366
+ entities = timeline.extract_entities(response_text)
367
+
368
+ # Auto-log events with causal chain linking
369
+ event_ids = await timeline.auto_log_from_response(
370
+ session_id=session_id,
371
+ response_text=response_text,
372
+ project_path=project_path,
373
+ parent_event_id=parent_event_id,
374
+ root_event_id=root_event_id
375
+ )
376
+
377
+ return {
378
+ "success": True,
379
+ "session_id": session_id,
380
+ "detected": {
381
+ "decisions": decisions[:5],
382
+ "observations": observations[:5],
383
+ "entities": entities
384
+ },
385
+ "logged_event_ids": event_ids,
386
+ "message": f"Auto-detected {len(decisions)} decisions, {len(observations)} observations"
387
+ }