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.
- package/.env.example +107 -0
- package/README.md +200 -0
- package/agent_card.py +512 -0
- package/bin/cli.js +181 -0
- package/bin/postinstall.js +216 -0
- package/config.py +104 -0
- package/dashboard.html +2689 -0
- package/hooks/README.md +196 -0
- package/hooks/__pycache__/auto-detect-response.cpython-312.pyc +0 -0
- package/hooks/__pycache__/auto_capture.cpython-312.pyc +0 -0
- package/hooks/__pycache__/session_end.cpython-312.pyc +0 -0
- package/hooks/__pycache__/session_start.cpython-312.pyc +0 -0
- package/hooks/auto-detect-response.py +348 -0
- package/hooks/auto_capture.py +255 -0
- package/hooks/detect-correction.py +173 -0
- package/hooks/grounding-hook.py +348 -0
- package/hooks/log-tool-use.py +234 -0
- package/hooks/log-user-request.py +208 -0
- package/hooks/pre-tool-decision.py +218 -0
- package/hooks/problem-detector.py +343 -0
- package/hooks/session_end.py +192 -0
- package/hooks/session_start.py +227 -0
- package/install.py +887 -0
- package/main.py +2859 -0
- package/manager.py +997 -0
- package/package.json +55 -0
- package/requirements.txt +8 -0
- package/run_server.py +136 -0
- package/services/__init__.py +50 -0
- package/services/__pycache__/__init__.cpython-312.pyc +0 -0
- package/services/__pycache__/agent_registry.cpython-312.pyc +0 -0
- package/services/__pycache__/auth.cpython-312.pyc +0 -0
- package/services/__pycache__/auto_inject.cpython-312.pyc +0 -0
- package/services/__pycache__/claude_md_sync.cpython-312.pyc +0 -0
- package/services/__pycache__/cleanup.cpython-312.pyc +0 -0
- package/services/__pycache__/compaction_flush.cpython-312.pyc +0 -0
- package/services/__pycache__/confidence.cpython-312.pyc +0 -0
- package/services/__pycache__/daily_log.cpython-312.pyc +0 -0
- package/services/__pycache__/database.cpython-312.pyc +0 -0
- package/services/__pycache__/embeddings.cpython-312.pyc +0 -0
- package/services/__pycache__/insights.cpython-312.pyc +0 -0
- package/services/__pycache__/llm_analyzer.cpython-312.pyc +0 -0
- package/services/__pycache__/memory_md_sync.cpython-312.pyc +0 -0
- package/services/__pycache__/retry_queue.cpython-312.pyc +0 -0
- package/services/__pycache__/timeline.cpython-312.pyc +0 -0
- package/services/__pycache__/vector_index.cpython-312.pyc +0 -0
- package/services/__pycache__/websocket.cpython-312.pyc +0 -0
- package/services/agent_registry.py +753 -0
- package/services/auth.py +331 -0
- package/services/auto_inject.py +250 -0
- package/services/claude_md_sync.py +275 -0
- package/services/cleanup.py +667 -0
- package/services/compaction_flush.py +447 -0
- package/services/confidence.py +301 -0
- package/services/daily_log.py +333 -0
- package/services/database.py +2485 -0
- package/services/embeddings.py +358 -0
- package/services/insights.py +632 -0
- package/services/llm_analyzer.py +595 -0
- package/services/memory_md_sync.py +409 -0
- package/services/retry_queue.py +453 -0
- package/services/timeline.py +579 -0
- package/services/vector_index.py +398 -0
- package/services/websocket.py +257 -0
- package/skills/__init__.py +6 -0
- package/skills/__pycache__/__init__.cpython-312.pyc +0 -0
- package/skills/__pycache__/admin.cpython-312.pyc +0 -0
- package/skills/__pycache__/checkpoint.cpython-312.pyc +0 -0
- package/skills/__pycache__/claude_md.cpython-312.pyc +0 -0
- package/skills/__pycache__/cleanup.cpython-312.pyc +0 -0
- package/skills/__pycache__/grounding.cpython-312.pyc +0 -0
- package/skills/__pycache__/insights.cpython-312.pyc +0 -0
- package/skills/__pycache__/natural_language.cpython-312.pyc +0 -0
- package/skills/__pycache__/retrieve.cpython-312.pyc +0 -0
- package/skills/__pycache__/search.cpython-312.pyc +0 -0
- package/skills/__pycache__/state.cpython-312.pyc +0 -0
- package/skills/__pycache__/store.cpython-312.pyc +0 -0
- package/skills/__pycache__/summarize.cpython-312.pyc +0 -0
- package/skills/__pycache__/timeline.cpython-312.pyc +0 -0
- package/skills/__pycache__/verification.cpython-312.pyc +0 -0
- package/skills/admin.py +469 -0
- package/skills/checkpoint.py +198 -0
- package/skills/claude_md.py +363 -0
- package/skills/cleanup.py +241 -0
- package/skills/grounding.py +801 -0
- package/skills/insights.py +231 -0
- package/skills/natural_language.py +277 -0
- package/skills/retrieve.py +67 -0
- package/skills/search.py +213 -0
- package/skills/state.py +182 -0
- package/skills/store.py +179 -0
- package/skills/summarize.py +588 -0
- package/skills/timeline.py +387 -0
- package/skills/verification.py +391 -0
- package/start_daemon.py +155 -0
- package/test_automation.py +221 -0
- package/test_complete.py +338 -0
- package/test_full.py +322 -0
- package/update_system.py +817 -0
- package/verify_db.py +134 -0
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-memory-agent",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Persistent semantic memory system for Claude Code sessions with anti-hallucination grounding",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude",
|
|
7
|
+
"claude-code",
|
|
8
|
+
"memory",
|
|
9
|
+
"ai",
|
|
10
|
+
"mcp",
|
|
11
|
+
"semantic-search",
|
|
12
|
+
"embeddings",
|
|
13
|
+
"ollama"
|
|
14
|
+
],
|
|
15
|
+
"author": "Your Name",
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "https://github.com/yourusername/claude-memory-agent.git"
|
|
20
|
+
},
|
|
21
|
+
"homepage": "https://github.com/yourusername/claude-memory-agent#readme",
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/yourusername/claude-memory-agent/issues"
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"claude-memory-agent": "./bin/cli.js",
|
|
27
|
+
"claude-memory": "./bin/cli.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"bin/",
|
|
31
|
+
"services/",
|
|
32
|
+
"skills/",
|
|
33
|
+
"hooks/",
|
|
34
|
+
"*.py",
|
|
35
|
+
"*.md",
|
|
36
|
+
"*.txt",
|
|
37
|
+
"*.html",
|
|
38
|
+
".env.example"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"postinstall": "node bin/postinstall.js",
|
|
42
|
+
"start": "python main.py",
|
|
43
|
+
"install-agent": "python install.py --auto",
|
|
44
|
+
"test": "python -m pytest"
|
|
45
|
+
},
|
|
46
|
+
"engines": {
|
|
47
|
+
"node": ">=16.0.0"
|
|
48
|
+
},
|
|
49
|
+
"os": [
|
|
50
|
+
"win32",
|
|
51
|
+
"darwin",
|
|
52
|
+
"linux"
|
|
53
|
+
],
|
|
54
|
+
"preferGlobal": true
|
|
55
|
+
}
|
package/requirements.txt
ADDED
package/run_server.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""Run the memory agent server (for background/production use).
|
|
2
|
+
|
|
3
|
+
Uses Windows file locking (msvcrt.locking) for a true process mutex.
|
|
4
|
+
The lock is held for the entire lifetime of the server, ensuring only
|
|
5
|
+
one instance can run at a time.
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
import atexit
|
|
11
|
+
import signal
|
|
12
|
+
import uvicorn
|
|
13
|
+
from dotenv import load_dotenv
|
|
14
|
+
|
|
15
|
+
load_dotenv()
|
|
16
|
+
|
|
17
|
+
AGENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
18
|
+
LOCK_FILE = os.path.join(AGENT_DIR, "memory-agent.lock")
|
|
19
|
+
PID_FILE = os.path.join(AGENT_DIR, "memory-agent.pid")
|
|
20
|
+
PORT = int(os.getenv("PORT", 8102))
|
|
21
|
+
|
|
22
|
+
# Global lock file handle - must stay open for lock to persist
|
|
23
|
+
_lock_handle = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def is_port_in_use(port: int) -> bool:
|
|
27
|
+
"""Check if the port is already in use."""
|
|
28
|
+
import socket
|
|
29
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
30
|
+
return s.connect_ex(('localhost', port)) == 0
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def acquire_server_lock() -> bool:
|
|
34
|
+
"""Acquire exclusive server lock using Windows file locking.
|
|
35
|
+
|
|
36
|
+
This uses msvcrt.locking() which provides mandatory file locking on Windows.
|
|
37
|
+
The lock is held as long as the file handle remains open.
|
|
38
|
+
"""
|
|
39
|
+
global _lock_handle
|
|
40
|
+
import msvcrt
|
|
41
|
+
|
|
42
|
+
my_pid = os.getpid()
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
# Open file for read/write, create if doesn't exist
|
|
46
|
+
# Using os.open to get a file descriptor for msvcrt.locking
|
|
47
|
+
_lock_handle = open(LOCK_FILE, 'w+')
|
|
48
|
+
|
|
49
|
+
# Try to acquire exclusive lock (non-blocking)
|
|
50
|
+
# msvcrt.LK_NBLCK = non-blocking exclusive lock
|
|
51
|
+
try:
|
|
52
|
+
msvcrt.locking(_lock_handle.fileno(), msvcrt.LK_NBLCK, 1)
|
|
53
|
+
except (IOError, OSError) as e:
|
|
54
|
+
# Lock is held by another process
|
|
55
|
+
print(f"[MUTEX] Cannot acquire lock - another instance is running")
|
|
56
|
+
_lock_handle.close()
|
|
57
|
+
_lock_handle = None
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
# We have the lock - write our PID
|
|
61
|
+
_lock_handle.seek(0)
|
|
62
|
+
_lock_handle.truncate()
|
|
63
|
+
_lock_handle.write(str(my_pid))
|
|
64
|
+
_lock_handle.flush()
|
|
65
|
+
|
|
66
|
+
# Double-check the port isn't somehow in use
|
|
67
|
+
if is_port_in_use(PORT):
|
|
68
|
+
print(f"[MUTEX] Port {PORT} is already in use!")
|
|
69
|
+
release_server_lock()
|
|
70
|
+
return False
|
|
71
|
+
|
|
72
|
+
print(f"[MUTEX] Acquired server lock (PID: {my_pid})")
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
print(f"[MUTEX] Failed to acquire lock: {e}")
|
|
77
|
+
if _lock_handle:
|
|
78
|
+
try:
|
|
79
|
+
_lock_handle.close()
|
|
80
|
+
except:
|
|
81
|
+
pass
|
|
82
|
+
_lock_handle = None
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def release_server_lock():
|
|
87
|
+
"""Release the server lock on exit."""
|
|
88
|
+
global _lock_handle
|
|
89
|
+
import msvcrt
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
if _lock_handle:
|
|
93
|
+
try:
|
|
94
|
+
# Unlock the file
|
|
95
|
+
msvcrt.locking(_lock_handle.fileno(), msvcrt.LK_UNLCK, 1)
|
|
96
|
+
except:
|
|
97
|
+
pass
|
|
98
|
+
_lock_handle.close()
|
|
99
|
+
_lock_handle = None
|
|
100
|
+
print("[MUTEX] Released server lock")
|
|
101
|
+
except Exception as e:
|
|
102
|
+
print(f"[MUTEX] Error releasing lock: {e}")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def cleanup_and_exit(signum=None, frame=None):
|
|
106
|
+
"""Clean up lock and exit."""
|
|
107
|
+
release_server_lock()
|
|
108
|
+
if signum:
|
|
109
|
+
sys.exit(0)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
# Register cleanup handlers FIRST
|
|
114
|
+
atexit.register(release_server_lock)
|
|
115
|
+
signal.signal(signal.SIGTERM, cleanup_and_exit)
|
|
116
|
+
signal.signal(signal.SIGINT, cleanup_and_exit)
|
|
117
|
+
|
|
118
|
+
# Try to acquire the mutex - this blocks other instances
|
|
119
|
+
if not acquire_server_lock():
|
|
120
|
+
print("[MUTEX] Cannot start: another instance is running")
|
|
121
|
+
sys.exit(1)
|
|
122
|
+
|
|
123
|
+
# Write PID file (for convenience, the lock is the real mutex)
|
|
124
|
+
with open(PID_FILE, 'w') as f:
|
|
125
|
+
f.write(str(os.getpid()))
|
|
126
|
+
|
|
127
|
+
print(f"[SERVER] Starting memory agent on port {PORT}...")
|
|
128
|
+
|
|
129
|
+
# Note: The lock is held because _lock_handle stays open
|
|
130
|
+
uvicorn.run(
|
|
131
|
+
"main:app",
|
|
132
|
+
host=os.getenv("HOST", "0.0.0.0"),
|
|
133
|
+
port=PORT,
|
|
134
|
+
reload=False,
|
|
135
|
+
log_level="warning"
|
|
136
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from .database import DatabaseService
|
|
2
|
+
from .embeddings import EmbeddingService
|
|
3
|
+
|
|
4
|
+
# Moltbot-inspired transparency services
|
|
5
|
+
from .daily_log import (
|
|
6
|
+
append_entry as daily_log_append,
|
|
7
|
+
append_session_summary as daily_log_append_session,
|
|
8
|
+
load_recent_logs as daily_log_read,
|
|
9
|
+
get_today_highlights as daily_log_highlights,
|
|
10
|
+
list_logs as daily_log_list,
|
|
11
|
+
get_log_path
|
|
12
|
+
)
|
|
13
|
+
from .memory_md_sync import (
|
|
14
|
+
sync_to_memory_md,
|
|
15
|
+
read_memory_md,
|
|
16
|
+
add_fact as add_memory_md_fact,
|
|
17
|
+
get_memory_md_summary,
|
|
18
|
+
get_memory_md_path
|
|
19
|
+
)
|
|
20
|
+
from .compaction_flush import (
|
|
21
|
+
check_flush_needed,
|
|
22
|
+
execute_flush as pre_compaction_flush,
|
|
23
|
+
list_flushes,
|
|
24
|
+
read_flush,
|
|
25
|
+
get_flush_path
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"DatabaseService",
|
|
30
|
+
"EmbeddingService",
|
|
31
|
+
# Daily log
|
|
32
|
+
"daily_log_append",
|
|
33
|
+
"daily_log_append_session",
|
|
34
|
+
"daily_log_read",
|
|
35
|
+
"daily_log_highlights",
|
|
36
|
+
"daily_log_list",
|
|
37
|
+
"get_log_path",
|
|
38
|
+
# MEMORY.md
|
|
39
|
+
"sync_to_memory_md",
|
|
40
|
+
"read_memory_md",
|
|
41
|
+
"add_memory_md_fact",
|
|
42
|
+
"get_memory_md_summary",
|
|
43
|
+
"get_memory_md_path",
|
|
44
|
+
# Compaction flush
|
|
45
|
+
"check_flush_needed",
|
|
46
|
+
"pre_compaction_flush",
|
|
47
|
+
"list_flushes",
|
|
48
|
+
"read_flush",
|
|
49
|
+
"get_flush_path",
|
|
50
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|