superlocalmemory 3.4.9 → 3.4.11
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/README.md +23 -3
- package/docs/cloud-backup.md +174 -0
- package/docs/skill-evolution.md +256 -0
- package/ide/hooks/tool-event-hook.sh +101 -11
- package/package.json +1 -1
- package/pyproject.toml +3 -2
- package/src/superlocalmemory/cli/commands.py +359 -0
- package/src/superlocalmemory/cli/ingest_cmd.py +81 -29
- package/src/superlocalmemory/cli/main.py +32 -0
- package/src/superlocalmemory/cli/setup_wizard.py +54 -11
- package/src/superlocalmemory/core/config.py +35 -0
- package/src/superlocalmemory/core/consolidation_engine.py +138 -0
- package/src/superlocalmemory/core/embedding_worker.py +1 -1
- package/src/superlocalmemory/core/engine.py +19 -0
- package/src/superlocalmemory/core/fact_consolidator.py +425 -0
- package/src/superlocalmemory/core/graph_pruner.py +290 -0
- package/src/superlocalmemory/core/maintenance_scheduler.py +44 -3
- package/src/superlocalmemory/core/recall_pipeline.py +9 -0
- package/src/superlocalmemory/core/tier_manager.py +325 -0
- package/src/superlocalmemory/encoding/entity_resolver.py +96 -28
- package/src/superlocalmemory/evolution/__init__.py +29 -0
- package/src/superlocalmemory/evolution/blind_verifier.py +115 -0
- package/src/superlocalmemory/evolution/evolution_store.py +302 -0
- package/src/superlocalmemory/evolution/mutation_generator.py +181 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +555 -0
- package/src/superlocalmemory/evolution/triggers.py +367 -0
- package/src/superlocalmemory/evolution/types.py +92 -0
- package/src/superlocalmemory/hooks/hook_handlers.py +13 -0
- package/src/superlocalmemory/infra/backup.py +63 -20
- package/src/superlocalmemory/infra/cloud_backup.py +703 -0
- package/src/superlocalmemory/learning/skill_performance_miner.py +422 -0
- package/src/superlocalmemory/mcp/server.py +4 -0
- package/src/superlocalmemory/mcp/tools_evolution.py +338 -0
- package/src/superlocalmemory/retrieval/engine.py +64 -4
- package/src/superlocalmemory/retrieval/forgetting_filter.py +22 -7
- package/src/superlocalmemory/retrieval/strategy.py +2 -2
- package/src/superlocalmemory/server/routes/backup.py +512 -8
- package/src/superlocalmemory/server/routes/behavioral.py +39 -17
- package/src/superlocalmemory/server/routes/evolution.py +213 -0
- package/src/superlocalmemory/server/routes/tiers.py +195 -0
- package/src/superlocalmemory/server/unified_daemon.py +36 -5
- package/src/superlocalmemory/storage/schema_v3410.py +159 -0
- package/src/superlocalmemory/storage/schema_v3411.py +149 -0
- package/src/superlocalmemory/ui/index.html +59 -3
- package/src/superlocalmemory/ui/js/core.js +3 -0
- package/src/superlocalmemory/ui/js/lifecycle.js +83 -0
- package/src/superlocalmemory/ui/js/ng-entities.js +27 -3
- package/src/superlocalmemory/ui/js/ng-shell.js +33 -0
- package/src/superlocalmemory/ui/js/ng-skills.js +611 -0
- package/src/superlocalmemory/ui/js/settings.js +311 -1
- package/src/superlocalmemory.egg-info/PKG-INFO +16 -1
- package/src/superlocalmemory.egg-info/SOURCES.txt +18 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
# Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
+
# Part of SuperLocalMemory V3 | https://qualixar.com | https://varunpratap.com
|
|
4
|
+
|
|
5
|
+
"""SuperLocalMemory V3.4.11 "Scale-Ready" — Schema Extensions.
|
|
6
|
+
|
|
7
|
+
New tables for tiered storage + future backend integrations:
|
|
8
|
+
- pinned_facts: User-pinned facts that stay in hot tier forever
|
|
9
|
+
- backend_status: Tracks initialization state of LanceDB/KùzuDB backends
|
|
10
|
+
- fact_consolidations: History of merged/consolidated facts
|
|
11
|
+
|
|
12
|
+
Design rules (inherited):
|
|
13
|
+
- CREATE IF NOT EXISTS for idempotency
|
|
14
|
+
- profile_id where applicable
|
|
15
|
+
- Never ALTER existing column types
|
|
16
|
+
|
|
17
|
+
Part of Qualixar | Author: Varun Pratap Bhardwaj
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import logging
|
|
23
|
+
import sqlite3
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# DDL — Pinned Facts (user override on lifecycle demotion)
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
_PINNED_FACTS_DDL = """
|
|
32
|
+
CREATE TABLE IF NOT EXISTS pinned_facts (
|
|
33
|
+
fact_id TEXT PRIMARY KEY,
|
|
34
|
+
profile_id TEXT DEFAULT 'default',
|
|
35
|
+
pinned_at TEXT NOT NULL,
|
|
36
|
+
reason TEXT DEFAULT ''
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX IF NOT EXISTS idx_pinned_facts_profile
|
|
40
|
+
ON pinned_facts(profile_id);
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
# DDL — Backend Status (LanceDB, KùzuDB, sqlite-vec tracking)
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
_BACKEND_STATUS_DDL = """
|
|
48
|
+
CREATE TABLE IF NOT EXISTS backend_status (
|
|
49
|
+
backend_name TEXT PRIMARY KEY,
|
|
50
|
+
status TEXT DEFAULT 'not_initialized',
|
|
51
|
+
record_count INTEGER DEFAULT 0,
|
|
52
|
+
last_sync_at TEXT,
|
|
53
|
+
error_message TEXT DEFAULT '',
|
|
54
|
+
config TEXT DEFAULT '{}'
|
|
55
|
+
);
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
# DDL — Fact Consolidations (merge history)
|
|
60
|
+
# ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
_FACT_CONSOLIDATIONS_DDL = """
|
|
63
|
+
CREATE TABLE IF NOT EXISTS fact_consolidations (
|
|
64
|
+
consolidation_id TEXT PRIMARY KEY,
|
|
65
|
+
profile_id TEXT DEFAULT 'default',
|
|
66
|
+
consolidated_fact_id TEXT NOT NULL,
|
|
67
|
+
source_fact_ids TEXT NOT NULL,
|
|
68
|
+
strategy TEXT DEFAULT 'entity_cluster',
|
|
69
|
+
created_at TEXT NOT NULL
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_fact_consolidations_profile
|
|
73
|
+
ON fact_consolidations(profile_id);
|
|
74
|
+
CREATE INDEX IF NOT EXISTS idx_fact_consolidations_target
|
|
75
|
+
ON fact_consolidations(consolidated_fact_id);
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
# DDL — Index on lifecycle for fast tier queries
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
_LIFECYCLE_INDEX_DDL = """
|
|
83
|
+
CREATE INDEX IF NOT EXISTS idx_atomic_facts_lifecycle
|
|
84
|
+
ON atomic_facts(lifecycle, profile_id);
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
# Migration runner
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
def apply_v3411_schema(db_path: str | sqlite3.Connection) -> dict:
|
|
93
|
+
"""Apply all v3.4.11 schema changes. Idempotent."""
|
|
94
|
+
result = {"applied": [], "errors": []}
|
|
95
|
+
|
|
96
|
+
if isinstance(db_path, sqlite3.Connection):
|
|
97
|
+
conn = db_path
|
|
98
|
+
own_connection = False
|
|
99
|
+
else:
|
|
100
|
+
conn = sqlite3.connect(str(db_path))
|
|
101
|
+
own_connection = True
|
|
102
|
+
|
|
103
|
+
try:
|
|
104
|
+
for name, ddl in [
|
|
105
|
+
("pinned_facts", _PINNED_FACTS_DDL),
|
|
106
|
+
("backend_status", _BACKEND_STATUS_DDL),
|
|
107
|
+
("fact_consolidations", _FACT_CONSOLIDATIONS_DDL),
|
|
108
|
+
("lifecycle_index", _LIFECYCLE_INDEX_DDL),
|
|
109
|
+
]:
|
|
110
|
+
try:
|
|
111
|
+
conn.executescript(ddl)
|
|
112
|
+
result["applied"].append(name)
|
|
113
|
+
except sqlite3.OperationalError as e:
|
|
114
|
+
result["errors"].append(f"{name}: {e}")
|
|
115
|
+
|
|
116
|
+
# Seed default backend entries
|
|
117
|
+
now = __import__("datetime").datetime.now().isoformat()
|
|
118
|
+
for backend in ("sqlite_vec", "sqlite_graph", "lancedb", "kuzu"):
|
|
119
|
+
try:
|
|
120
|
+
conn.execute(
|
|
121
|
+
"INSERT OR IGNORE INTO backend_status "
|
|
122
|
+
"(backend_name, status, config) VALUES (?, ?, ?)",
|
|
123
|
+
(backend, "not_initialized" if backend in ("lancedb", "kuzu") else "active", "{}"),
|
|
124
|
+
)
|
|
125
|
+
except sqlite3.OperationalError:
|
|
126
|
+
pass
|
|
127
|
+
|
|
128
|
+
# Mark version
|
|
129
|
+
try:
|
|
130
|
+
conn.execute(
|
|
131
|
+
"INSERT OR IGNORE INTO schema_version (version, applied_at) VALUES (?, ?)",
|
|
132
|
+
("3.4.11", now),
|
|
133
|
+
)
|
|
134
|
+
except sqlite3.OperationalError:
|
|
135
|
+
pass
|
|
136
|
+
|
|
137
|
+
conn.commit()
|
|
138
|
+
|
|
139
|
+
if result["applied"]:
|
|
140
|
+
logger.info("Schema v3.4.11 applied: %s", ", ".join(result["applied"]))
|
|
141
|
+
|
|
142
|
+
except Exception as e:
|
|
143
|
+
result["errors"].append(f"fatal: {e}")
|
|
144
|
+
logger.error("Schema v3.4.11 migration failed: %s", e)
|
|
145
|
+
finally:
|
|
146
|
+
if own_connection:
|
|
147
|
+
conn.close()
|
|
148
|
+
|
|
149
|
+
return result
|
|
@@ -43,6 +43,14 @@
|
|
|
43
43
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
/* Cloud account widget — sidebar (v3.4.10) */
|
|
47
|
+
#ng-account-widget:hover { background: rgba(255,255,255,0.06) !important; }
|
|
48
|
+
.account-dest-item { padding: 6px 0; display: flex; align-items: center; gap: 8px; }
|
|
49
|
+
.account-dest-badge { font-size: 10px; padding: 1px 6px; border-radius: 4px; }
|
|
50
|
+
.account-dest-badge.synced { background: rgba(0,212,170,0.15); color: #00D4AA; }
|
|
51
|
+
.account-dest-badge.failed { background: rgba(255,71,87,0.15); color: #ff4757; }
|
|
52
|
+
.account-dest-badge.never { background: rgba(255,255,255,0.06); color: #888; }
|
|
53
|
+
|
|
46
54
|
/* Stat cards with gradient backgrounds */
|
|
47
55
|
.stat-card {
|
|
48
56
|
border: none;
|
|
@@ -1511,10 +1519,12 @@
|
|
|
1511
1519
|
<h5 class="mb-0"><i class="bi bi-hourglass-split text-warning"></i> Memory Lifecycle</h5>
|
|
1512
1520
|
<div>
|
|
1513
1521
|
<span class="badge bg-secondary me-2" id="lifecycle-profile-badge">default</span>
|
|
1514
|
-
<button class="btn btn-sm btn-outline-
|
|
1522
|
+
<button class="btn btn-sm btn-outline-success" onclick="evaluateTiersNow()"><i class="bi bi-arrow-repeat"></i> Evaluate Tiers</button>
|
|
1523
|
+
<button class="btn btn-sm btn-outline-info ms-1" onclick="compactDryRun()"><i class="bi bi-funnel"></i> Preview Compaction</button>
|
|
1515
1524
|
<button class="btn btn-sm btn-outline-warning ms-1" onclick="compactExecute()"><i class="bi bi-lightning"></i> Compact Now</button>
|
|
1516
1525
|
</div>
|
|
1517
1526
|
</div>
|
|
1527
|
+
|
|
1518
1528
|
<!-- State Distribution -->
|
|
1519
1529
|
<div class="row g-3 mb-4" id="lifecycle-states-row">
|
|
1520
1530
|
<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>
|
|
@@ -1717,6 +1727,26 @@
|
|
|
1717
1727
|
<div id="entity-detail-panel" style="display:none;margin-top:24px"></div>
|
|
1718
1728
|
</div>
|
|
1719
1729
|
|
|
1730
|
+
<!-- Skill Evolution (v3.4.10 — Arsenal Evolution) -->
|
|
1731
|
+
<div class="tab-pane fade" id="skills-pane">
|
|
1732
|
+
<div class="ng-content-header">
|
|
1733
|
+
<div>
|
|
1734
|
+
<div class="ng-content-title"><i class="bi bi-lightning-charge"></i> Skill Evolution</div>
|
|
1735
|
+
<div class="ng-content-subtitle">Track skill performance, evolution history, and health status</div>
|
|
1736
|
+
</div>
|
|
1737
|
+
<button class="ng-btn" onclick="loadSkillEvolution()"><i class="bi bi-arrow-clockwise"></i> Refresh</button>
|
|
1738
|
+
</div>
|
|
1739
|
+
<div id="skills-overview-cards"></div>
|
|
1740
|
+
<div id="skills-list" style="margin-top:24px">
|
|
1741
|
+
<div class="text-center" style="padding:24px;color:var(--ng-text-tertiary)">
|
|
1742
|
+
<div class="spinner-border" style="color:var(--ng-accent)"></div>
|
|
1743
|
+
<div style="margin-top:8px">Loading skill performance data...</div>
|
|
1744
|
+
</div>
|
|
1745
|
+
</div>
|
|
1746
|
+
<div id="skill-lineage-container" class="mt-3"></div>
|
|
1747
|
+
<div id="skill-detail-panel" style="display:none;margin-top:24px"></div>
|
|
1748
|
+
</div>
|
|
1749
|
+
|
|
1720
1750
|
<!-- Mesh Peers (v3.4.3 — Neural Glass) -->
|
|
1721
1751
|
<div class="tab-pane fade" id="mesh-pane">
|
|
1722
1752
|
<div class="ng-content-header">
|
|
@@ -1974,6 +2004,31 @@
|
|
|
1974
2004
|
</div>
|
|
1975
2005
|
</div>
|
|
1976
2006
|
</div>
|
|
2007
|
+
<!-- Cloud Backup (v3.4.10 "Fortress") -->
|
|
2008
|
+
<div class="card p-3 mb-3">
|
|
2009
|
+
<h5 class="mb-3"><i class="bi bi-cloud-arrow-up"></i> Cloud Backup</h5>
|
|
2010
|
+
<p class="text-muted small mb-2">
|
|
2011
|
+
Sync your memory backups to Google Drive or GitHub. Credentials are stored securely in your OS keychain.
|
|
2012
|
+
</p>
|
|
2013
|
+
<div id="cloud-destinations" class="mb-3">
|
|
2014
|
+
<div class="text-muted small">Loading cloud destinations...</div>
|
|
2015
|
+
</div>
|
|
2016
|
+
<div class="d-flex gap-2 flex-wrap">
|
|
2017
|
+
<button class="btn btn-outline-primary btn-sm" onclick="connectGoogleDrive()">
|
|
2018
|
+
<i class="bi bi-google"></i> Connect Google Drive
|
|
2019
|
+
</button>
|
|
2020
|
+
<button class="btn btn-outline-dark btn-sm" onclick="connectGitHub()">
|
|
2021
|
+
<i class="bi bi-github"></i> Connect GitHub
|
|
2022
|
+
</button>
|
|
2023
|
+
<button class="btn btn-outline-success btn-sm" onclick="syncCloudNow()">
|
|
2024
|
+
<i class="bi bi-cloud-upload"></i> Sync Now
|
|
2025
|
+
</button>
|
|
2026
|
+
<button class="btn btn-outline-info btn-sm" onclick="exportBackup()">
|
|
2027
|
+
<i class="bi bi-file-earmark-zip"></i> Export Backup
|
|
2028
|
+
</button>
|
|
2029
|
+
</div>
|
|
2030
|
+
</div>
|
|
2031
|
+
|
|
1977
2032
|
<!-- Learning Data Management (v2.7.4) -->
|
|
1978
2033
|
<div class="card p-3 mb-3">
|
|
1979
2034
|
<h5 class="mb-3"><i class="bi bi-brain"></i> Learning Data</h5>
|
|
@@ -2097,9 +2152,10 @@
|
|
|
2097
2152
|
<!-- Neural Glass v3.4.4 — Dashboard V2 -->
|
|
2098
2153
|
<script src="static/js/ng-health.js?v=345"></script>
|
|
2099
2154
|
<script src="static/js/ng-ingestion.js?v=345"></script>
|
|
2100
|
-
<script src="static/js/ng-entities.js?v=
|
|
2155
|
+
<script src="static/js/ng-entities.js?v=3410"></script>
|
|
2156
|
+
<script src="static/js/ng-skills.js?v=3411"></script>
|
|
2101
2157
|
<script src="static/js/ng-mesh.js?v=345"></script>
|
|
2102
|
-
<script src="static/js/ng-shell.js?v=
|
|
2158
|
+
<script src="static/js/ng-shell.js?v=3410"></script>
|
|
2103
2159
|
|
|
2104
2160
|
<footer style="text-align:center; padding:24px; border-top:1px solid var(--bs-border-color); font-size:0.8125rem;">
|
|
2105
2161
|
<div style="display:flex; align-items:center; justify-content:center; gap:8px; margin-bottom:8px;">
|
|
@@ -243,4 +243,7 @@ window.addEventListener('DOMContentLoaded', function() {
|
|
|
243
243
|
if (typeof initEventStream === 'function') initEventStream();
|
|
244
244
|
if (typeof loadEventStats === 'function') loadEventStats();
|
|
245
245
|
if (typeof loadAgents === 'function') loadAgents();
|
|
246
|
+
|
|
247
|
+
// v3.4.10 — Account widget (cloud backup status in header)
|
|
248
|
+
if (typeof loadCloudDestinations === 'function') loadCloudDestinations();
|
|
246
249
|
});
|
|
@@ -7,10 +7,27 @@ var _lifecycleData = null;
|
|
|
7
7
|
|
|
8
8
|
async function loadLifecycle() {
|
|
9
9
|
try {
|
|
10
|
+
// V3.4.11: Load tier stats from the authoritative source (atomic_facts.lifecycle)
|
|
11
|
+
// and merge into the existing lifecycle view for a unified display.
|
|
12
|
+
var tierResponse = await fetch('/api/tiers/stats');
|
|
13
|
+
var tierData = await tierResponse.json();
|
|
14
|
+
|
|
10
15
|
var response = await fetch('/api/lifecycle/status');
|
|
11
16
|
var data = await response.json();
|
|
12
17
|
_lifecycleData = data;
|
|
13
18
|
|
|
19
|
+
// Override state counts with tier stats (atomic_facts is the truth)
|
|
20
|
+
if (tierData && tierData.total > 0) {
|
|
21
|
+
data.states = data.states || {};
|
|
22
|
+
data.states.active = tierData.active;
|
|
23
|
+
data.states.warm = tierData.warm;
|
|
24
|
+
data.states.cold = tierData.cold;
|
|
25
|
+
data.states.archived = tierData.archived;
|
|
26
|
+
data.total_memories = tierData.total;
|
|
27
|
+
data._pinned = tierData.pinned;
|
|
28
|
+
data.available = true;
|
|
29
|
+
}
|
|
30
|
+
|
|
14
31
|
if (!data.available) {
|
|
15
32
|
showEmpty('lifecycle-states-row', 'hourglass-split', 'Lifecycle engine not available. Upgrade to v2.8.');
|
|
16
33
|
return;
|
|
@@ -21,6 +38,19 @@ async function loadLifecycle() {
|
|
|
21
38
|
renderLifecycleAgeStats(data);
|
|
22
39
|
renderLifecycleTransitions(data);
|
|
23
40
|
|
|
41
|
+
// V3.4.11: Show pinned count below the state cards
|
|
42
|
+
var pinnedInfo = document.getElementById('lifecycle-pinned-info');
|
|
43
|
+
if (!pinnedInfo) {
|
|
44
|
+
pinnedInfo = document.createElement('div');
|
|
45
|
+
pinnedInfo.id = 'lifecycle-pinned-info';
|
|
46
|
+
pinnedInfo.className = 'small text-muted mb-3';
|
|
47
|
+
var statesRow = document.getElementById('lifecycle-states-row');
|
|
48
|
+
if (statesRow) statesRow.parentNode.insertBefore(pinnedInfo, statesRow.nextSibling);
|
|
49
|
+
}
|
|
50
|
+
var pinCount = data._pinned || 0;
|
|
51
|
+
pinnedInfo.innerHTML = '<i class="bi bi-pin-angle"></i> ' + pinCount + ' pinned facts (protected from demotion). ' +
|
|
52
|
+
'<span class="text-muted" style="font-size:11px;">Active = full weight | Warm = 0.7x | Cold = 0.3x | Archived = deep recall only</span>';
|
|
53
|
+
|
|
24
54
|
var badge = document.getElementById('lifecycle-profile-badge');
|
|
25
55
|
if (badge) badge.textContent = data.active_profile || 'default';
|
|
26
56
|
} catch (error) {
|
|
@@ -296,3 +326,56 @@ async function compactExecute() {
|
|
|
296
326
|
console.error('Compaction error:', e);
|
|
297
327
|
}
|
|
298
328
|
}
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
// ---- V3.4.11: Tier Management (pinning + evaluation) ----
|
|
332
|
+
|
|
333
|
+
async function evaluateTiersNow() {
|
|
334
|
+
showToast('Evaluating tiers...');
|
|
335
|
+
try {
|
|
336
|
+
var response = await fetch('/api/tiers/evaluate', { method: 'POST' });
|
|
337
|
+
var data = await response.json();
|
|
338
|
+
if (data.success) {
|
|
339
|
+
var s = data.stats;
|
|
340
|
+
var demoted = (s.demoted_to_warm || 0) + (s.demoted_to_cold || 0) + (s.demoted_to_archive || 0);
|
|
341
|
+
showToast('Tier evaluation: ' + demoted + ' facts demoted, ' + s.pinned_protected + ' pinned protected');
|
|
342
|
+
loadTierStats();
|
|
343
|
+
loadLifecycle();
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
showToast('Tier evaluation failed');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function pinFact(factId, reason) {
|
|
351
|
+
try {
|
|
352
|
+
var response = await fetch('/api/tiers/pin', {
|
|
353
|
+
method: 'POST',
|
|
354
|
+
headers: { 'Content-Type': 'application/json' },
|
|
355
|
+
body: JSON.stringify({ fact_id: factId, reason: reason || '' })
|
|
356
|
+
});
|
|
357
|
+
var data = await response.json();
|
|
358
|
+
if (data.success) {
|
|
359
|
+
showToast('Fact pinned');
|
|
360
|
+
loadTierStats();
|
|
361
|
+
}
|
|
362
|
+
} catch (error) {
|
|
363
|
+
showToast('Failed to pin fact');
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async function unpinFact(factId) {
|
|
368
|
+
try {
|
|
369
|
+
var response = await fetch('/api/tiers/unpin', {
|
|
370
|
+
method: 'POST',
|
|
371
|
+
headers: { 'Content-Type': 'application/json' },
|
|
372
|
+
body: JSON.stringify({ fact_id: factId })
|
|
373
|
+
});
|
|
374
|
+
if (response.ok) {
|
|
375
|
+
showToast('Fact unpinned');
|
|
376
|
+
loadTierStats();
|
|
377
|
+
}
|
|
378
|
+
} catch (error) {
|
|
379
|
+
showToast('Failed to unpin fact');
|
|
380
|
+
}
|
|
381
|
+
}
|
|
@@ -85,14 +85,23 @@
|
|
|
85
85
|
var typeColor = getTypeColor(entity.type);
|
|
86
86
|
var summaryText = entity.summary_preview || 'No summary yet — click to view details';
|
|
87
87
|
var hasTruth = entity.has_compiled_truth;
|
|
88
|
+
var typeIcon = getTypeIcon(entity.type);
|
|
89
|
+
|
|
90
|
+
// Skill entities get a distinctive accent border
|
|
91
|
+
var borderAccent = entity.type === 'skill'
|
|
92
|
+
? 'border-left:3px solid ' + typeColor + ';'
|
|
93
|
+
: '';
|
|
88
94
|
|
|
89
95
|
return '<div class="col-md-6 col-lg-4">' +
|
|
90
|
-
'<div class="ng-glass" style="padding:16px;cursor:pointer;transition:border-color 0.2s" ' +
|
|
96
|
+
'<div class="ng-glass" style="padding:16px;cursor:pointer;transition:border-color 0.2s;' + borderAccent + '" ' +
|
|
91
97
|
'onclick="showEntityDetail(\'' + escapeAttr(entity.name) + '\')" ' +
|
|
92
98
|
'onmouseover="this.style.borderColor=\'var(--ng-border-prominent)\'" ' +
|
|
93
99
|
'onmouseout="this.style.borderColor=\'var(--ng-border-subtle)\'">' +
|
|
94
100
|
'<div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px">' +
|
|
95
|
-
'<div style="font-weight:590;font-size:0.9375rem;color:var(--ng-text-primary)">' +
|
|
101
|
+
'<div style="font-weight:590;font-size:0.9375rem;color:var(--ng-text-primary)">' +
|
|
102
|
+
'<i class="bi ' + typeIcon + '" style="color:' + typeColor + ';margin-right:4px"></i>' +
|
|
103
|
+
escapeHtml(entity.name) +
|
|
104
|
+
'</div>' +
|
|
96
105
|
'<span class="ng-badge ng-badge-accent">' + escapeHtml(entity.type) + '</span>' +
|
|
97
106
|
'</div>' +
|
|
98
107
|
'<div style="font-size:0.8125rem;color:var(--ng-text-secondary);margin-bottom:8px;' +
|
|
@@ -107,6 +116,18 @@
|
|
|
107
116
|
'</div>';
|
|
108
117
|
}
|
|
109
118
|
|
|
119
|
+
function getTypeIcon(type) {
|
|
120
|
+
var icons = {
|
|
121
|
+
person: 'bi-person',
|
|
122
|
+
concept: 'bi-lightbulb',
|
|
123
|
+
organization: 'bi-building',
|
|
124
|
+
place: 'bi-geo-alt',
|
|
125
|
+
event: 'bi-calendar-event',
|
|
126
|
+
skill: 'bi-lightning-charge'
|
|
127
|
+
};
|
|
128
|
+
return icons[type] || 'bi-circle';
|
|
129
|
+
}
|
|
130
|
+
|
|
110
131
|
// Show entity detail panel
|
|
111
132
|
window.showEntityDetail = function(entityName) {
|
|
112
133
|
var panel = document.getElementById('entity-detail-panel');
|
|
@@ -253,7 +274,10 @@
|
|
|
253
274
|
}
|
|
254
275
|
|
|
255
276
|
function getTypeColor(type) {
|
|
256
|
-
var colors = {
|
|
277
|
+
var colors = {
|
|
278
|
+
person: '#3b82f6', concept: '#10b981', organization: '#f59e0b',
|
|
279
|
+
location: '#ef4444', skill: '#8b5cf6', event: '#ec4899'
|
|
280
|
+
};
|
|
257
281
|
return colors[type] || '#7C6AEF';
|
|
258
282
|
}
|
|
259
283
|
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
{ id: 'health-pane', icon: 'bi-heart-pulse', text: 'Health Monitor', badge: 'NEW' },
|
|
45
45
|
{ id: 'ingestion-pane', icon: 'bi-cloud-download', text: 'Ingestion', badge: 'NEW' },
|
|
46
46
|
{ id: 'entities-pane', icon: 'bi-person-badge', text: 'Entity Explorer', badge: 'NEW' },
|
|
47
|
+
{ id: 'skills-pane', icon: 'bi-lightning-charge', text: 'Skill Evolution', badge: 'NEW' },
|
|
47
48
|
{ id: 'mesh-pane', icon: 'bi-share', text: 'Mesh Peers', badge: 'NEW' }
|
|
48
49
|
]
|
|
49
50
|
},
|
|
@@ -134,6 +135,35 @@
|
|
|
134
135
|
var addProfileBtn = document.getElementById('add-profile-btn');
|
|
135
136
|
|
|
136
137
|
footer.innerHTML =
|
|
138
|
+
// v3.4.10: Cloud Backup Account Widget
|
|
139
|
+
'<div id="ng-account-widget" style="margin-bottom:10px;padding:8px;border-radius:10px;background:rgba(255,255,255,0.03);border:1px solid rgba(255,255,255,0.06);cursor:pointer;" onclick="document.querySelector(\'[data-target=settings]\')?.click()">' +
|
|
140
|
+
'<div style="display:flex;align-items:center;gap:8px;">' +
|
|
141
|
+
'<span id="ng-account-avatar" style="width:28px;height:28px;border-radius:50%;background:rgba(255,255,255,0.08);display:flex;align-items:center;justify-content:center;font-size:13px;flex-shrink:0;overflow:hidden;">' +
|
|
142
|
+
'<i class="bi bi-cloud-slash" style="font-size:13px;opacity:0.4;"></i>' +
|
|
143
|
+
'</span>' +
|
|
144
|
+
'<div style="flex:1;min-width:0;">' +
|
|
145
|
+
'<div id="ng-account-name" style="font-size:12px;color:#e0e0e0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">Not connected</div>' +
|
|
146
|
+
'<div id="ng-account-status" style="font-size:10px;color:#666;">No cloud backup</div>' +
|
|
147
|
+
'</div>' +
|
|
148
|
+
'<span id="ng-account-dot" style="width:7px;height:7px;border-radius:50%;background:#444;flex-shrink:0;" title="No cloud backup"></span>' +
|
|
149
|
+
'</div>' +
|
|
150
|
+
'<div id="ng-account-actions" style="display:none;margin-top:8px;padding-top:8px;border-top:1px solid rgba(255,255,255,0.05);">' +
|
|
151
|
+
'<div style="display:flex;gap:4px;">' +
|
|
152
|
+
'<button class="ng-btn" onclick="event.stopPropagation();connectGoogleDrive()" title="Connect Google Drive" style="flex:1;justify-content:center;font-size:11px;padding:4px;">' +
|
|
153
|
+
'<i class="bi bi-google" style="color:#4285f4;"></i>' +
|
|
154
|
+
'</button>' +
|
|
155
|
+
'<button class="ng-btn" onclick="event.stopPropagation();connectGitHub()" title="Connect GitHub" style="flex:1;justify-content:center;font-size:11px;padding:4px;">' +
|
|
156
|
+
'<i class="bi bi-github"></i>' +
|
|
157
|
+
'</button>' +
|
|
158
|
+
'<button class="ng-btn" onclick="event.stopPropagation();syncCloudNow()" title="Sync Now" style="flex:1;justify-content:center;font-size:11px;padding:4px;">' +
|
|
159
|
+
'<i class="bi bi-cloud-upload" style="color:#00D4AA;"></i>' +
|
|
160
|
+
'</button>' +
|
|
161
|
+
'<button class="ng-btn" onclick="event.stopPropagation();exportBackup()" title="Export Backup" style="flex:1;justify-content:center;font-size:11px;padding:4px;">' +
|
|
162
|
+
'<i class="bi bi-download" style="color:#f39c12;"></i>' +
|
|
163
|
+
'</button>' +
|
|
164
|
+
'</div>' +
|
|
165
|
+
'</div>' +
|
|
166
|
+
'</div>' +
|
|
137
167
|
'<div style="margin-bottom:8px">' +
|
|
138
168
|
'<div class="ng-sidebar-section-label" style="padding:0 0 4px">Profile</div>' +
|
|
139
169
|
'<div style="display:flex;gap:4px;align-items:center" id="ng-profile-container"></div>' +
|
|
@@ -359,6 +389,9 @@
|
|
|
359
389
|
case 'entities-pane':
|
|
360
390
|
if (typeof loadEntityExplorer === 'function') loadEntityExplorer();
|
|
361
391
|
break;
|
|
392
|
+
case 'skills-pane':
|
|
393
|
+
if (typeof loadSkillEvolution === 'function') loadSkillEvolution();
|
|
394
|
+
break;
|
|
362
395
|
case 'mesh-pane':
|
|
363
396
|
if (typeof loadMeshPeers === 'function') loadMeshPeers();
|
|
364
397
|
break;
|