superlocalmemory 2.8.0 → 2.8.1
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/api_server.py +23 -0
- package/bin/slm +20 -0
- package/install.sh +15 -0
- package/mcp_server.py +38 -3
- package/package.json +1 -1
- package/skills/slm-list-recent/SKILL.md +1 -1
- package/skills/slm-remember/SKILL.md +1 -1
- package/skills/slm-status/SKILL.md +1 -1
- package/skills/slm-switch-profile/SKILL.md +3 -3
- package/src/hnsw_index.py +10 -4
- package/src/lifecycle/lifecycle_engine.py +61 -8
- package/src/lifecycle/lifecycle_evaluator.py +38 -6
- package/src/mcp_tools_v28.py +4 -3
- package/src/memory-profiles.py +1 -0
- package/ui/index.html +152 -4
- package/ui/js/behavioral.js +276 -0
- package/ui/js/compliance.js +252 -0
- package/ui/js/init.js +10 -0
- package/ui/js/lifecycle.js +298 -0
- package/ui/js/profiles.js +4 -0
- package/ui_server.py +19 -0
package/api_server.py
CHANGED
|
@@ -671,6 +671,29 @@ async def get_tree():
|
|
|
671
671
|
}
|
|
672
672
|
|
|
673
673
|
|
|
674
|
+
# ============================================================================
|
|
675
|
+
# v2.8 Routes (graceful — don't fail if engines unavailable)
|
|
676
|
+
# ============================================================================
|
|
677
|
+
|
|
678
|
+
try:
|
|
679
|
+
from routes.lifecycle import router as lifecycle_router
|
|
680
|
+
app.include_router(lifecycle_router)
|
|
681
|
+
except ImportError:
|
|
682
|
+
pass
|
|
683
|
+
|
|
684
|
+
try:
|
|
685
|
+
from routes.behavioral import router as behavioral_router
|
|
686
|
+
app.include_router(behavioral_router)
|
|
687
|
+
except ImportError:
|
|
688
|
+
pass
|
|
689
|
+
|
|
690
|
+
try:
|
|
691
|
+
from routes.compliance import router as compliance_router
|
|
692
|
+
app.include_router(compliance_router)
|
|
693
|
+
except ImportError:
|
|
694
|
+
pass
|
|
695
|
+
|
|
696
|
+
|
|
674
697
|
# ============================================================================
|
|
675
698
|
# Server Startup
|
|
676
699
|
# ============================================================================
|
package/bin/slm
CHANGED
|
@@ -55,6 +55,23 @@ case "$1" in
|
|
|
55
55
|
"$SLM_DIR/bin/superlocalmemoryv2:profile" "$@"
|
|
56
56
|
;;
|
|
57
57
|
|
|
58
|
+
switch-profile)
|
|
59
|
+
shift
|
|
60
|
+
# Convenience alias: "slm switch-profile <name>" -> "slm profile switch <name>"
|
|
61
|
+
"$SLM_DIR/bin/superlocalmemoryv2:profile" switch "$@"
|
|
62
|
+
;;
|
|
63
|
+
|
|
64
|
+
list-profiles)
|
|
65
|
+
# Convenience alias: "slm list-profiles" -> "slm profile list"
|
|
66
|
+
"$SLM_DIR/bin/superlocalmemoryv2:profile" list
|
|
67
|
+
;;
|
|
68
|
+
|
|
69
|
+
create-profile)
|
|
70
|
+
shift
|
|
71
|
+
# Convenience alias: "slm create-profile <name>" -> "slm profile create <name>"
|
|
72
|
+
"$SLM_DIR/bin/superlocalmemoryv2:profile" create "$@"
|
|
73
|
+
;;
|
|
74
|
+
|
|
58
75
|
reset)
|
|
59
76
|
shift
|
|
60
77
|
# Calls existing command - no duplicate logic
|
|
@@ -310,6 +327,9 @@ PROFILE MANAGEMENT:
|
|
|
310
327
|
slm profile switch <name> Switch to profile
|
|
311
328
|
slm profile delete <name> Delete profile
|
|
312
329
|
slm profile current Show current profile
|
|
330
|
+
slm switch-profile <name> Shortcut for profile switch
|
|
331
|
+
slm list-profiles Shortcut for profile list
|
|
332
|
+
slm create-profile <name> Shortcut for profile create
|
|
313
333
|
|
|
314
334
|
KNOWLEDGE GRAPH:
|
|
315
335
|
slm graph build Build/rebuild knowledge graph
|
package/install.sh
CHANGED
|
@@ -338,6 +338,21 @@ print('Database ready')
|
|
|
338
338
|
" && echo "✓ Database initialized (fallback)"
|
|
339
339
|
fi
|
|
340
340
|
|
|
341
|
+
# Run v2.8 schema migration (adds lifecycle columns to existing databases)
|
|
342
|
+
echo ""
|
|
343
|
+
echo "Running schema migration..."
|
|
344
|
+
cd "$INSTALL_DIR"
|
|
345
|
+
python3 -c "
|
|
346
|
+
import sys
|
|
347
|
+
sys.path.insert(0, '.')
|
|
348
|
+
try:
|
|
349
|
+
from memory_store_v2 import MemoryStoreV2
|
|
350
|
+
MemoryStoreV2()
|
|
351
|
+
print(' Schema migration complete')
|
|
352
|
+
except Exception as e:
|
|
353
|
+
print(f' Migration note: {e}')
|
|
354
|
+
" 2>/dev/null || echo " Migration will run on first use"
|
|
355
|
+
|
|
341
356
|
# Install core dependencies (required for graph & dashboard)
|
|
342
357
|
echo ""
|
|
343
358
|
echo "Installing core dependencies..."
|
package/mcp_server.py
CHANGED
|
@@ -560,6 +560,36 @@ def _maybe_passive_decay() -> None:
|
|
|
560
560
|
pass
|
|
561
561
|
|
|
562
562
|
|
|
563
|
+
# ============================================================================
|
|
564
|
+
# Eager initialization — ensure schema migration runs at startup (v2.8)
|
|
565
|
+
# ============================================================================
|
|
566
|
+
|
|
567
|
+
def _eager_init():
|
|
568
|
+
"""Initialize all engines at startup. Ensures schema migration runs."""
|
|
569
|
+
try:
|
|
570
|
+
get_store() # Triggers MemoryStoreV2._init_db() which creates v2.8 columns
|
|
571
|
+
except Exception:
|
|
572
|
+
pass # Don't block server startup
|
|
573
|
+
try:
|
|
574
|
+
from lifecycle.lifecycle_engine import LifecycleEngine
|
|
575
|
+
LifecycleEngine() # Triggers _ensure_columns()
|
|
576
|
+
except Exception:
|
|
577
|
+
pass
|
|
578
|
+
try:
|
|
579
|
+
from behavioral.outcome_tracker import OutcomeTracker
|
|
580
|
+
OutcomeTracker(str(Path.home() / ".claude-memory" / "learning.db"))
|
|
581
|
+
except Exception:
|
|
582
|
+
pass
|
|
583
|
+
try:
|
|
584
|
+
from compliance.audit_db import AuditDB
|
|
585
|
+
AuditDB(str(Path.home() / ".claude-memory" / "audit.db"))
|
|
586
|
+
except Exception:
|
|
587
|
+
pass
|
|
588
|
+
|
|
589
|
+
# Run once at module load
|
|
590
|
+
_eager_init()
|
|
591
|
+
|
|
592
|
+
|
|
563
593
|
# ============================================================================
|
|
564
594
|
# MCP TOOLS (Functions callable by AI)
|
|
565
595
|
# ============================================================================
|
|
@@ -585,7 +615,7 @@ async def remember(
|
|
|
585
615
|
Args:
|
|
586
616
|
content: The content to remember (required)
|
|
587
617
|
tags: Comma-separated tags (optional, e.g. "python,api,backend")
|
|
588
|
-
project: Project name
|
|
618
|
+
project: Project name to scope the memory
|
|
589
619
|
importance: Importance score 1-10 (default 5)
|
|
590
620
|
|
|
591
621
|
Returns:
|
|
@@ -1394,7 +1424,12 @@ try:
|
|
|
1394
1424
|
|
|
1395
1425
|
@mcp.tool(annotations=ToolAnnotations(readOnlyHint=False, destructiveHint=False))
|
|
1396
1426
|
async def compact_memories(dry_run: bool = True, profile: str = None) -> dict:
|
|
1397
|
-
"""Evaluate and compact stale memories through lifecycle transitions. dry_run=True by default.
|
|
1427
|
+
"""Evaluate and compact stale memories through lifecycle transitions. dry_run=True by default.
|
|
1428
|
+
|
|
1429
|
+
Args:
|
|
1430
|
+
dry_run: If True (default), show what would happen without changes.
|
|
1431
|
+
profile: Profile name to filter.
|
|
1432
|
+
"""
|
|
1398
1433
|
return await _compact_memories(dry_run, profile)
|
|
1399
1434
|
|
|
1400
1435
|
@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True, destructiveHint=False))
|
|
@@ -1649,7 +1684,7 @@ if __name__ == "__main__":
|
|
|
1649
1684
|
print(" - list_recent(limit)", file=sys.stderr)
|
|
1650
1685
|
print(" - get_status()", file=sys.stderr)
|
|
1651
1686
|
print(" - build_graph()", file=sys.stderr)
|
|
1652
|
-
print(" - switch_profile(name)", file=sys.stderr)
|
|
1687
|
+
print(" - switch_profile(name) [Project/Profile switch]", file=sys.stderr)
|
|
1653
1688
|
print(" - backup_status() [Auto-Backup]", file=sys.stderr)
|
|
1654
1689
|
if LEARNING_AVAILABLE:
|
|
1655
1690
|
print(" - memory_used(memory_id, query, usefulness) [v2.7 Learning]", file=sys.stderr)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "superlocalmemory",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.1",
|
|
4
4
|
"description": "Your AI Finally Remembers You - Local-first intelligent memory system for AI assistants. Works with Claude, Cursor, Windsurf, VS Code/Copilot, Codex, and 17+ AI tools. 100% local, zero cloud dependencies.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-memory",
|
|
@@ -336,7 +336,7 @@ date
|
|
|
336
336
|
- `slm remember` - Save a new memory
|
|
337
337
|
- `slm recall` - Search memories by relevance
|
|
338
338
|
- `slm status` - Check memory count and stats
|
|
339
|
-
- `slm switch-profile` - View different
|
|
339
|
+
- `slm switch-profile` - View different profile's memories
|
|
340
340
|
|
|
341
341
|
---
|
|
342
342
|
|
|
@@ -157,7 +157,7 @@ slm remember "Commit: $commit_msg (${commit_hash:0:7})" \
|
|
|
157
157
|
- **Cross-tool sync:** All AI tools access same database (Cursor, ChatGPT, Claude, etc.)
|
|
158
158
|
- **Unlimited:** No memory limits, no quotas
|
|
159
159
|
- **Privacy:** Your data stays on your computer
|
|
160
|
-
- **Profiles:** Use `slm switch-profile` for
|
|
160
|
+
- **Profiles:** Use `slm switch-profile` for profile isolation
|
|
161
161
|
|
|
162
162
|
## Related Commands
|
|
163
163
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: slm-switch-profile
|
|
3
|
-
description: Switch between memory profiles for
|
|
3
|
+
description: Switch between memory profiles for context isolation and management. Use when the user wants to change profile context, separate work/personal memories, or manage multiple independent memory spaces. Each profile has its own database, graph, and patterns.
|
|
4
4
|
version: "2.1.0"
|
|
5
5
|
license: MIT
|
|
6
6
|
compatibility: "Requires SuperLocalMemory V2 installed at ~/.claude-memory/"
|
|
@@ -26,8 +26,8 @@ slm switch-profile <name> [--create]
|
|
|
26
26
|
|
|
27
27
|
Each profile has its own:
|
|
28
28
|
- ✅ **Separate database** - Zero context bleeding
|
|
29
|
-
- ✅ **Independent knowledge graph** -
|
|
30
|
-
- ✅ **Unique patterns** - Different coding preferences per
|
|
29
|
+
- ✅ **Independent knowledge graph** - Profile-specific relationships
|
|
30
|
+
- ✅ **Unique patterns** - Different coding preferences per profile
|
|
31
31
|
- ✅ **Isolated history** - No cross-contamination
|
|
32
32
|
|
|
33
33
|
**Think of profiles as workspaces:**
|
package/src/hnsw_index.py
CHANGED
|
@@ -139,12 +139,18 @@ class HNSWIndex:
|
|
|
139
139
|
with open(self.metadata_path, 'r') as f:
|
|
140
140
|
metadata = json.load(f)
|
|
141
141
|
|
|
142
|
-
# Validate metadata
|
|
142
|
+
# Validate metadata — auto-rebuild on dimension mismatch
|
|
143
143
|
if metadata.get('dimension') != self.dimension:
|
|
144
|
-
logger.
|
|
145
|
-
|
|
146
|
-
"
|
|
144
|
+
logger.info(
|
|
145
|
+
"Index dimension changed: %s -> %s. "
|
|
146
|
+
"Deleting old index files and rebuilding.",
|
|
147
|
+
metadata.get('dimension'), self.dimension,
|
|
147
148
|
)
|
|
149
|
+
try:
|
|
150
|
+
self.index_path.unlink(missing_ok=True)
|
|
151
|
+
self.metadata_path.unlink(missing_ok=True)
|
|
152
|
+
except OSError as del_err:
|
|
153
|
+
logger.warning("Could not delete old index files: %s", del_err)
|
|
148
154
|
return
|
|
149
155
|
|
|
150
156
|
# Load HNSW index
|
|
@@ -38,6 +38,7 @@ class LifecycleEngine:
|
|
|
38
38
|
self._db_path = str(db_path)
|
|
39
39
|
self._config_path = config_path
|
|
40
40
|
self._lock = threading.Lock()
|
|
41
|
+
self._ensure_columns()
|
|
41
42
|
|
|
42
43
|
def _get_connection(self) -> sqlite3.Connection:
|
|
43
44
|
"""Get a SQLite connection to memory.db."""
|
|
@@ -45,6 +46,34 @@ class LifecycleEngine:
|
|
|
45
46
|
conn.row_factory = sqlite3.Row
|
|
46
47
|
return conn
|
|
47
48
|
|
|
49
|
+
def _ensure_columns(self) -> None:
|
|
50
|
+
"""Ensure v2.8 lifecycle columns exist in memories table."""
|
|
51
|
+
try:
|
|
52
|
+
conn = self._get_connection()
|
|
53
|
+
try:
|
|
54
|
+
cursor = conn.cursor()
|
|
55
|
+
cursor.execute("PRAGMA table_info(memories)")
|
|
56
|
+
existing = {row[1] for row in cursor.fetchall()}
|
|
57
|
+
v28_cols = [
|
|
58
|
+
("lifecycle_state", "TEXT DEFAULT 'active'"),
|
|
59
|
+
("lifecycle_updated_at", "TIMESTAMP"),
|
|
60
|
+
("lifecycle_history", "TEXT DEFAULT '[]'"),
|
|
61
|
+
("access_level", "TEXT DEFAULT 'public'"),
|
|
62
|
+
]
|
|
63
|
+
for col_name, col_type in v28_cols:
|
|
64
|
+
if col_name not in existing:
|
|
65
|
+
try:
|
|
66
|
+
cursor.execute(
|
|
67
|
+
f"ALTER TABLE memories ADD COLUMN {col_name} {col_type}"
|
|
68
|
+
)
|
|
69
|
+
except sqlite3.OperationalError:
|
|
70
|
+
pass
|
|
71
|
+
conn.commit()
|
|
72
|
+
finally:
|
|
73
|
+
conn.close()
|
|
74
|
+
except Exception:
|
|
75
|
+
pass # Graceful degradation — don't block engine init
|
|
76
|
+
|
|
48
77
|
def is_valid_transition(self, from_state: str, to_state: str) -> bool:
|
|
49
78
|
"""Check if a state transition is valid per the state machine.
|
|
50
79
|
|
|
@@ -70,10 +99,22 @@ class LifecycleEngine:
|
|
|
70
99
|
"""
|
|
71
100
|
conn = self._get_connection()
|
|
72
101
|
try:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
102
|
+
try:
|
|
103
|
+
row = conn.execute(
|
|
104
|
+
"SELECT lifecycle_state FROM memories WHERE id = ?",
|
|
105
|
+
(memory_id,),
|
|
106
|
+
).fetchone()
|
|
107
|
+
except sqlite3.OperationalError as e:
|
|
108
|
+
if "no such column" in str(e):
|
|
109
|
+
conn.close()
|
|
110
|
+
self._ensure_columns()
|
|
111
|
+
conn = self._get_connection()
|
|
112
|
+
row = conn.execute(
|
|
113
|
+
"SELECT lifecycle_state FROM memories WHERE id = ?",
|
|
114
|
+
(memory_id,),
|
|
115
|
+
).fetchone()
|
|
116
|
+
else:
|
|
117
|
+
raise
|
|
77
118
|
if row is None:
|
|
78
119
|
return None
|
|
79
120
|
return row["lifecycle_state"] or "active"
|
|
@@ -278,10 +319,22 @@ class LifecycleEngine:
|
|
|
278
319
|
conn = self._get_connection()
|
|
279
320
|
try:
|
|
280
321
|
dist = {state: 0 for state in self.STATES}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
322
|
+
try:
|
|
323
|
+
rows = conn.execute(
|
|
324
|
+
"SELECT lifecycle_state, COUNT(*) as cnt "
|
|
325
|
+
"FROM memories GROUP BY lifecycle_state"
|
|
326
|
+
).fetchall()
|
|
327
|
+
except sqlite3.OperationalError as e:
|
|
328
|
+
if "no such column" in str(e):
|
|
329
|
+
conn.close()
|
|
330
|
+
self._ensure_columns()
|
|
331
|
+
conn = self._get_connection()
|
|
332
|
+
rows = conn.execute(
|
|
333
|
+
"SELECT lifecycle_state, COUNT(*) as cnt "
|
|
334
|
+
"FROM memories GROUP BY lifecycle_state"
|
|
335
|
+
).fetchall()
|
|
336
|
+
else:
|
|
337
|
+
raise
|
|
285
338
|
for row in rows:
|
|
286
339
|
state = row["lifecycle_state"] if row["lifecycle_state"] else "active"
|
|
287
340
|
if state in dist:
|
|
@@ -59,6 +59,15 @@ class LifecycleEvaluator:
|
|
|
59
59
|
conn.row_factory = sqlite3.Row
|
|
60
60
|
return conn
|
|
61
61
|
|
|
62
|
+
def _ensure_lifecycle_columns(self) -> None:
|
|
63
|
+
"""Ensure v2.8 lifecycle columns exist via LifecycleEngine."""
|
|
64
|
+
try:
|
|
65
|
+
from lifecycle.lifecycle_engine import LifecycleEngine
|
|
66
|
+
engine = LifecycleEngine(db_path=self._db_path)
|
|
67
|
+
engine._ensure_columns()
|
|
68
|
+
except Exception:
|
|
69
|
+
pass # Best effort — don't block evaluation
|
|
70
|
+
|
|
62
71
|
def evaluate_memories(
|
|
63
72
|
self,
|
|
64
73
|
profile: Optional[str] = None,
|
|
@@ -87,7 +96,17 @@ class LifecycleEvaluator:
|
|
|
87
96
|
query += " AND profile = ?"
|
|
88
97
|
params.append(profile)
|
|
89
98
|
|
|
90
|
-
|
|
99
|
+
try:
|
|
100
|
+
rows = conn.execute(query, params).fetchall()
|
|
101
|
+
except sqlite3.OperationalError as e:
|
|
102
|
+
if "no such column" in str(e):
|
|
103
|
+
conn.close()
|
|
104
|
+
self._ensure_lifecycle_columns()
|
|
105
|
+
conn = self._get_connection()
|
|
106
|
+
rows = conn.execute(query, params).fetchall()
|
|
107
|
+
else:
|
|
108
|
+
raise
|
|
109
|
+
|
|
91
110
|
recommendations = []
|
|
92
111
|
now = datetime.now()
|
|
93
112
|
|
|
@@ -123,11 +142,24 @@ class LifecycleEvaluator:
|
|
|
123
142
|
config = self._load_config()
|
|
124
143
|
conn = self._get_connection()
|
|
125
144
|
try:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
145
|
+
try:
|
|
146
|
+
row = conn.execute(
|
|
147
|
+
"SELECT id, lifecycle_state, importance, last_accessed, created_at "
|
|
148
|
+
"FROM memories WHERE id = ?",
|
|
149
|
+
(memory_id,),
|
|
150
|
+
).fetchone()
|
|
151
|
+
except sqlite3.OperationalError as e:
|
|
152
|
+
if "no such column" in str(e):
|
|
153
|
+
conn.close()
|
|
154
|
+
self._ensure_lifecycle_columns()
|
|
155
|
+
conn = self._get_connection()
|
|
156
|
+
row = conn.execute(
|
|
157
|
+
"SELECT id, lifecycle_state, importance, last_accessed, created_at "
|
|
158
|
+
"FROM memories WHERE id = ?",
|
|
159
|
+
(memory_id,),
|
|
160
|
+
).fetchone()
|
|
161
|
+
else:
|
|
162
|
+
raise
|
|
131
163
|
if row is None:
|
|
132
164
|
return None
|
|
133
165
|
return self._evaluate_row(row, config, datetime.now())
|
package/src/mcp_tools_v28.py
CHANGED
|
@@ -46,7 +46,7 @@ async def report_outcome(
|
|
|
46
46
|
action_type: Category (code_written, decision_made, debug_resolved, etc.).
|
|
47
47
|
context: Optional JSON string with additional context metadata.
|
|
48
48
|
agent_id: Identifier for the reporting agent.
|
|
49
|
-
project: Project name for scoping.
|
|
49
|
+
project: Project name for scoping outcomes.
|
|
50
50
|
|
|
51
51
|
Returns:
|
|
52
52
|
Dict with success status and outcome_id on success.
|
|
@@ -170,17 +170,18 @@ async def compact_memories(
|
|
|
170
170
|
|
|
171
171
|
Args:
|
|
172
172
|
dry_run: If True (default), show what would happen without changes.
|
|
173
|
-
profile: Optional profile filter.
|
|
173
|
+
profile: Optional profile filter to scope compaction.
|
|
174
174
|
|
|
175
175
|
Returns:
|
|
176
176
|
Dict with recommendations (dry_run=True) or transition counts (dry_run=False).
|
|
177
177
|
"""
|
|
178
|
+
active_profile = profile
|
|
178
179
|
try:
|
|
179
180
|
from lifecycle.lifecycle_evaluator import LifecycleEvaluator
|
|
180
181
|
from lifecycle.lifecycle_engine import LifecycleEngine
|
|
181
182
|
|
|
182
183
|
evaluator = LifecycleEvaluator(DEFAULT_MEMORY_DB)
|
|
183
|
-
recommendations = evaluator.evaluate_memories(profile=
|
|
184
|
+
recommendations = evaluator.evaluate_memories(profile=active_profile)
|
|
184
185
|
|
|
185
186
|
if dry_run:
|
|
186
187
|
return {
|
package/src/memory-profiles.py
CHANGED
package/ui/index.html
CHANGED
|
@@ -234,7 +234,7 @@
|
|
|
234
234
|
vertical-align: middle;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
/*
|
|
237
|
+
/* Project select in navbar */
|
|
238
238
|
.profile-select {
|
|
239
239
|
background: rgba(255,255,255,0.15);
|
|
240
240
|
border: 1px solid rgba(255,255,255,0.3);
|
|
@@ -316,7 +316,7 @@
|
|
|
316
316
|
text-transform: uppercase;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
/*
|
|
319
|
+
/* Project delete button */
|
|
320
320
|
.btn-delete-profile {
|
|
321
321
|
padding: 2px 8px;
|
|
322
322
|
font-size: 0.75rem;
|
|
@@ -546,7 +546,7 @@
|
|
|
546
546
|
padding: 4px 8px;
|
|
547
547
|
}
|
|
548
548
|
|
|
549
|
-
/*
|
|
549
|
+
/* Project select - smaller */
|
|
550
550
|
.profile-select {
|
|
551
551
|
min-width: 90px;
|
|
552
552
|
font-size: 0.75rem;
|
|
@@ -641,7 +641,7 @@
|
|
|
641
641
|
<div class="d-flex align-items-center gap-3">
|
|
642
642
|
<div class="d-flex align-items-center gap-2">
|
|
643
643
|
<i class="bi bi-person-circle text-white-50"></i>
|
|
644
|
-
<select class="form-select form-select-sm profile-select" id="profile-select" title="Switch
|
|
644
|
+
<select class="form-select form-select-sm profile-select" id="profile-select" title="Switch profile">
|
|
645
645
|
<option value="default">default</option>
|
|
646
646
|
</select>
|
|
647
647
|
<button class="btn btn-sm theme-toggle" id="add-profile-btn" title="Create new profile" style="padding:2px 8px;font-size:1rem;line-height:1;">
|
|
@@ -738,6 +738,21 @@
|
|
|
738
738
|
<i class="bi bi-robot"></i> Agents
|
|
739
739
|
</button>
|
|
740
740
|
</li>
|
|
741
|
+
<li class="nav-item" role="presentation">
|
|
742
|
+
<button class="nav-link" id="lifecycle-tab" data-bs-toggle="tab" data-bs-target="#lifecycle-pane" type="button" role="tab">
|
|
743
|
+
<i class="bi bi-hourglass-split"></i> Lifecycle <span class="badge bg-success ms-1">v2.8</span>
|
|
744
|
+
</button>
|
|
745
|
+
</li>
|
|
746
|
+
<li class="nav-item" role="presentation">
|
|
747
|
+
<button class="nav-link" id="behavioral-tab" data-bs-toggle="tab" data-bs-target="#behavioral-pane" type="button" role="tab">
|
|
748
|
+
<i class="bi bi-lightbulb"></i> Behavioral <span class="badge bg-success ms-1">v2.8</span>
|
|
749
|
+
</button>
|
|
750
|
+
</li>
|
|
751
|
+
<li class="nav-item" role="presentation">
|
|
752
|
+
<button class="nav-link" id="compliance-tab" data-bs-toggle="tab" data-bs-target="#compliance-pane" type="button" role="tab">
|
|
753
|
+
<i class="bi bi-shield-lock"></i> Compliance <span class="badge bg-success ms-1">v2.8</span>
|
|
754
|
+
</button>
|
|
755
|
+
</li>
|
|
741
756
|
<li class="nav-item">
|
|
742
757
|
<button class="nav-link" id="settings-tab" data-bs-toggle="tab" data-bs-target="#settings-pane">
|
|
743
758
|
<i class="bi bi-gear"></i> Settings
|
|
@@ -1193,6 +1208,136 @@
|
|
|
1193
1208
|
</div>
|
|
1194
1209
|
</div>
|
|
1195
1210
|
|
|
1211
|
+
<!-- Lifecycle (v2.8) -->
|
|
1212
|
+
<div class="tab-pane fade" id="lifecycle-pane" role="tabpanel">
|
|
1213
|
+
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
1214
|
+
<h5 class="mb-0"><i class="bi bi-hourglass-split text-warning"></i> Memory Lifecycle</h5>
|
|
1215
|
+
<div>
|
|
1216
|
+
<span class="badge bg-secondary me-2" id="lifecycle-profile-badge">default</span>
|
|
1217
|
+
<button class="btn btn-sm btn-outline-info" onclick="compactDryRun()"><i class="bi bi-funnel"></i> Preview Compaction</button>
|
|
1218
|
+
<button class="btn btn-sm btn-outline-warning ms-1" onclick="compactExecute()"><i class="bi bi-lightning"></i> Compact Now</button>
|
|
1219
|
+
</div>
|
|
1220
|
+
</div>
|
|
1221
|
+
<!-- State Distribution -->
|
|
1222
|
+
<div class="row g-3 mb-4" id="lifecycle-states-row">
|
|
1223
|
+
<div class="col-md-2"><div class="card p-3 text-center"><div class="text-success fw-bold fs-3" id="lc-active-count">-</div><small class="text-muted">Active</small></div></div>
|
|
1224
|
+
<div class="col-md-2"><div class="card p-3 text-center"><div class="text-warning fw-bold fs-3" id="lc-warm-count">-</div><small class="text-muted">Warm</small></div></div>
|
|
1225
|
+
<div class="col-md-2"><div class="card p-3 text-center"><div class="text-info fw-bold fs-3" id="lc-cold-count">-</div><small class="text-muted">Cold</small></div></div>
|
|
1226
|
+
<div class="col-md-2"><div class="card p-3 text-center"><div class="text-secondary fw-bold fs-3" id="lc-archived-count">-</div><small class="text-muted">Archived</small></div></div>
|
|
1227
|
+
<div class="col-md-2"><div class="card p-3 text-center"><div class="text-danger fw-bold fs-3" id="lc-tombstoned-count">-</div><small class="text-muted">Tombstoned</small></div></div>
|
|
1228
|
+
<div class="col-md-2"><div class="card p-3 text-center"><div class="fw-bold fs-3" id="lc-total-count">-</div><small class="text-muted">Total</small></div></div>
|
|
1229
|
+
</div>
|
|
1230
|
+
<!-- State Progress Bar -->
|
|
1231
|
+
<div class="card p-3 mb-4">
|
|
1232
|
+
<h6 class="mb-2">State Distribution</h6>
|
|
1233
|
+
<div class="progress" style="height: 24px;" id="lifecycle-progress-bar"></div>
|
|
1234
|
+
</div>
|
|
1235
|
+
<!-- Age Stats -->
|
|
1236
|
+
<div class="card p-3 mb-4" id="lifecycle-age-stats">
|
|
1237
|
+
<h6 class="mb-2">Average Memory Age by State</h6>
|
|
1238
|
+
<div id="lifecycle-age-content"><span class="text-muted">Loading...</span></div>
|
|
1239
|
+
</div>
|
|
1240
|
+
<!-- Recent Transitions -->
|
|
1241
|
+
<div class="card p-3">
|
|
1242
|
+
<h6 class="mb-2">Recent Transitions</h6>
|
|
1243
|
+
<div id="lifecycle-transitions-content"><span class="text-muted">Loading...</span></div>
|
|
1244
|
+
</div>
|
|
1245
|
+
<!-- Compaction Results Modal -->
|
|
1246
|
+
<div id="compaction-results" class="card p-3 mt-3 d-none">
|
|
1247
|
+
<h6 class="mb-2" id="compaction-results-title">Compaction Preview</h6>
|
|
1248
|
+
<div id="compaction-results-content"></div>
|
|
1249
|
+
</div>
|
|
1250
|
+
</div>
|
|
1251
|
+
|
|
1252
|
+
<!-- Behavioral Learning (v2.8) -->
|
|
1253
|
+
<div class="tab-pane fade" id="behavioral-pane" role="tabpanel">
|
|
1254
|
+
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
1255
|
+
<h5 class="mb-0"><i class="bi bi-lightbulb text-info"></i> Behavioral Learning</h5>
|
|
1256
|
+
<span class="badge bg-secondary" id="behavioral-profile-badge">default</span>
|
|
1257
|
+
</div>
|
|
1258
|
+
<!-- Stats Row -->
|
|
1259
|
+
<div class="row g-3 mb-4">
|
|
1260
|
+
<div class="col-md-3"><div class="card p-3 text-center"><div class="fw-bold fs-3 text-success" id="bh-success-count">-</div><small class="text-muted">Successes</small></div></div>
|
|
1261
|
+
<div class="col-md-3"><div class="card p-3 text-center"><div class="fw-bold fs-3 text-danger" id="bh-failure-count">-</div><small class="text-muted">Failures</small></div></div>
|
|
1262
|
+
<div class="col-md-3"><div class="card p-3 text-center"><div class="fw-bold fs-3 text-warning" id="bh-partial-count">-</div><small class="text-muted">Partial</small></div></div>
|
|
1263
|
+
<div class="col-md-3"><div class="card p-3 text-center"><div class="fw-bold fs-3 text-info" id="bh-patterns-count">-</div><small class="text-muted">Patterns Learned</small></div></div>
|
|
1264
|
+
</div>
|
|
1265
|
+
<!-- Report Outcome Form -->
|
|
1266
|
+
<div class="card p-3 mb-4">
|
|
1267
|
+
<h6 class="mb-2"><i class="bi bi-plus-circle"></i> Report Outcome</h6>
|
|
1268
|
+
<div class="row g-2">
|
|
1269
|
+
<div class="col-md-3"><input type="text" class="form-control form-control-sm" id="bh-memory-ids" placeholder="Memory IDs (comma-separated)"></div>
|
|
1270
|
+
<div class="col-md-2"><select class="form-select form-select-sm" id="bh-outcome"><option value="success">Success</option><option value="failure">Failure</option><option value="partial">Partial</option></select></div>
|
|
1271
|
+
<div class="col-md-3"><select class="form-select form-select-sm" id="bh-action-type"><option value="code_written">Code Written</option><option value="decision_made">Decision Made</option><option value="debug_resolved">Debug Resolved</option><option value="architecture_chosen">Architecture Chosen</option><option value="other">Other</option></select></div>
|
|
1272
|
+
<div class="col-md-3"><input type="text" class="form-control form-control-sm" id="bh-context" placeholder="Context (optional)"></div>
|
|
1273
|
+
<div class="col-md-1"><button class="btn btn-sm btn-info w-100" onclick="reportOutcome()"><i class="bi bi-send"></i></button></div>
|
|
1274
|
+
</div>
|
|
1275
|
+
</div>
|
|
1276
|
+
<!-- Learned Patterns -->
|
|
1277
|
+
<div class="card p-3 mb-4">
|
|
1278
|
+
<h6 class="mb-2">Learned Patterns</h6>
|
|
1279
|
+
<div id="behavioral-patterns-content"><span class="text-muted">Loading...</span></div>
|
|
1280
|
+
</div>
|
|
1281
|
+
<!-- Cross-Project Transfers -->
|
|
1282
|
+
<div class="card p-3 mb-4">
|
|
1283
|
+
<h6 class="mb-2">Cross-Project Transfers</h6>
|
|
1284
|
+
<div id="behavioral-transfers-content"><span class="text-muted">Loading...</span></div>
|
|
1285
|
+
</div>
|
|
1286
|
+
<!-- Recent Outcomes -->
|
|
1287
|
+
<div class="card p-3">
|
|
1288
|
+
<h6 class="mb-2">Recent Outcomes</h6>
|
|
1289
|
+
<div id="behavioral-outcomes-content"><span class="text-muted">Loading...</span></div>
|
|
1290
|
+
</div>
|
|
1291
|
+
</div>
|
|
1292
|
+
|
|
1293
|
+
<!-- Compliance & Audit (v2.8) -->
|
|
1294
|
+
<div class="tab-pane fade" id="compliance-pane" role="tabpanel">
|
|
1295
|
+
<div class="d-flex justify-content-between align-items-center mb-3">
|
|
1296
|
+
<h5 class="mb-0"><i class="bi bi-shield-lock text-success"></i> Compliance & Audit</h5>
|
|
1297
|
+
<span class="badge bg-secondary" id="compliance-profile-badge">default</span>
|
|
1298
|
+
</div>
|
|
1299
|
+
<!-- Stats Row -->
|
|
1300
|
+
<div class="row g-3 mb-4">
|
|
1301
|
+
<div class="col-md-4"><div class="card p-3 text-center"><div class="fw-bold fs-3" id="cp-audit-count">-</div><small class="text-muted">Audit Events</small></div></div>
|
|
1302
|
+
<div class="col-md-4"><div class="card p-3 text-center"><div class="fw-bold fs-3" id="cp-retention-count">-</div><small class="text-muted">Retention Policies</small></div></div>
|
|
1303
|
+
<div class="col-md-4"><div class="card p-3 text-center"><div class="fw-bold fs-3" id="cp-abac-count">-</div><small class="text-muted">Access Policies</small></div></div>
|
|
1304
|
+
</div>
|
|
1305
|
+
<!-- Set Retention Policy Form -->
|
|
1306
|
+
<div class="card p-3 mb-4">
|
|
1307
|
+
<h6 class="mb-2"><i class="bi bi-plus-circle"></i> Create Retention Policy</h6>
|
|
1308
|
+
<div class="row g-2">
|
|
1309
|
+
<div class="col-md-3"><input type="text" class="form-control form-control-sm" id="cp-policy-name" placeholder="Policy name"></div>
|
|
1310
|
+
<div class="col-md-2"><input type="number" class="form-control form-control-sm" id="cp-retention-days" placeholder="Days" value="90"></div>
|
|
1311
|
+
<div class="col-md-2"><input type="text" class="form-control form-control-sm" id="cp-category" placeholder="Category (optional)"></div>
|
|
1312
|
+
<div class="col-md-2"><select class="form-select form-select-sm" id="cp-action"><option value="archive">Archive</option><option value="tombstone">Tombstone</option><option value="notify">Notify Only</option></select></div>
|
|
1313
|
+
<div class="col-md-2"><button class="btn btn-sm btn-success w-100" onclick="createRetentionPolicy()"><i class="bi bi-shield-plus"></i> Create</button></div>
|
|
1314
|
+
</div>
|
|
1315
|
+
</div>
|
|
1316
|
+
<!-- Retention Policies List -->
|
|
1317
|
+
<div class="card p-3 mb-4">
|
|
1318
|
+
<h6 class="mb-2">Active Retention Policies</h6>
|
|
1319
|
+
<div id="compliance-policies-content"><span class="text-muted">Loading...</span></div>
|
|
1320
|
+
</div>
|
|
1321
|
+
<!-- Audit Trail -->
|
|
1322
|
+
<div class="card p-3">
|
|
1323
|
+
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
1324
|
+
<h6 class="mb-0">Audit Trail</h6>
|
|
1325
|
+
<div class="d-flex gap-2">
|
|
1326
|
+
<select class="form-select form-select-sm" id="cp-audit-filter" style="width: auto;" onchange="loadCompliance()">
|
|
1327
|
+
<option value="">All Events</option>
|
|
1328
|
+
<option value="recall">Recall</option>
|
|
1329
|
+
<option value="remember">Remember</option>
|
|
1330
|
+
<option value="delete">Delete</option>
|
|
1331
|
+
<option value="lifecycle_transition">Lifecycle</option>
|
|
1332
|
+
<option value="access_denied">Access Denied</option>
|
|
1333
|
+
<option value="retention_enforced">Retention</option>
|
|
1334
|
+
</select>
|
|
1335
|
+
</div>
|
|
1336
|
+
</div>
|
|
1337
|
+
<div id="compliance-audit-content"><span class="text-muted">Loading...</span></div>
|
|
1338
|
+
</div>
|
|
1339
|
+
</div>
|
|
1340
|
+
|
|
1196
1341
|
<!-- Settings & Backup -->
|
|
1197
1342
|
<div class="tab-pane fade" id="settings-pane">
|
|
1198
1343
|
<!-- Profile Management -->
|
|
@@ -1360,6 +1505,9 @@
|
|
|
1360
1505
|
<script src="static/js/agents.js"></script>
|
|
1361
1506
|
<script src="static/js/learning.js"></script>
|
|
1362
1507
|
<script src="static/js/feedback.js"></script>
|
|
1508
|
+
<script src="static/js/lifecycle.js"></script>
|
|
1509
|
+
<script src="static/js/behavioral.js"></script>
|
|
1510
|
+
<script src="static/js/compliance.js"></script>
|
|
1363
1511
|
<script src="static/js/init.js"></script>
|
|
1364
1512
|
|
|
1365
1513
|
<footer>
|