claude-memory-agent 2.0.0 → 2.1.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 (34) hide show
  1. package/README.md +206 -200
  2. package/agent_card.py +186 -0
  3. package/bin/cli.js +317 -181
  4. package/bin/postinstall.js +270 -216
  5. package/dashboard.html +4232 -2689
  6. package/hooks/__pycache__/grounding-hook.cpython-312.pyc +0 -0
  7. package/hooks/__pycache__/session_end.cpython-312.pyc +0 -0
  8. package/hooks/grounding-hook.py +422 -348
  9. package/hooks/session_end.py +293 -192
  10. package/hooks/session_start.py +227 -227
  11. package/install.py +919 -887
  12. package/main.py +4496 -2859
  13. package/package.json +47 -55
  14. package/services/__init__.py +50 -50
  15. package/services/__pycache__/__init__.cpython-312.pyc +0 -0
  16. package/services/__pycache__/curator.cpython-312.pyc +0 -0
  17. package/services/__pycache__/database.cpython-312.pyc +0 -0
  18. package/services/curator.py +1606 -0
  19. package/services/database.py +3637 -2485
  20. package/skills/__init__.py +21 -1
  21. package/skills/__pycache__/__init__.cpython-312.pyc +0 -0
  22. package/skills/__pycache__/confidence_tracker.cpython-312.pyc +0 -0
  23. package/skills/__pycache__/context.cpython-312.pyc +0 -0
  24. package/skills/__pycache__/curator.cpython-312.pyc +0 -0
  25. package/skills/__pycache__/search.cpython-312.pyc +0 -0
  26. package/skills/__pycache__/session_review.cpython-312.pyc +0 -0
  27. package/skills/__pycache__/store.cpython-312.pyc +0 -0
  28. package/skills/confidence_tracker.py +441 -0
  29. package/skills/context.py +675 -0
  30. package/skills/curator.py +348 -0
  31. package/skills/search.py +369 -213
  32. package/skills/session_review.py +418 -0
  33. package/skills/store.py +377 -179
  34. package/update_system.py +829 -817
@@ -0,0 +1,418 @@
1
+ """Session review skill for end-of-session memory verification.
2
+
3
+ Allows users to review memories created during a session and mark them as:
4
+ - keep: Verified as useful, increases confidence
5
+ - discard: Not useful, decreases confidence significantly
6
+ - partial: Partially useful, sets confidence to middle value
7
+ """
8
+ from typing import Dict, Any, Optional, List
9
+ from services.database import DatabaseService
10
+ from services.embeddings import EmbeddingService
11
+
12
+
13
+ async def get_session_memories(
14
+ db: DatabaseService,
15
+ session_id: str,
16
+ include_patterns: bool = False,
17
+ limit: int = 100
18
+ ) -> Dict[str, Any]:
19
+ """
20
+ Get all memories created in a specific session for review.
21
+
22
+ Args:
23
+ db: Database service instance
24
+ session_id: Session identifier to filter memories
25
+ include_patterns: Whether to include patterns created during session
26
+ limit: Maximum number of memories to return
27
+
28
+ Returns:
29
+ Dict with memories list and metadata
30
+ """
31
+ if not session_id:
32
+ return {
33
+ "success": False,
34
+ "error": "session_id is required"
35
+ }
36
+
37
+ # Get memories for this session
38
+ memories_query = """
39
+ SELECT
40
+ id, type, content, project_path, project_name,
41
+ outcome, success, outcome_status, confidence,
42
+ importance, tags, created_at
43
+ FROM memories
44
+ WHERE session_id = ?
45
+ ORDER BY created_at DESC
46
+ LIMIT ?
47
+ """
48
+
49
+ memories = await db.execute_query(memories_query, [session_id, limit])
50
+
51
+ result = {
52
+ "success": True,
53
+ "session_id": session_id,
54
+ "memories": memories or [],
55
+ "memory_count": len(memories) if memories else 0
56
+ }
57
+
58
+ # Optionally include patterns
59
+ if include_patterns:
60
+ patterns_query = """
61
+ SELECT
62
+ id, name, problem_type, solution,
63
+ success_count, failure_count, created_at
64
+ FROM patterns
65
+ WHERE created_at >= (
66
+ SELECT MIN(created_at) FROM memories WHERE session_id = ?
67
+ )
68
+ AND created_at <= (
69
+ SELECT MAX(created_at) FROM memories WHERE session_id = ?
70
+ )
71
+ ORDER BY created_at DESC
72
+ LIMIT ?
73
+ """
74
+ patterns = await db.execute_query(patterns_query, [session_id, session_id, limit])
75
+ result["patterns"] = patterns or []
76
+ result["pattern_count"] = len(patterns) if patterns else 0
77
+
78
+ # Generate summary
79
+ type_counts = {}
80
+ for m in (memories or []):
81
+ mtype = m.get("type", "unknown")
82
+ type_counts[mtype] = type_counts.get(mtype, 0) + 1
83
+
84
+ result["summary"] = {
85
+ "by_type": type_counts,
86
+ "total_memories": result["memory_count"],
87
+ "avg_importance": (
88
+ sum(m.get("importance", 5) for m in (memories or [])) / len(memories)
89
+ if memories else 0
90
+ ),
91
+ "avg_confidence": (
92
+ sum(m.get("confidence", 0.5) for m in (memories or [])) / len(memories)
93
+ if memories else 0
94
+ )
95
+ }
96
+
97
+ return result
98
+
99
+
100
+ async def review_session_memories(
101
+ db: DatabaseService,
102
+ session_id: str,
103
+ reviews: List[Dict[str, Any]]
104
+ ) -> Dict[str, Any]:
105
+ """
106
+ Process user review decisions for session memories.
107
+
108
+ Args:
109
+ db: Database service instance
110
+ session_id: Session identifier
111
+ reviews: List of review decisions, each containing:
112
+ - memory_id: ID of the memory
113
+ - decision: 'keep', 'discard', or 'partial'
114
+ - feedback: Optional user feedback
115
+
116
+ Returns:
117
+ Dict with processing results
118
+ """
119
+ if not session_id:
120
+ return {
121
+ "success": False,
122
+ "error": "session_id is required"
123
+ }
124
+
125
+ if not reviews:
126
+ return {
127
+ "success": False,
128
+ "error": "reviews list is required"
129
+ }
130
+
131
+ results = {
132
+ "success": True,
133
+ "session_id": session_id,
134
+ "processed": 0,
135
+ "kept": 0,
136
+ "discarded": 0,
137
+ "partial": 0,
138
+ "errors": []
139
+ }
140
+
141
+ # Confidence mappings for each decision
142
+ confidence_map = {
143
+ "keep": 0.9, # High confidence - verified useful
144
+ "partial": 0.5, # Medium confidence - partially useful
145
+ "discard": 0.1 # Low confidence - not useful
146
+ }
147
+
148
+ outcome_status_map = {
149
+ "keep": "success",
150
+ "partial": "partial",
151
+ "discard": "failed"
152
+ }
153
+
154
+ for review in reviews:
155
+ memory_id = review.get("memory_id")
156
+ decision = review.get("decision", "keep").lower()
157
+ feedback = review.get("feedback")
158
+
159
+ if not memory_id:
160
+ results["errors"].append({
161
+ "error": "memory_id is required",
162
+ "review": review
163
+ })
164
+ continue
165
+
166
+ if decision not in confidence_map:
167
+ results["errors"].append({
168
+ "memory_id": memory_id,
169
+ "error": f"Invalid decision: {decision}. Must be 'keep', 'discard', or 'partial'"
170
+ })
171
+ continue
172
+
173
+ try:
174
+ # Update confidence
175
+ new_confidence = confidence_map[decision]
176
+ await db.update_memory_confidence(memory_id, new_confidence)
177
+
178
+ # Update outcome status
179
+ new_outcome_status = outcome_status_map[decision]
180
+ await db.update_memory_outcome(
181
+ memory_id=memory_id,
182
+ outcome_status=new_outcome_status
183
+ )
184
+
185
+ # Store feedback if provided
186
+ if feedback:
187
+ await db.execute_query(
188
+ "UPDATE memories SET user_feedback = ? WHERE id = ?",
189
+ [feedback, memory_id]
190
+ )
191
+
192
+ results["processed"] += 1
193
+
194
+ if decision == "keep":
195
+ results["kept"] += 1
196
+ elif decision == "discard":
197
+ results["discarded"] += 1
198
+ elif decision == "partial":
199
+ results["partial"] += 1
200
+
201
+ except Exception as e:
202
+ results["errors"].append({
203
+ "memory_id": memory_id,
204
+ "error": str(e)
205
+ })
206
+
207
+ # Calculate success rate
208
+ total = results["kept"] + results["discarded"] + results["partial"]
209
+ if total > 0:
210
+ results["keep_rate"] = round(results["kept"] / total * 100, 1)
211
+ else:
212
+ results["keep_rate"] = 0
213
+
214
+ return results
215
+
216
+
217
+ async def suggest_session_reviews(
218
+ db: DatabaseService,
219
+ embeddings: EmbeddingService,
220
+ session_id: str
221
+ ) -> Dict[str, Any]:
222
+ """
223
+ Analyze session memories and suggest which to keep/discard.
224
+
225
+ Suggestions are based on:
226
+ - Memory type (decisions and patterns more likely to keep)
227
+ - Importance score
228
+ - Outcome status if already set
229
+ - Similarity to existing successful memories
230
+
231
+ Args:
232
+ db: Database service instance
233
+ embeddings: Embedding service instance
234
+ session_id: Session identifier
235
+
236
+ Returns:
237
+ Dict with suggested reviews
238
+ """
239
+ if not session_id:
240
+ return {
241
+ "success": False,
242
+ "error": "session_id is required"
243
+ }
244
+
245
+ # Get session memories
246
+ memories_result = await get_session_memories(db, session_id)
247
+ if not memories_result.get("success"):
248
+ return memories_result
249
+
250
+ memories = memories_result.get("memories", [])
251
+ suggestions = []
252
+
253
+ # Types that are more likely to be valuable
254
+ high_value_types = {"decision", "error", "pattern", "preference"}
255
+
256
+ for memory in memories:
257
+ memory_id = memory.get("id")
258
+ memory_type = memory.get("type", "chunk")
259
+ importance = memory.get("importance", 5)
260
+ outcome_status = memory.get("outcome_status", "pending")
261
+ confidence = memory.get("confidence", 0.5)
262
+ content = memory.get("content", "")
263
+
264
+ suggestion = {
265
+ "memory_id": memory_id,
266
+ "type": memory_type,
267
+ "content_preview": content[:200] + "..." if len(content) > 200 else content,
268
+ "current_confidence": confidence,
269
+ "importance": importance
270
+ }
271
+
272
+ # Determine suggestion based on heuristics
273
+ if outcome_status == "success":
274
+ suggestion["suggested_decision"] = "keep"
275
+ suggestion["reason"] = "Already marked as successful"
276
+ elif outcome_status == "failed":
277
+ suggestion["suggested_decision"] = "discard"
278
+ suggestion["reason"] = "Already marked as failed"
279
+ elif outcome_status == "partial":
280
+ suggestion["suggested_decision"] = "partial"
281
+ suggestion["reason"] = "Already marked as partial success"
282
+ elif memory_type in high_value_types:
283
+ if importance >= 7:
284
+ suggestion["suggested_decision"] = "keep"
285
+ suggestion["reason"] = f"High importance {memory_type}"
286
+ else:
287
+ suggestion["suggested_decision"] = "partial"
288
+ suggestion["reason"] = f"Review this {memory_type}"
289
+ elif importance >= 8:
290
+ suggestion["suggested_decision"] = "keep"
291
+ suggestion["reason"] = "High importance score"
292
+ elif importance <= 3:
293
+ suggestion["suggested_decision"] = "discard"
294
+ suggestion["reason"] = "Low importance score"
295
+ else:
296
+ suggestion["suggested_decision"] = "partial"
297
+ suggestion["reason"] = "Review recommended"
298
+
299
+ suggestions.append(suggestion)
300
+
301
+ return {
302
+ "success": True,
303
+ "session_id": session_id,
304
+ "suggestions": suggestions,
305
+ "summary": {
306
+ "total": len(suggestions),
307
+ "suggested_keep": sum(1 for s in suggestions if s["suggested_decision"] == "keep"),
308
+ "suggested_discard": sum(1 for s in suggestions if s["suggested_decision"] == "discard"),
309
+ "suggested_partial": sum(1 for s in suggestions if s["suggested_decision"] == "partial")
310
+ }
311
+ }
312
+
313
+
314
+ async def get_recent_sessions(
315
+ db: DatabaseService,
316
+ project_path: Optional[str] = None,
317
+ limit: int = 10
318
+ ) -> Dict[str, Any]:
319
+ """
320
+ Get recent sessions with memory counts for review selection.
321
+
322
+ Args:
323
+ db: Database service instance
324
+ project_path: Optional filter by project
325
+ limit: Maximum number of sessions to return
326
+
327
+ Returns:
328
+ Dict with session list
329
+ """
330
+ query = """
331
+ SELECT
332
+ session_id,
333
+ COUNT(*) as memory_count,
334
+ MIN(created_at) as started_at,
335
+ MAX(created_at) as ended_at,
336
+ project_path,
337
+ project_name,
338
+ SUM(CASE WHEN outcome_status = 'success' THEN 1 ELSE 0 END) as success_count,
339
+ SUM(CASE WHEN outcome_status = 'failed' THEN 1 ELSE 0 END) as failed_count,
340
+ SUM(CASE WHEN outcome_status = 'pending' THEN 1 ELSE 0 END) as pending_count,
341
+ AVG(confidence) as avg_confidence
342
+ FROM memories
343
+ WHERE session_id IS NOT NULL
344
+ """
345
+ params = []
346
+
347
+ if project_path:
348
+ query += " AND project_path = ?"
349
+ params.append(project_path)
350
+
351
+ query += """
352
+ GROUP BY session_id
353
+ ORDER BY MAX(created_at) DESC
354
+ LIMIT ?
355
+ """
356
+ params.append(limit)
357
+
358
+ sessions = await db.execute_query(query, params)
359
+
360
+ return {
361
+ "success": True,
362
+ "sessions": sessions or [],
363
+ "count": len(sessions) if sessions else 0
364
+ }
365
+
366
+
367
+ async def bulk_review_by_type(
368
+ db: DatabaseService,
369
+ session_id: str,
370
+ type_decisions: Dict[str, str]
371
+ ) -> Dict[str, Any]:
372
+ """
373
+ Apply review decisions to all memories of specific types in a session.
374
+
375
+ Useful for quickly processing sessions, e.g.:
376
+ - Keep all 'decision' and 'error' memories
377
+ - Discard all 'chunk' memories
378
+
379
+ Args:
380
+ db: Database service instance
381
+ session_id: Session identifier
382
+ type_decisions: Dict mapping memory types to decisions
383
+ e.g., {"decision": "keep", "chunk": "discard", "error": "keep"}
384
+
385
+ Returns:
386
+ Dict with processing results
387
+ """
388
+ if not session_id:
389
+ return {
390
+ "success": False,
391
+ "error": "session_id is required"
392
+ }
393
+
394
+ if not type_decisions:
395
+ return {
396
+ "success": False,
397
+ "error": "type_decisions is required"
398
+ }
399
+
400
+ # Get session memories
401
+ memories_result = await get_session_memories(db, session_id)
402
+ if not memories_result.get("success"):
403
+ return memories_result
404
+
405
+ memories = memories_result.get("memories", [])
406
+
407
+ # Build reviews based on type decisions
408
+ reviews = []
409
+ for memory in memories:
410
+ memory_type = memory.get("type", "chunk")
411
+ if memory_type in type_decisions:
412
+ reviews.append({
413
+ "memory_id": memory.get("id"),
414
+ "decision": type_decisions[memory_type]
415
+ })
416
+
417
+ # Process reviews
418
+ return await review_session_memories(db, session_id, reviews)