superlocalmemory 3.4.0 → 3.4.3

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 (63) hide show
  1. package/README.md +7 -8
  2. package/docs/screenshots/01-dashboard-main.png +0 -0
  3. package/docs/screenshots/02-knowledge-graph.png +0 -0
  4. package/docs/screenshots/03-patterns-learning.png +0 -0
  5. package/docs/screenshots/04-learning-dashboard.png +0 -0
  6. package/docs/screenshots/05-behavioral-analysis.png +0 -0
  7. package/docs/screenshots/06-graph-communities.png +0 -0
  8. package/package.json +2 -2
  9. package/pyproject.toml +11 -2
  10. package/scripts/postinstall.js +26 -7
  11. package/src/superlocalmemory/cli/commands.py +42 -60
  12. package/src/superlocalmemory/cli/daemon.py +107 -47
  13. package/src/superlocalmemory/cli/main.py +10 -0
  14. package/src/superlocalmemory/cli/setup_wizard.py +137 -9
  15. package/src/superlocalmemory/core/config.py +28 -0
  16. package/src/superlocalmemory/core/consolidation_engine.py +38 -1
  17. package/src/superlocalmemory/core/engine.py +9 -0
  18. package/src/superlocalmemory/core/engine_wiring.py +5 -1
  19. package/src/superlocalmemory/core/graph_analyzer.py +254 -12
  20. package/src/superlocalmemory/core/health_monitor.py +313 -0
  21. package/src/superlocalmemory/core/reranker_worker.py +19 -5
  22. package/src/superlocalmemory/ingestion/__init__.py +13 -0
  23. package/src/superlocalmemory/ingestion/adapter_manager.py +234 -0
  24. package/src/superlocalmemory/ingestion/base_adapter.py +177 -0
  25. package/src/superlocalmemory/ingestion/calendar_adapter.py +340 -0
  26. package/src/superlocalmemory/ingestion/credentials.py +118 -0
  27. package/src/superlocalmemory/ingestion/gmail_adapter.py +369 -0
  28. package/src/superlocalmemory/ingestion/parsers.py +100 -0
  29. package/src/superlocalmemory/ingestion/transcript_adapter.py +156 -0
  30. package/src/superlocalmemory/learning/consolidation_worker.py +287 -53
  31. package/src/superlocalmemory/learning/entity_compiler.py +377 -0
  32. package/src/superlocalmemory/mesh/__init__.py +12 -0
  33. package/src/superlocalmemory/mesh/broker.py +344 -0
  34. package/src/superlocalmemory/retrieval/entity_channel.py +141 -4
  35. package/src/superlocalmemory/retrieval/spreading_activation.py +45 -0
  36. package/src/superlocalmemory/server/api.py +15 -8
  37. package/src/superlocalmemory/server/routes/behavioral.py +8 -4
  38. package/src/superlocalmemory/server/routes/chat.py +320 -0
  39. package/src/superlocalmemory/server/routes/entity.py +95 -0
  40. package/src/superlocalmemory/server/routes/ingest.py +110 -0
  41. package/src/superlocalmemory/server/routes/insights.py +368 -0
  42. package/src/superlocalmemory/server/routes/learning.py +106 -6
  43. package/src/superlocalmemory/server/routes/memories.py +20 -9
  44. package/src/superlocalmemory/server/routes/mesh.py +186 -0
  45. package/src/superlocalmemory/server/routes/stats.py +25 -3
  46. package/src/superlocalmemory/server/routes/timeline.py +252 -0
  47. package/src/superlocalmemory/server/routes/v3_api.py +161 -0
  48. package/src/superlocalmemory/server/ui.py +8 -0
  49. package/src/superlocalmemory/server/unified_daemon.py +691 -0
  50. package/src/superlocalmemory/storage/schema_v343.py +229 -0
  51. package/src/superlocalmemory/ui/index.html +168 -58
  52. package/src/superlocalmemory/ui/js/graph-event-bus.js +83 -0
  53. package/src/superlocalmemory/ui/js/graph-filters.js +1 -1
  54. package/src/superlocalmemory/ui/js/knowledge-graph.js +942 -0
  55. package/src/superlocalmemory/ui/js/memory-chat.js +344 -0
  56. package/src/superlocalmemory/ui/js/memory-timeline.js +265 -0
  57. package/src/superlocalmemory/ui/js/quick-actions.js +334 -0
  58. package/src/superlocalmemory.egg-info/PKG-INFO +0 -594
  59. package/src/superlocalmemory.egg-info/SOURCES.txt +0 -279
  60. package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
  61. package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
  62. package/src/superlocalmemory.egg-info/requires.txt +0 -47
  63. package/src/superlocalmemory.egg-info/top_level.txt +0 -1
@@ -0,0 +1,229 @@
1
+ # Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
2
+ # Licensed under the Elastic License 2.0 - see LICENSE file
3
+ # Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
4
+
5
+ """SuperLocalMemory V3.4.3 "The Unified Brain" — Schema Extensions.
6
+
7
+ All new tables for v3.4.3 features:
8
+ - Phase C: Mesh broker tables (mesh_peers, mesh_messages, mesh_state, mesh_locks, mesh_events)
9
+ - Phase D: Entity compilation (ALTER entity_profiles + new index)
10
+ - Phase E: Ingestion log (ingestion_log)
11
+
12
+ Design rules (inherited from schema_v32.py):
13
+ - CREATE IF NOT EXISTS for idempotency
14
+ - profile_id where applicable
15
+ - Never ALTER existing column types (add new columns only)
16
+
17
+ Part of Qualixar | Author: Varun Pratap Bhardwaj
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import logging
23
+ import sqlite3
24
+ from typing import Final
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ # ---------------------------------------------------------------------------
29
+ # Table names (for reference and rollback)
30
+ # ---------------------------------------------------------------------------
31
+
32
+ V343_TABLES: Final[tuple[str, ...]] = (
33
+ "mesh_peers",
34
+ "mesh_messages",
35
+ "mesh_state",
36
+ "mesh_locks",
37
+ "mesh_events",
38
+ "ingestion_log",
39
+ )
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # DDL — Phase C: Mesh Broker Tables
43
+ # ---------------------------------------------------------------------------
44
+
45
+ _MESH_DDL = """
46
+ -- Mesh Peers
47
+ CREATE TABLE IF NOT EXISTS mesh_peers (
48
+ peer_id TEXT PRIMARY KEY,
49
+ session_id TEXT NOT NULL,
50
+ summary TEXT DEFAULT '',
51
+ status TEXT DEFAULT 'active',
52
+ host TEXT DEFAULT '127.0.0.1',
53
+ port INTEGER DEFAULT 0,
54
+ registered_at TEXT NOT NULL,
55
+ last_heartbeat TEXT NOT NULL
56
+ );
57
+
58
+ -- Mesh Messages
59
+ CREATE TABLE IF NOT EXISTS mesh_messages (
60
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
61
+ from_peer TEXT NOT NULL,
62
+ to_peer TEXT NOT NULL,
63
+ msg_type TEXT DEFAULT 'text',
64
+ content TEXT NOT NULL,
65
+ read INTEGER DEFAULT 0,
66
+ created_at TEXT NOT NULL
67
+ );
68
+
69
+ -- Mesh State (shared key-value store)
70
+ CREATE TABLE IF NOT EXISTS mesh_state (
71
+ key TEXT PRIMARY KEY,
72
+ value TEXT NOT NULL,
73
+ set_by TEXT NOT NULL,
74
+ updated_at TEXT NOT NULL
75
+ );
76
+
77
+ -- Mesh Locks (file-level locks for coordination)
78
+ CREATE TABLE IF NOT EXISTS mesh_locks (
79
+ file_path TEXT PRIMARY KEY,
80
+ locked_by TEXT NOT NULL,
81
+ locked_at TEXT NOT NULL,
82
+ expires_at TEXT NOT NULL DEFAULT '9999-12-31T23:59:59Z'
83
+ );
84
+
85
+ -- Mesh Events (audit log)
86
+ CREATE TABLE IF NOT EXISTS mesh_events (
87
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
88
+ event_type TEXT NOT NULL,
89
+ payload TEXT DEFAULT '{}',
90
+ emitted_by TEXT NOT NULL,
91
+ created_at TEXT NOT NULL
92
+ );
93
+
94
+ CREATE INDEX IF NOT EXISTS idx_mesh_messages_to
95
+ ON mesh_messages(to_peer, read);
96
+ CREATE INDEX IF NOT EXISTS idx_mesh_events_type
97
+ ON mesh_events(event_type, created_at);
98
+ """
99
+
100
+ # ---------------------------------------------------------------------------
101
+ # DDL — Phase D: Entity Compilation (ALTER entity_profiles)
102
+ # ---------------------------------------------------------------------------
103
+
104
+ _ENTITY_COMPILATION_ALTERS = [
105
+ "ALTER TABLE entity_profiles ADD COLUMN project_name TEXT DEFAULT ''",
106
+ "ALTER TABLE entity_profiles ADD COLUMN compiled_truth TEXT DEFAULT ''",
107
+ "ALTER TABLE entity_profiles ADD COLUMN timeline TEXT DEFAULT '[]'",
108
+ "ALTER TABLE entity_profiles ADD COLUMN compilation_confidence REAL DEFAULT 0.5",
109
+ "ALTER TABLE entity_profiles ADD COLUMN last_compiled_at TEXT DEFAULT NULL",
110
+ ]
111
+
112
+ _ENTITY_COMPILATION_INDEX = """
113
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_entity_profiles_compilation
114
+ ON entity_profiles(entity_id, profile_id, project_name);
115
+ CREATE INDEX IF NOT EXISTS idx_entity_profiles_project
116
+ ON entity_profiles(profile_id, project_name);
117
+ """
118
+
119
+ # ---------------------------------------------------------------------------
120
+ # DDL — Phase E: Ingestion Log
121
+ # ---------------------------------------------------------------------------
122
+
123
+ _INGESTION_DDL = """
124
+ CREATE TABLE IF NOT EXISTS ingestion_log (
125
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
126
+ source_type TEXT NOT NULL,
127
+ dedup_key TEXT NOT NULL,
128
+ fact_ids TEXT DEFAULT '[]',
129
+ metadata TEXT DEFAULT '{}',
130
+ status TEXT DEFAULT 'ingested',
131
+ ingested_at TEXT NOT NULL,
132
+ UNIQUE(source_type, dedup_key)
133
+ );
134
+ CREATE INDEX IF NOT EXISTS idx_ingestion_dedup
135
+ ON ingestion_log(source_type, dedup_key);
136
+ """
137
+
138
+ # ---------------------------------------------------------------------------
139
+ # Version marker
140
+ # ---------------------------------------------------------------------------
141
+
142
+ _VERSION_DDL = """
143
+ CREATE TABLE IF NOT EXISTS schema_version (
144
+ version TEXT PRIMARY KEY,
145
+ applied_at TEXT NOT NULL
146
+ );
147
+ """
148
+
149
+
150
+ # ---------------------------------------------------------------------------
151
+ # Migration runner
152
+ # ---------------------------------------------------------------------------
153
+
154
+ def apply_v343_schema(db_path: str | sqlite3.Connection) -> dict:
155
+ """Apply all v3.4.3 schema changes. Idempotent — safe to call multiple times.
156
+
157
+ Returns dict with migration status and any errors.
158
+ """
159
+ result = {"applied": [], "skipped": [], "errors": []}
160
+
161
+ if isinstance(db_path, sqlite3.Connection):
162
+ conn = db_path
163
+ own_connection = False
164
+ else:
165
+ conn = sqlite3.connect(str(db_path))
166
+ own_connection = True
167
+
168
+ try:
169
+ # Check if already applied
170
+ try:
171
+ row = conn.execute(
172
+ "SELECT version FROM schema_version WHERE version = '3.4.3'"
173
+ ).fetchone()
174
+ if row:
175
+ result["skipped"].append("v3.4.3 already applied")
176
+ return result
177
+ except sqlite3.OperationalError:
178
+ pass # schema_version table doesn't exist yet
179
+
180
+ # Apply version table
181
+ conn.executescript(_VERSION_DDL)
182
+
183
+ # Phase C: Mesh tables
184
+ try:
185
+ conn.executescript(_MESH_DDL)
186
+ result["applied"].append("mesh tables (5 tables, 2 indexes)")
187
+ except sqlite3.OperationalError as e:
188
+ result["errors"].append(f"mesh: {e}")
189
+
190
+ # Phase D: Entity compilation ALTER TABLE
191
+ for alter_sql in _ENTITY_COMPILATION_ALTERS:
192
+ try:
193
+ conn.execute(alter_sql)
194
+ result["applied"].append(alter_sql.split("ADD COLUMN")[1].strip().split()[0])
195
+ except sqlite3.OperationalError:
196
+ # Column already exists — fine (idempotent)
197
+ pass
198
+
199
+ try:
200
+ conn.executescript(_ENTITY_COMPILATION_INDEX)
201
+ result["applied"].append("entity_profiles indexes")
202
+ except sqlite3.OperationalError as e:
203
+ result["errors"].append(f"entity indexes: {e}")
204
+
205
+ # Phase E: Ingestion log
206
+ try:
207
+ conn.executescript(_INGESTION_DDL)
208
+ result["applied"].append("ingestion_log table")
209
+ except sqlite3.OperationalError as e:
210
+ result["errors"].append(f"ingestion: {e}")
211
+
212
+ # Mark version
213
+ conn.execute(
214
+ "INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (?, ?)",
215
+ ("3.4.3", __import__("datetime").datetime.now().isoformat()),
216
+ )
217
+ conn.commit()
218
+
219
+ if result["applied"]:
220
+ logger.info("Schema v3.4.3 applied: %s", ", ".join(result["applied"]))
221
+
222
+ except Exception as e:
223
+ result["errors"].append(f"fatal: {e}")
224
+ logger.error("Schema v3.4.3 migration failed: %s", e)
225
+ finally:
226
+ if own_connection:
227
+ conn.close()
228
+
229
+ return result
@@ -654,6 +654,9 @@
654
654
  </button>
655
655
  </div>
656
656
  <span class="text-white-50 d-none d-md-inline" id="navbar-subtitle">Knowledge Graph Explorer</span>
657
+ <a href="https://github.com/qualixar/superlocalmemory" target="_blank" class="btn btn-sm btn-outline-light ms-2 d-none d-md-inline-flex align-items-center" style="font-size:0.75rem; gap:4px;" title="Star us on GitHub">
658
+ <i class="bi bi-github"></i> <i class="bi bi-star"></i> Star
659
+ </a>
657
660
  <button class="btn btn-sm theme-toggle" id="refresh-dashboard-btn" onclick="refreshDashboard()" title="Refresh Dashboard" style="padding:4px 8px;">
658
661
  <i class="bi bi-arrow-clockwise" aria-hidden="true"></i>
659
662
  </button>
@@ -894,7 +897,7 @@
894
897
  <div class="row align-items-center mb-2">
895
898
  <div class="col-md-8">
896
899
  <h5 class="mb-0">Interactive Knowledge Graph</h5>
897
- <small class="text-muted">Zoom, pan, click nodes to explore • Powered by Cytoscape.js</small>
900
+ <small class="text-muted" id="graph-renderer-label">Zoom, pan, click nodes to explore • <span id="graph-engine-name">Sigma.js WebGL</span></small>
898
901
  <div id="graph-stats" class="mt-2"></div>
899
902
  </div>
900
903
  <div class="col-md-4 text-end">
@@ -902,62 +905,161 @@
902
905
  <i class="bi bi-arrow-clockwise"></i> Refresh
903
906
  </button>
904
907
  <select class="form-select form-select-sm d-inline-block w-auto" id="graph-max-nodes" onchange="loadGraph()" aria-label="Select maximum number of nodes to display">
905
- <option value="50" selected>50 nodes</option>
908
+ <option value="50">50 nodes</option>
906
909
  <option value="100">100 nodes</option>
907
- <option value="200">200 nodes</option>
910
+ <option value="200" selected>200 nodes</option>
908
911
  <option value="500">500 nodes</option>
912
+ <option value="1000">1,000 nodes</option>
913
+ <option value="2000">2,000 nodes</option>
914
+ <option value="5000">All (5K+)</option>
909
915
  </select>
910
916
  </div>
911
917
  </div>
912
- <div class="row align-items-center">
913
- <div class="col-md-6">
914
- <label class="form-label mb-1 small" for="graph-layout-selector">Layout Algorithm:</label>
915
- <select class="form-select form-select-sm" id="graph-layout-selector" onchange="changeGraphLayout(this.value)" aria-label="Select graph layout algorithm">
916
- <option value="fcose" selected>Force-Directed (Fast)</option>
917
- <option value="cose">Force-Directed (Classic)</option>
918
- <option value="circle">Circular</option>
919
- <option value="grid">Grid</option>
920
- <option value="breadthfirst">Hierarchical</option>
921
- <option value="concentric">Concentric (by Importance)</option>
922
- </select>
918
+ <!-- Filter status (kept for cluster/modal integration) -->
919
+ <div id="graph-status-container" style="display:none;">
920
+ <div id="graph-status-full"></div>
921
+ <div id="graph-status-filtered"></div>
922
+ </div>
923
+ </div>
924
+ <!-- v3.4.1: Sigma.js controls bar -->
925
+ <div class="row g-2 mb-2" id="sigma-controls">
926
+ <div class="col-12">
927
+ <div class="d-flex align-items-center gap-2">
928
+ <div class="input-group input-group-sm" style="max-width:400px;">
929
+ <span class="input-group-text"><i class="bi bi-search"></i></span>
930
+ <input type="text" class="form-control" id="sigma-search-input"
931
+ placeholder="Search memories in graph..." aria-label="Search graph nodes"
932
+ oninput="sigmaSearch(this.value)">
933
+ <button class="btn btn-outline-secondary" type="button"
934
+ onclick="sigmaClearSearch(); document.getElementById('sigma-search-input').value='';">
935
+ <i class="bi bi-x-lg"></i>
936
+ </button>
937
+ </div>
938
+ <div class="btn-group btn-group-sm">
939
+ <button class="btn btn-outline-secondary" onclick="sigmaZoomIn()" title="Zoom In"><i class="bi bi-zoom-in"></i></button>
940
+ <button class="btn btn-outline-secondary" onclick="sigmaZoomOut()" title="Zoom Out"><i class="bi bi-zoom-out"></i></button>
941
+ <button class="btn btn-outline-secondary" onclick="sigmaResetView()" title="Reset View"><i class="bi bi-arrows-angle-expand"></i></button>
942
+ </div>
923
943
  </div>
924
- <div class="col-md-6">
925
- <!-- Clear status indicator - shows what user is viewing -->
926
- <div id="graph-status-container">
927
- <!-- When viewing FULL graph (default state) -->
928
- <div id="graph-status-full" style="display:block;" role="status" aria-live="polite">
929
- <div class="d-flex align-items-center">
930
- <span class="text-muted small">
931
- <i class="bi bi-diagram-3"></i> <span id="graph-status-full-text">Showing all memories</span>
932
- </span>
933
- <button class="btn btn-sm btn-outline-secondary ms-2" onclick="loadGraph()" aria-label="Refresh current graph view">
934
- <i class="bi bi-arrow-clockwise"></i> Refresh
935
- </button>
936
- </div>
937
- </div>
944
+ </div>
945
+ </div>
938
946
 
939
- <!-- When viewing FILTERED by cluster (show BIG clear button) -->
940
- <div id="graph-status-filtered" style="display:none;" role="status" aria-live="polite">
941
- <div class="alert alert-info py-2 px-3 mb-0 d-flex align-items-center justify-content-between">
942
- <span>
943
- <i class="bi bi-funnel-fill"></i>
944
- <strong id="graph-filter-description">Viewing Cluster X</strong>
945
- <span class="small text-muted ms-2" id="graph-filter-count">(X memories)</span>
946
- </span>
947
- <button class="btn btn-primary btn-sm ms-3" onclick="clearGraphFilters()" aria-label="Clear filter and show all memories">
948
- <i class="bi bi-grid-3x3"></i> Show All Memories
949
- </button>
950
- </div>
947
+ <div class="row g-2">
948
+ <!-- LEFT PANEL: Legend + Communities + Stats -->
949
+ <div class="col-md-2" id="graph-left-panel">
950
+ <div class="card p-2" style="font-size:0.8rem; max-height:550px; overflow-y:auto;">
951
+ <h6 class="mb-2"><i class="bi bi-palette"></i> Legend</h6>
952
+ <div class="mb-1"><span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#667eea;"></span> Semantic</div>
953
+ <div class="mb-1"><span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#43e97b;"></span> Episodic</div>
954
+ <div class="mb-1"><span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#f093fb;"></span> Opinion</div>
955
+ <div class="mb-1"><span style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#4facfe;"></span> Temporal</div>
956
+ <hr class="my-2">
957
+ <h6 class="mb-2 d-flex justify-content-between align-items-center">
958
+ <span><i class="bi bi-collection"></i> Communities</span>
959
+ <div class="btn-group btn-group-sm" role="group" aria-label="Community source">
960
+ <input type="radio" class="btn-check" name="community-source" id="community-live" checked onchange="handleCommunitySourceToggle('live')">
961
+ <label class="btn btn-outline-secondary py-0 px-1" style="font-size:0.65rem;" for="community-live">Live</label>
962
+ <input type="radio" class="btn-check" name="community-source" id="community-backend" onchange="handleCommunitySourceToggle('backend')">
963
+ <label class="btn btn-outline-secondary py-0 px-1" style="font-size:0.65rem;" for="community-backend">Backend</label>
951
964
  </div>
965
+ </h6>
966
+ <div class="mb-1" id="community-resolution-container">
967
+ <label class="form-label small mb-0">Resolution <span id="community-resolution-value" class="text-muted">1.0</span></label>
968
+ <input type="range" class="form-range" id="community-resolution" min="0.1" max="3.0" step="0.1" value="1.0"
969
+ oninput="handleResolutionChange(this.value)">
970
+ </div>
971
+ <div id="community-list-panel">
972
+ <div class="text-muted small">No communities detected yet.</div>
973
+ <button class="btn btn-sm btn-outline-primary w-100 mt-1" onclick="runCommunityDetection()">
974
+ <i class="bi bi-cpu"></i> Detect
975
+ </button>
976
+ </div>
977
+ <hr class="my-2">
978
+ <h6 class="mb-2"><i class="bi bi-graph-up"></i> Stats</h6>
979
+ <div id="sigma-stats-panel">
980
+ <div class="text-muted">Loading...</div>
981
+ </div>
982
+ <hr class="my-2">
983
+ <h6 class="mb-2"><i class="bi bi-funnel"></i> Filter</h6>
984
+ <select class="form-select form-select-sm mb-1" id="sigma-category-filter"
985
+ onchange="sigmaFilterByCategory(this.value)">
986
+ <option value="">All types</option>
987
+ <option value="semantic">Semantic</option>
988
+ <option value="episodic">Episodic</option>
989
+ <option value="opinion">Opinion</option>
990
+ <option value="temporal">Temporal</option>
991
+ </select>
992
+ </div>
993
+ </div>
994
+
995
+ <!-- CENTER: Graph canvas -->
996
+ <div class="col-md-7">
997
+ <div id="graph-container"
998
+ role="application"
999
+ aria-label="Interactive knowledge graph"
1000
+ aria-describedby="graph-stats"
1001
+ style="width:100%; height:550px; border:1px solid #dee2e6; border-radius:8px; background:#fafbfc;">
1002
+ </div>
1003
+ </div>
1004
+
1005
+ <!-- RIGHT PANEL: Node detail -->
1006
+ <div class="col-md-3" id="graph-right-panel">
1007
+ <div class="card p-2" id="sigma-detail-panel" style="font-size:0.85rem; max-height:550px; overflow-y:auto;">
1008
+ <h6 class="mb-2"><i class="bi bi-info-circle"></i> Node Detail</h6>
1009
+ <div id="sigma-detail-content" class="text-muted">
1010
+ Click a node to see its details, connected memories, and trust score.
952
1011
  </div>
953
1012
  </div>
954
1013
  </div>
955
1014
  </div>
956
- <div id="graph-container"
957
- role="application"
958
- aria-label="Interactive knowledge graph - use Tab to navigate nodes, Enter to view details, Arrow keys to move between adjacent nodes, Escape to clear filters"
959
- aria-describedby="graph-stats"
960
- style="width:100%; height:600px; border:1px solid #dee2e6; border-radius:8px; background:#f8f9fa;">
1015
+
1016
+ <!-- v3.4.1: Quick Insight Actions -->
1017
+ <div class="row g-2 mt-2">
1018
+ <div class="col-12">
1019
+ <div class="card p-2">
1020
+ <div class="d-flex flex-wrap gap-1 mb-2">
1021
+ <button class="btn btn-sm btn-outline-info" data-insight-action="changed_this_week">
1022
+ <i class="bi bi-clock-history"></i> Changed This Week
1023
+ </button>
1024
+ <button class="btn btn-sm btn-outline-warning" data-insight-action="opinions">
1025
+ <i class="bi bi-lightbulb"></i> Opinions & Rationale
1026
+ </button>
1027
+ <button class="btn btn-sm btn-outline-danger" data-insight-action="contradictions">
1028
+ <i class="bi bi-exclamation-triangle"></i> Contradictions
1029
+ </button>
1030
+ <button class="btn btn-sm btn-outline-success" data-insight-action="health">
1031
+ <i class="bi bi-heart-pulse"></i> Memory Health
1032
+ </button>
1033
+ <button class="btn btn-sm btn-outline-primary" data-insight-action="cross_project">
1034
+ <i class="bi bi-diagram-3"></i> Cross-Project Links
1035
+ </button>
1036
+ </div>
1037
+ <div id="insight-results" style="max-height:300px; overflow-y:auto;"></div>
1038
+ </div>
1039
+ </div>
1040
+ </div>
1041
+
1042
+ <!-- v3.4.1: Memory Timeline (D3.js) -->
1043
+ <div id="memory-timeline-panel" class="mt-3">
1044
+ <div class="d-flex justify-content-between align-items-center mb-2">
1045
+ <h6 class="mb-0"><i class="bi bi-clock-history"></i> Memory Timeline</h6>
1046
+ <div class="btn-group btn-group-sm" role="group" id="timeline-zoom-group">
1047
+ <button type="button" class="btn btn-outline-secondary active" data-zoom="week">Week</button>
1048
+ <button type="button" class="btn btn-outline-secondary" data-zoom="day">Day</button>
1049
+ <button type="button" class="btn btn-outline-secondary" data-zoom="month">Month</button>
1050
+ </div>
1051
+ <div class="btn-group btn-group-sm ms-2" role="group" id="timeline-group-by-group">
1052
+ <button type="button" class="btn btn-outline-info active" data-groupby="category">By Type</button>
1053
+ <button type="button" class="btn btn-outline-info" data-groupby="community">By Community</button>
1054
+ </div>
1055
+ </div>
1056
+ <div id="memory-timeline-chart" style="width:100%;height:280px;overflow:hidden;border:1px solid #dee2e6;border-radius:8px;background:#fafbfc;"></div>
1057
+ <div class="d-flex gap-3 mt-1 small text-muted">
1058
+ <span><span class="badge" style="background:#198754">&nbsp;</span> High trust (>=0.7)</span>
1059
+ <span><span class="badge" style="background:#ffc107">&nbsp;</span> Medium (0.4-0.7)</span>
1060
+ <span><span class="badge" style="background:#dc3545">&nbsp;</span> Low (<0.4)</span>
1061
+ <span><span class="badge" style="background:#6c757d">&nbsp;</span> Unknown</span>
1062
+ </div>
961
1063
  </div>
962
1064
 
963
1065
  <!-- Skip link for keyboard users -->
@@ -1830,22 +1932,30 @@
1830
1932
  <!-- D3.js (kept for backward compatibility) -->
1831
1933
  <script src="https://d3js.org/d3.v7.min.js"></script>
1832
1934
 
1833
- <!-- Cytoscape.js + Extensions (v2.6.5interactive graph) -->
1834
- <script src="https://unpkg.com/cytoscape@3.30.4/dist/cytoscape.min.js"></script>
1835
- <script src="https://unpkg.com/layout-base@2.0.1/layout-base.js"></script>
1836
- <script src="https://unpkg.com/cose-base@2.2.0/cose-base.js"></script>
1837
- <script src="https://unpkg.com/cytoscape-fcose@2.2.0/cytoscape-fcose.js"></script>
1838
- <link href="https://unpkg.com/cytoscape-navigator@1.3.3/cytoscape.js-navigator.css" rel="stylesheet" type="text/css" />
1839
- <script src="https://unpkg.com/cytoscape-navigator@1.3.3/cytoscape-navigator.js"></script>
1935
+ <!-- Cytoscape.js REMOVED in v3.4.1replaced by Sigma.js WebGL -->
1936
+
1937
+ <!-- Sigma.js v3 + graphology (v3.4.1 — WebGL graph) -->
1938
+ <script src="https://cdn.jsdelivr.net/npm/graphology@0.25.4/dist/graphology.umd.min.js"></script>
1939
+ <script src="https://cdn.jsdelivr.net/npm/graphology-library@0.8.0/dist/graphology-library.min.js"></script>
1940
+ <script src="https://cdn.jsdelivr.net/npm/sigma@2.4.0/build/sigma.min.js"></script>
1840
1941
 
1841
1942
  <!-- Modular JS (v2.5 — split from monolith app.js) -->
1842
1943
  <script src="static/js/core.js"></script>
1843
1944
 
1844
- <!-- Graph Visualization Scripts (v2.6.5 - Modular) -->
1845
- <script src="static/js/graph-core.js"></script>
1846
- <script src="static/js/graph-filters.js"></script>
1945
+ <!-- v3.4.1: Event bus (must load before graph + chat) -->
1946
+ <script src="static/js/graph-event-bus.js"></script>
1947
+ <!-- v3.4.1: Sigma.js Knowledge Graph (defines globals: filterState, loadGraph, renderGraph) -->
1948
+ <script src="static/js/knowledge-graph.js"></script>
1949
+ <!-- Shared UI utilities (showLoadingSpinner, updateGraphStats, etc.) -->
1847
1950
  <script src="static/js/graph-ui.js"></script>
1848
- <script src="static/js/graph-interactions.js"></script>
1951
+ <!-- Filter functions (filterByCluster, filterByEntity — used by clusters.js, modal.js) -->
1952
+ <script src="static/js/graph-filters.js"></script>
1953
+ <!-- v3.4.1: Ask My Memory chat interface -->
1954
+ <script src="static/js/memory-chat.js"></script>
1955
+ <!-- v3.4.1: Quick Insight Actions (5 one-click intelligence buttons) -->
1956
+ <script src="static/js/quick-actions.js"></script>
1957
+ <!-- v3.4.1: Memory Timeline (D3 time axis, zoom, trust colors) -->
1958
+ <script src="static/js/memory-timeline.js"></script>
1849
1959
 
1850
1960
  <script src="static/js/memories.js"></script>
1851
1961
  <script src="static/js/search.js"></script>
@@ -1872,8 +1982,8 @@
1872
1982
  <script src="static/js/fact-detail.js"></script>
1873
1983
 
1874
1984
  <footer>
1875
- <p>SuperLocalMemory V3 by <a href="https://github.com/varun369">Varun Pratap Bhardwaj</a></p>
1876
- <p><a href="https://github.com/qualixar/superlocalmemory">GitHub Repository</a> | Elastic License 2.0</p>
1985
+ <p>SuperLocalMemory V3 by <a href="https://github.com/varun369">Varun Pratap Bhardwaj</a> · <a href="https://qualixar.com">Qualixar</a></p>
1986
+ <p><a href="https://github.com/qualixar/superlocalmemory">GitHub</a> · AGPL-3.0-or-later · <a href="https://github.com/qualixar/superlocalmemory/stargazers">⭐ Star us on GitHub</a></p>
1877
1987
  </footer>
1878
1988
  </body>
1879
1989
  </html>
@@ -0,0 +1,83 @@
1
+ // SuperLocalMemory v3.4.1 — Graph Event Bus
2
+ // Copyright (c) 2026 Varun Pratap Bhardwaj — AGPL-3.0-or-later
3
+ // CustomEvent-based pub/sub for graph ↔ chat bidirectional linking.
4
+ // Renderer-agnostic — works regardless of which graph engine is active.
5
+
6
+ var SLMEventBus = (function() {
7
+ var _debounceTimers = {};
8
+
9
+ function publish(eventName, detail) {
10
+ window.dispatchEvent(new CustomEvent(eventName, {
11
+ detail: Object.freeze(detail || {}),
12
+ }));
13
+ }
14
+
15
+ function publishDebounced(eventName, detail, delayMs) {
16
+ if (_debounceTimers[eventName]) {
17
+ clearTimeout(_debounceTimers[eventName]);
18
+ }
19
+ _debounceTimers[eventName] = setTimeout(function() {
20
+ publish(eventName, detail);
21
+ _debounceTimers[eventName] = null;
22
+ }, delayMs || 200);
23
+ }
24
+
25
+ function subscribe(eventName, callback) {
26
+ window.addEventListener(eventName, function(e) {
27
+ callback(e.detail);
28
+ });
29
+ }
30
+
31
+ return { publish: publish, publishDebounced: publishDebounced, subscribe: subscribe };
32
+ })();
33
+
34
+ // Expose globally
35
+ window.SLMEventBus = SLMEventBus;
36
+
37
+ // ============================================================================
38
+ // EVENT DEFINITIONS
39
+ // ============================================================================
40
+ // slm:graph:nodeClicked — { factId, label } — graph node was clicked
41
+ // slm:graph:highlightNode — { factId } — highlight a node in graph
42
+ // slm:chat:citationClicked — { factId } — citation badge clicked in chat
43
+ // slm:chat:queryAbout — { query } — fill chat input and send
44
+
45
+ // ============================================================================
46
+ // WIRING: Graph → Chat
47
+ // ============================================================================
48
+
49
+ SLMEventBus.subscribe('slm:graph:nodeClicked', function(detail) {
50
+ // Double-click on graph node → fill chat with query about that node
51
+ // (Single click handled by openSigmaNodeDetail in knowledge-graph.js)
52
+ });
53
+
54
+ SLMEventBus.subscribe('slm:chat:queryAbout', function(detail) {
55
+ if (!detail || !detail.query) return;
56
+ var input = document.getElementById('chat-input');
57
+ if (input) {
58
+ input.value = detail.query;
59
+ // Switch to chat panel if on detail panel
60
+ if (typeof showChatPanel === 'function') showChatPanel();
61
+ // Auto-send
62
+ if (typeof sendChatFromInput === 'function') sendChatFromInput();
63
+ }
64
+ });
65
+
66
+ // ============================================================================
67
+ // WIRING: Chat → Graph
68
+ // ============================================================================
69
+
70
+ SLMEventBus.subscribe('slm:chat:citationClicked', function(detail) {
71
+ if (!detail || !detail.factId) return;
72
+ // Highlight the cited node in the graph
73
+ if (typeof sigmaHighlightNode === 'function') {
74
+ sigmaHighlightNode(detail.factId);
75
+ }
76
+ });
77
+
78
+ SLMEventBus.subscribe('slm:graph:highlightNode', function(detail) {
79
+ if (!detail || !detail.factId) return;
80
+ if (typeof sigmaHighlightNode === 'function') {
81
+ sigmaHighlightNode(detail.factId);
82
+ }
83
+ });
@@ -123,7 +123,7 @@ function setupGraphEventListeners() {
123
123
  graphTab.addEventListener('shown.bs.tab', function(event) {
124
124
  console.log('[Event] Knowledge Graph tab SHOWN (tab switch)');
125
125
 
126
- if (cy) {
126
+ if (cy || sigmaInstance) {
127
127
  // Graph already exists - user is returning to KG tab from another tab
128
128
  // Clear filter and reload to show full graph
129
129
  console.log('[Event] Returning to KG tab from another tab - clearing filter');