superlocalmemory 3.4.19 → 3.4.21
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/CHANGELOG.md +24 -0
- package/README.md +42 -34
- package/bin/slm +11 -0
- package/bin/slm.bat +12 -0
- package/package.json +4 -3
- package/pyproject.toml +3 -2
- package/scripts/build-slm-hook.ps1 +40 -0
- package/scripts/build-slm-hook.sh +45 -0
- package/scripts/build_entry.py +452 -0
- package/scripts/ci/stage5b_gate.sh +50 -0
- package/scripts/postinstall/validation.js +187 -0
- package/scripts/postinstall-interactive.js +756 -0
- package/scripts/postinstall_binary.js +287 -0
- package/scripts/release_manifest.py +273 -0
- package/scripts/slm-hook.spec +56 -0
- package/skills/slm-build-graph/SKILL.md +423 -0
- package/skills/slm-list-recent/SKILL.md +348 -0
- package/skills/slm-recall/SKILL.md +343 -0
- package/skills/slm-remember/SKILL.md +194 -0
- package/skills/slm-show-patterns/SKILL.md +224 -0
- package/skills/slm-status/SKILL.md +363 -0
- package/skills/slm-switch-profile/SKILL.md +442 -0
- package/src/superlocalmemory/cli/commands.py +219 -79
- package/src/superlocalmemory/cli/context_commands.py +192 -0
- package/src/superlocalmemory/cli/daemon.py +15 -1
- package/src/superlocalmemory/cli/db_migrate.py +80 -0
- package/src/superlocalmemory/cli/escape_hatch.py +220 -0
- package/src/superlocalmemory/cli/main.py +72 -1
- package/src/superlocalmemory/core/context_cache.py +397 -0
- package/src/superlocalmemory/core/engine.py +38 -2
- package/src/superlocalmemory/core/engine_wiring.py +1 -1
- package/src/superlocalmemory/core/ram_lock.py +111 -0
- package/src/superlocalmemory/core/recall_pipeline.py +433 -3
- package/src/superlocalmemory/core/recall_worker.py +8 -3
- package/src/superlocalmemory/core/security_primitives.py +635 -0
- package/src/superlocalmemory/core/shadow_router.py +319 -0
- package/src/superlocalmemory/core/slm_disabled.py +87 -0
- package/src/superlocalmemory/core/slmignore.py +125 -0
- package/src/superlocalmemory/core/topic_signature.py +143 -0
- package/src/superlocalmemory/core/worker_pool.py +14 -3
- package/src/superlocalmemory/encoding/cognitive_consolidator.py +2 -2
- package/src/superlocalmemory/evolution/budget.py +321 -0
- package/src/superlocalmemory/evolution/llm_dispatch.py +508 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +144 -94
- package/src/superlocalmemory/hooks/_outcome_common.py +506 -0
- package/src/superlocalmemory/hooks/adapter_base.py +317 -0
- package/src/superlocalmemory/hooks/antigravity_adapter.py +192 -0
- package/src/superlocalmemory/hooks/claude_code_hooks.py +33 -1
- package/src/superlocalmemory/hooks/context_payload.py +312 -0
- package/src/superlocalmemory/hooks/copilot_adapter.py +154 -0
- package/src/superlocalmemory/hooks/cross_platform_connector.py +90 -0
- package/src/superlocalmemory/hooks/cursor_adapter.py +195 -0
- package/src/superlocalmemory/hooks/hook_handlers.py +109 -8
- package/src/superlocalmemory/hooks/ide_connector.py +25 -2
- package/src/superlocalmemory/hooks/post_tool_async_hook.py +165 -0
- package/src/superlocalmemory/hooks/post_tool_outcome_hook.py +223 -0
- package/src/superlocalmemory/hooks/prewarm_auth.py +170 -0
- package/src/superlocalmemory/hooks/session_registry.py +186 -0
- package/src/superlocalmemory/hooks/stop_outcome_hook.py +134 -0
- package/src/superlocalmemory/hooks/sync_loop.py +114 -0
- package/src/superlocalmemory/hooks/user_prompt_hook.py +128 -0
- package/src/superlocalmemory/hooks/user_prompt_rehash_hook.py +202 -0
- package/src/superlocalmemory/infra/backup.py +3 -3
- package/src/superlocalmemory/infra/cloud_backup.py +2 -2
- package/src/superlocalmemory/infra/event_bus.py +2 -2
- package/src/superlocalmemory/infra/webhook_dispatcher.py +3 -3
- package/src/superlocalmemory/learning/arm_catalog.py +99 -0
- package/src/superlocalmemory/learning/bandit.py +526 -0
- package/src/superlocalmemory/learning/bandit_cache.py +133 -0
- package/src/superlocalmemory/learning/behavioral.py +53 -1
- package/src/superlocalmemory/learning/consolidation_cycle.py +381 -0
- package/src/superlocalmemory/learning/consolidation_worker.py +188 -520
- package/src/superlocalmemory/learning/database.py +256 -0
- package/src/superlocalmemory/learning/dedup_hnsw.py +413 -0
- package/src/superlocalmemory/learning/ensemble.py +300 -0
- package/src/superlocalmemory/learning/fact_outcome_joins.py +207 -0
- package/src/superlocalmemory/learning/forgetting_scheduler.py +55 -0
- package/src/superlocalmemory/learning/hnsw_dedup.py +69 -0
- package/src/superlocalmemory/learning/labeler.py +87 -0
- package/src/superlocalmemory/learning/legacy_migration.py +277 -0
- package/src/superlocalmemory/learning/memory_merge.py +160 -0
- package/src/superlocalmemory/learning/model_cache.py +269 -0
- package/src/superlocalmemory/learning/model_rollback.py +278 -0
- package/src/superlocalmemory/learning/outcome_queue.py +284 -0
- package/src/superlocalmemory/learning/pattern_miner.py +415 -0
- package/src/superlocalmemory/learning/pattern_miner_constants.py +47 -0
- package/src/superlocalmemory/learning/ranker.py +225 -81
- package/src/superlocalmemory/learning/ranker_common.py +163 -0
- package/src/superlocalmemory/learning/ranker_retrain_legacy.py +202 -0
- package/src/superlocalmemory/learning/ranker_retrain_online.py +411 -0
- package/src/superlocalmemory/learning/reward.py +777 -0
- package/src/superlocalmemory/learning/reward_archive.py +210 -0
- package/src/superlocalmemory/learning/reward_boost.py +201 -0
- package/src/superlocalmemory/learning/reward_proxy.py +326 -0
- package/src/superlocalmemory/learning/shadow_test.py +524 -0
- package/src/superlocalmemory/learning/signal_worker.py +270 -0
- package/src/superlocalmemory/learning/signals.py +314 -0
- package/src/superlocalmemory/learning/trigram_index.py +547 -0
- package/src/superlocalmemory/mcp/server.py +5 -5
- package/src/superlocalmemory/mcp/tools_context.py +183 -0
- package/src/superlocalmemory/mcp/tools_core.py +92 -27
- package/src/superlocalmemory/parameterization/soft_prompt_generator.py +13 -0
- package/src/superlocalmemory/retrieval/engine.py +52 -0
- package/src/superlocalmemory/server/api.py +2 -2
- package/src/superlocalmemory/server/bandit_loops.py +140 -0
- package/src/superlocalmemory/server/middleware/__init__.py +11 -0
- package/src/superlocalmemory/server/middleware/security_headers.py +144 -0
- package/src/superlocalmemory/server/routes/backup.py +36 -13
- package/src/superlocalmemory/server/routes/behavioral.py +50 -19
- package/src/superlocalmemory/server/routes/brain.py +1234 -0
- package/src/superlocalmemory/server/routes/data_io.py +4 -4
- package/src/superlocalmemory/server/routes/events.py +2 -2
- package/src/superlocalmemory/server/routes/helpers.py +1 -1
- package/src/superlocalmemory/server/routes/learning.py +192 -7
- package/src/superlocalmemory/server/routes/memories.py +189 -1
- package/src/superlocalmemory/server/routes/prewarm.py +171 -0
- package/src/superlocalmemory/server/routes/profiles.py +3 -3
- package/src/superlocalmemory/server/routes/token.py +88 -0
- package/src/superlocalmemory/server/routes/ws.py +5 -5
- package/src/superlocalmemory/server/security_middleware.py +13 -7
- package/src/superlocalmemory/server/ui.py +2 -2
- package/src/superlocalmemory/server/unified_daemon.py +335 -3
- package/src/superlocalmemory/storage/migration_runner.py +545 -0
- package/src/superlocalmemory/storage/migrations/M001_add_signal_features_columns.py +67 -0
- package/src/superlocalmemory/storage/migrations/M002_model_state_history.py +132 -0
- package/src/superlocalmemory/storage/migrations/M003_migration_log.py +38 -0
- package/src/superlocalmemory/storage/migrations/M004_cross_platform_sync_log.py +46 -0
- package/src/superlocalmemory/storage/migrations/M005_bandit_tables.py +75 -0
- package/src/superlocalmemory/storage/migrations/M006_action_outcomes_reward.py +75 -0
- package/src/superlocalmemory/storage/migrations/M007_pending_outcomes.py +63 -0
- package/src/superlocalmemory/storage/migrations/M009_model_lineage.py +54 -0
- package/src/superlocalmemory/storage/migrations/M010_evolution_config.py +75 -0
- package/src/superlocalmemory/storage/migrations/M011_archive_and_merge.py +87 -0
- package/src/superlocalmemory/storage/migrations/M012_shadow_observations.py +72 -0
- package/src/superlocalmemory/storage/migrations/M013_bi_temporal_columns.py +55 -0
- package/src/superlocalmemory/storage/migrations/__init__.py +81 -0
- package/src/superlocalmemory/storage/models.py +4 -0
- package/src/superlocalmemory/ui/css/brain.css +409 -0
- package/src/superlocalmemory/ui/css/legacy-dashboard.css +645 -0
- package/src/superlocalmemory/ui/index.html +459 -1345
- package/src/superlocalmemory/ui/js/brain.js +1321 -0
- package/src/superlocalmemory/ui/js/clusters.js +123 -4
- package/src/superlocalmemory/ui/js/init.js +48 -39
- package/src/superlocalmemory/ui/js/memories.js +88 -2
- package/src/superlocalmemory/ui/js/modal.js +71 -1
- package/src/superlocalmemory/ui/js/ng-shell.js +101 -88
- package/src/superlocalmemory/ui/js/trust-dashboard.js +168 -25
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/bootstrap-icons.css +2018 -0
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff +0 -0
- package/src/superlocalmemory/ui/vendor/bootstrap-icons/fonts/bootstrap-icons.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/bootstrap.bundle.min.js +7 -0
- package/src/superlocalmemory/ui/vendor/bootstrap.min.css +6 -0
- package/src/superlocalmemory/ui/vendor/d3.v7.min.js +2 -0
- package/src/superlocalmemory/ui/vendor/graphology-library.min.js +2 -0
- package/src/superlocalmemory/ui/vendor/graphology.umd.min.js +2 -0
- package/src/superlocalmemory/ui/vendor/inter-ui/inter-variable.min.css +8 -0
- package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable-Italic.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/inter-ui/variable/InterVariable.woff2 +0 -0
- package/src/superlocalmemory/ui/vendor/sigma.min.js +1 -0
- package/src/superlocalmemory/ui/js/behavioral.js +0 -447
- package/src/superlocalmemory/ui/js/graph-core.js +0 -447
- package/src/superlocalmemory/ui/js/graph-interactions.js +0 -351
- package/src/superlocalmemory/ui/js/learning.js +0 -435
- package/src/superlocalmemory/ui/js/patterns.js +0 -93
- package/src/superlocalmemory.egg-info/PKG-INFO +0 -647
- package/src/superlocalmemory.egg-info/SOURCES.txt +0 -335
- package/src/superlocalmemory.egg-info/dependency_links.txt +0 -1
- package/src/superlocalmemory.egg-info/entry_points.txt +0 -2
- package/src/superlocalmemory.egg-info/requires.txt +0 -58
- package/src/superlocalmemory.egg-info/top_level.txt +0 -1
|
@@ -1,22 +1,141 @@
|
|
|
1
1
|
// SuperLocalMemory V3 - Clusters View
|
|
2
2
|
// Part of Qualixar | https://superlocalmemory.com
|
|
3
|
+
//
|
|
4
|
+
// v3.4.21: client-side pagination + sort. /api/clusters returns all
|
|
5
|
+
// clusters in one shot (677 on live data) — rendering them all gave
|
|
6
|
+
// an unlimited scroll. State kept on ``_CLUSTERS_STATE`` so sort /
|
|
7
|
+
// page changes don't re-fetch.
|
|
8
|
+
|
|
9
|
+
var _CLUSTERS_STATE = {
|
|
10
|
+
clusters: [],
|
|
11
|
+
page: 0,
|
|
12
|
+
pageSize: 25,
|
|
13
|
+
sort: 'members-desc',
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function _sortClusters(list) {
|
|
17
|
+
var arr = list.slice();
|
|
18
|
+
switch (_CLUSTERS_STATE.sort) {
|
|
19
|
+
case 'members-asc':
|
|
20
|
+
arr.sort(function (a, b) { return (a.member_count || 0) - (b.member_count || 0); });
|
|
21
|
+
break;
|
|
22
|
+
case 'recent-desc':
|
|
23
|
+
arr.sort(function (a, b) {
|
|
24
|
+
return String(b.first_memory || '').localeCompare(String(a.first_memory || ''));
|
|
25
|
+
});
|
|
26
|
+
break;
|
|
27
|
+
case 'members-desc':
|
|
28
|
+
default:
|
|
29
|
+
arr.sort(function (a, b) { return (b.member_count || 0) - (a.member_count || 0); });
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
return arr;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function _renderClustersPage() {
|
|
36
|
+
var container = document.getElementById('clusters-list');
|
|
37
|
+
if (!container) return;
|
|
38
|
+
|
|
39
|
+
var all = _sortClusters(_CLUSTERS_STATE.clusters);
|
|
40
|
+
var total = all.length;
|
|
41
|
+
var pageSize = Math.max(1, _CLUSTERS_STATE.pageSize);
|
|
42
|
+
var maxPage = Math.max(0, Math.ceil(total / pageSize) - 1);
|
|
43
|
+
if (_CLUSTERS_STATE.page > maxPage) _CLUSTERS_STATE.page = maxPage;
|
|
44
|
+
if (_CLUSTERS_STATE.page < 0) _CLUSTERS_STATE.page = 0;
|
|
45
|
+
var start = _CLUSTERS_STATE.page * pageSize;
|
|
46
|
+
var end = Math.min(total, start + pageSize);
|
|
47
|
+
|
|
48
|
+
var info = document.getElementById('clusters-page-info');
|
|
49
|
+
if (info) {
|
|
50
|
+
info.textContent = total === 0
|
|
51
|
+
? 'No rows'
|
|
52
|
+
: ((start + 1) + '\u2013' + end + ' of ' + total.toLocaleString());
|
|
53
|
+
}
|
|
54
|
+
var detail = document.getElementById('clusters-page-detail');
|
|
55
|
+
if (detail) {
|
|
56
|
+
detail.textContent = 'Page ' + (_CLUSTERS_STATE.page + 1) + ' of ' + Math.max(1, maxPage + 1);
|
|
57
|
+
}
|
|
58
|
+
var prev = document.getElementById('clusters-prev-btn');
|
|
59
|
+
var next = document.getElementById('clusters-next-btn');
|
|
60
|
+
if (prev) prev.disabled = _CLUSTERS_STATE.page <= 0;
|
|
61
|
+
if (next) next.disabled = _CLUSTERS_STATE.page >= maxPage;
|
|
62
|
+
|
|
63
|
+
if (total === 0) {
|
|
64
|
+
if (typeof showEmpty === 'function') {
|
|
65
|
+
showEmpty('clusters-list', 'collection',
|
|
66
|
+
'No clusters found yet. Clusters form automatically as you store related memories.');
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
renderClusters(all.slice(start, end));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function _wireClustersControls() {
|
|
75
|
+
var sort = document.getElementById('clusters-sort');
|
|
76
|
+
if (sort && !sort._wired) {
|
|
77
|
+
sort._wired = true;
|
|
78
|
+
sort.addEventListener('change', function () {
|
|
79
|
+
_CLUSTERS_STATE.sort = sort.value;
|
|
80
|
+
_CLUSTERS_STATE.page = 0;
|
|
81
|
+
_renderClustersPage();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
var pageSize = document.getElementById('clusters-page-size');
|
|
85
|
+
if (pageSize && !pageSize._wired) {
|
|
86
|
+
pageSize._wired = true;
|
|
87
|
+
pageSize.addEventListener('change', function () {
|
|
88
|
+
_CLUSTERS_STATE.pageSize = parseInt(pageSize.value, 10) || 25;
|
|
89
|
+
_CLUSTERS_STATE.page = 0;
|
|
90
|
+
_renderClustersPage();
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
var prev = document.getElementById('clusters-prev-btn');
|
|
94
|
+
if (prev && !prev._wired) {
|
|
95
|
+
prev._wired = true;
|
|
96
|
+
prev.addEventListener('click', function () {
|
|
97
|
+
if (_CLUSTERS_STATE.page > 0) {
|
|
98
|
+
_CLUSTERS_STATE.page -= 1;
|
|
99
|
+
_renderClustersPage();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
var next = document.getElementById('clusters-next-btn');
|
|
104
|
+
if (next && !next._wired) {
|
|
105
|
+
next._wired = true;
|
|
106
|
+
next.addEventListener('click', function () {
|
|
107
|
+
_CLUSTERS_STATE.page += 1;
|
|
108
|
+
_renderClustersPage();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
3
112
|
|
|
4
113
|
async function loadClusters() {
|
|
5
|
-
|
|
114
|
+
_wireClustersControls();
|
|
115
|
+
if (typeof showLoading === 'function') {
|
|
116
|
+
showLoading('clusters-list', 'Loading clusters...');
|
|
117
|
+
}
|
|
6
118
|
try {
|
|
7
119
|
var response = await fetch('/api/clusters');
|
|
8
120
|
var data = await response.json();
|
|
9
|
-
|
|
121
|
+
_CLUSTERS_STATE.clusters = data.clusters || [];
|
|
122
|
+
_CLUSTERS_STATE.page = 0;
|
|
123
|
+
_renderClustersPage();
|
|
10
124
|
} catch (error) {
|
|
11
125
|
console.error('Error loading clusters:', error);
|
|
12
|
-
|
|
126
|
+
if (typeof showEmpty === 'function') {
|
|
127
|
+
showEmpty('clusters-list', 'collection', 'Failed to load clusters');
|
|
128
|
+
}
|
|
13
129
|
}
|
|
14
130
|
}
|
|
15
131
|
|
|
16
132
|
function renderClusters(clusters) {
|
|
17
133
|
var container = document.getElementById('clusters-list');
|
|
18
134
|
if (!clusters || clusters.length === 0) {
|
|
19
|
-
|
|
135
|
+
if (typeof showEmpty === 'function') {
|
|
136
|
+
showEmpty('clusters-list', 'collection',
|
|
137
|
+
'No clusters found yet. Clusters form automatically as you store related memories.');
|
|
138
|
+
}
|
|
20
139
|
return;
|
|
21
140
|
}
|
|
22
141
|
|
|
@@ -1,45 +1,54 @@
|
|
|
1
1
|
// SuperLocalMemory V2 - Event Listener Wiring
|
|
2
2
|
// Loads LAST — after all module scripts. Connects UI events to module functions.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
3
|
+
//
|
|
4
|
+
// v3.4.21 Domain rewrite: several tabs / panes were folded together
|
|
5
|
+
// (Patterns/Learning/Behavioral -> Brain; Clusters/Entities -> Graph;
|
|
6
|
+
// Recall Lab/Timeline -> Memories; Events/Agents/Trust/Lifecycle/
|
|
7
|
+
// Compliance/Math/Ingestion -> Health). Every line below is now
|
|
8
|
+
// null-guarded so missing elements never halt JS execution — a
|
|
9
|
+
// silent TypeError here used to cascade into "all spinners forever"
|
|
10
|
+
// because ng-shell.js failed to initialise. This whole file is
|
|
11
|
+
// slated for deletion in the final dead-code purge; until then,
|
|
12
|
+
// null-guards keep it harmless.
|
|
13
|
+
|
|
14
|
+
(function wire() {
|
|
15
|
+
function onShown(id, fn) {
|
|
16
|
+
var el = document.getElementById(id);
|
|
17
|
+
if (el && typeof fn === 'function') {
|
|
18
|
+
el.addEventListener('shown.bs.tab', fn);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function on(id, ev, fn) {
|
|
23
|
+
var el = document.getElementById(id);
|
|
24
|
+
if (el) el.addEventListener(ev, fn);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
onShown('memories-tab', typeof loadMemories === 'function' ? loadMemories : null);
|
|
28
|
+
onShown('clusters-tab', typeof loadClusters === 'function' ? loadClusters : null);
|
|
29
|
+
onShown('patterns-tab', typeof loadPatterns === 'function' ? loadPatterns : null);
|
|
30
|
+
onShown('timeline-tab', typeof loadTimeline === 'function' ? loadTimeline : null);
|
|
31
|
+
onShown('settings-tab', typeof loadSettings === 'function' ? loadSettings : null);
|
|
32
|
+
onShown('events-tab', typeof loadEventStats === 'function' ? loadEventStats : null);
|
|
33
|
+
onShown('agents-tab', typeof loadAgents === 'function' ? loadAgents : null);
|
|
34
|
+
onShown('learning-tab', typeof loadLearning === 'function' ? loadLearning : null);
|
|
35
|
+
onShown('lifecycle-tab', typeof loadLifecycle === 'function' ? loadLifecycle : null);
|
|
36
|
+
onShown('behavioral-tab', typeof loadBehavioral === 'function' ? loadBehavioral : null);
|
|
37
|
+
onShown('compliance-tab', typeof loadCompliance === 'function' ? loadCompliance : null);
|
|
38
|
+
|
|
39
|
+
on('search-query', 'keypress', function (e) {
|
|
40
|
+
if (e.key === 'Enter' && typeof searchMemories === 'function') searchMemories();
|
|
23
41
|
});
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// v2.5 tabs (graceful — elements may not exist on older installs)
|
|
27
|
-
var eventsTab = document.getElementById('events-tab');
|
|
28
|
-
if (eventsTab) eventsTab.addEventListener('shown.bs.tab', loadEventStats);
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
if (
|
|
32
|
-
|
|
33
|
-
// v2.7 learning tab (graceful)
|
|
34
|
-
var learningTab = document.getElementById('learning-tab');
|
|
35
|
-
if (learningTab) learningTab.addEventListener('shown.bs.tab', loadLearning);
|
|
36
|
-
|
|
37
|
-
// v2.8 tabs (graceful — elements may not exist on older installs)
|
|
38
|
-
var lifecycleTab = document.getElementById('lifecycle-tab');
|
|
39
|
-
if (lifecycleTab) lifecycleTab.addEventListener('shown.bs.tab', loadLifecycle);
|
|
43
|
+
on('profile-select', 'change', function () {
|
|
44
|
+
if (typeof switchProfile === 'function') switchProfile(this.value);
|
|
45
|
+
});
|
|
40
46
|
|
|
41
|
-
|
|
42
|
-
if (
|
|
47
|
+
on('add-profile-btn', 'click', function () {
|
|
48
|
+
if (typeof createProfile === 'function') createProfile();
|
|
49
|
+
});
|
|
43
50
|
|
|
44
|
-
|
|
45
|
-
if (
|
|
51
|
+
on('new-profile-name', 'keypress', function (e) {
|
|
52
|
+
if (e.key === 'Enter' && typeof createProfile === 'function') createProfile();
|
|
53
|
+
});
|
|
54
|
+
})();
|
|
@@ -6,12 +6,29 @@
|
|
|
6
6
|
// value passes through escapeHtml(). Data comes from our own local SQLite DB only.
|
|
7
7
|
// nosemgrep: innerHTML-xss — all dynamic values escaped via escapeHtml()
|
|
8
8
|
|
|
9
|
+
// S9-DASH-07: active named filter ('high_reward' | 'being_forgotten' | null).
|
|
10
|
+
var _slmMemoryFilter = null;
|
|
11
|
+
|
|
12
|
+
function setMemoryFilter(name) {
|
|
13
|
+
// Toggle: second click on same pill clears.
|
|
14
|
+
_slmMemoryFilter = (_slmMemoryFilter === name) ? null : name;
|
|
15
|
+
var pills = document.querySelectorAll('.mem-filter-pill');
|
|
16
|
+
for (var i = 0; i < pills.length; i++) {
|
|
17
|
+
var active = pills[i].getAttribute('data-filter') === _slmMemoryFilter;
|
|
18
|
+
pills[i].classList.toggle('active', active);
|
|
19
|
+
}
|
|
20
|
+
loadMemories();
|
|
21
|
+
}
|
|
22
|
+
|
|
9
23
|
async function loadMemories() {
|
|
10
24
|
var category = document.getElementById('filter-category').value;
|
|
11
25
|
var project = document.getElementById('filter-project').value;
|
|
12
26
|
var url = '/api/memories?limit=50';
|
|
13
27
|
if (category) url += '&category=' + encodeURIComponent(category);
|
|
14
28
|
if (project) url += '&project_name=' + encodeURIComponent(project);
|
|
29
|
+
if (_slmMemoryFilter) {
|
|
30
|
+
url += '&filter=' + encodeURIComponent(_slmMemoryFilter);
|
|
31
|
+
}
|
|
15
32
|
|
|
16
33
|
showLoading('memories-list', 'Loading memories...');
|
|
17
34
|
try {
|
|
@@ -56,13 +73,33 @@ function renderMemoriesTable(memories, showScores) {
|
|
|
56
73
|
}
|
|
57
74
|
|
|
58
75
|
var memId = mem.memory_id || mem.id;
|
|
76
|
+
var factId = mem.fact_id || '';
|
|
59
77
|
var expandBtnHtml = '<button class="btn btn-sm btn-outline-secondary expand-facts-btn ms-1" data-memory-id="' + escapeHtml(String(memId)) + '" title="View atomic facts">▼</button>';
|
|
60
78
|
|
|
79
|
+
// S9-DASH-06: inline thumbs ↑/↓ on recall results. Writes a
|
|
80
|
+
// reward label to action_outcomes via /api/behavioral/
|
|
81
|
+
// report-outcome. Only shown when we have a fact_id (recall
|
|
82
|
+
// path) since the outcome is keyed by memory_ids.
|
|
83
|
+
var feedbackHtml = '';
|
|
84
|
+
if (showScores && factId) {
|
|
85
|
+
feedbackHtml = ''
|
|
86
|
+
+ '<span class="inline-feedback" data-fact-id="'
|
|
87
|
+
+ escapeHtml(String(factId)) + '" '
|
|
88
|
+
+ 'style="margin-left:8px; white-space:nowrap;">'
|
|
89
|
+
+ '<button type="button" class="btn btn-sm btn-outline-success fb-up" '
|
|
90
|
+
+ 'title="This was helpful (+1 reward)" '
|
|
91
|
+
+ 'style="padding:0 6px; font-size:11px;">👍</button> '
|
|
92
|
+
+ '<button type="button" class="btn btn-sm btn-outline-danger fb-down" '
|
|
93
|
+
+ 'title="Not helpful (0 reward)" '
|
|
94
|
+
+ 'style="padding:0 6px; font-size:11px;">👎</button>'
|
|
95
|
+
+ '</span>';
|
|
96
|
+
}
|
|
97
|
+
|
|
61
98
|
rows += '<tr data-mem-idx="' + idx + '">'
|
|
62
99
|
+ '<td>' + escapeHtml(String(mem.id)) + '</td>'
|
|
63
100
|
+ '<td><span class="badge bg-primary">' + escapeHtml(mem.category || 'None') + '</span></td>'
|
|
64
101
|
+ '<td><small>' + escapeHtml(mem.project_name || '-') + '</small></td>'
|
|
65
|
-
+ '<td class="memory-content" title="' + escapeHtml(content) + '">' + escapeHtml(contentPreview) + expandBtnHtml + '</td>'
|
|
102
|
+
+ '<td class="memory-content" title="' + escapeHtml(content) + '">' + escapeHtml(contentPreview) + expandBtnHtml + feedbackHtml + '</td>'
|
|
66
103
|
+ scoreCell
|
|
67
104
|
+ '<td><span class="badge bg-' + importanceClass + ' badge-importance">' + escapeHtml(String(importance)) + '</span></td>'
|
|
68
105
|
+ '<td>' + escapeHtml(String(mem.cluster_id || '-')) + '</td>'
|
|
@@ -100,8 +137,57 @@ function renderMemoriesTable(memories, showScores) {
|
|
|
100
137
|
return;
|
|
101
138
|
}
|
|
102
139
|
|
|
140
|
+
// S9-DASH-06: inline thumbs ↑/↓.
|
|
141
|
+
var fbUp = e.target.closest('.fb-up');
|
|
142
|
+
var fbDown = e.target.closest('.fb-down');
|
|
143
|
+
if (fbUp || fbDown) {
|
|
144
|
+
e.stopPropagation();
|
|
145
|
+
var btn = fbUp || fbDown;
|
|
146
|
+
var wrapper = btn.closest('.inline-feedback');
|
|
147
|
+
var fid = wrapper && wrapper.getAttribute('data-fact-id');
|
|
148
|
+
if (!fid) return;
|
|
149
|
+
var outcome = fbUp ? 'success' : 'failure';
|
|
150
|
+
// Disable both buttons while the request is in flight.
|
|
151
|
+
var allBtns = wrapper.querySelectorAll('button');
|
|
152
|
+
for (var i = 0; i < allBtns.length; i++) {
|
|
153
|
+
allBtns[i].setAttribute('disabled', 'disabled');
|
|
154
|
+
}
|
|
155
|
+
btn.textContent = '…';
|
|
156
|
+
fetch('/api/behavioral/report-outcome', {
|
|
157
|
+
method: 'POST',
|
|
158
|
+
headers: {'Content-Type': 'application/json'},
|
|
159
|
+
credentials: 'same-origin',
|
|
160
|
+
body: JSON.stringify({
|
|
161
|
+
memory_ids: [fid],
|
|
162
|
+
outcome: outcome,
|
|
163
|
+
action_type: 'inline_feedback',
|
|
164
|
+
context: 'search/recall result',
|
|
165
|
+
}),
|
|
166
|
+
}).then(function(r) { return r.json(); })
|
|
167
|
+
.then(function(json) {
|
|
168
|
+
if (json && json.success) {
|
|
169
|
+
btn.textContent = fbUp ? '✓' : '✓';
|
|
170
|
+
btn.style.background = fbUp ? '#2d7a2d' : '#7a2d2d';
|
|
171
|
+
btn.style.borderColor = fbUp ? '#2d7a2d' : '#7a2d2d';
|
|
172
|
+
btn.style.color = '#fff';
|
|
173
|
+
} else {
|
|
174
|
+
btn.textContent = fbUp ? '👍' : '👎';
|
|
175
|
+
for (var j = 0; j < allBtns.length; j++) {
|
|
176
|
+
allBtns[j].removeAttribute('disabled');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}).catch(function() {
|
|
180
|
+
btn.textContent = fbUp ? '👍' : '👎';
|
|
181
|
+
for (var k = 0; k < allBtns.length; k++) {
|
|
182
|
+
allBtns[k].removeAttribute('disabled');
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
103
188
|
var row = e.target.closest('tr[data-mem-idx]');
|
|
104
|
-
if (row && !e.target.closest('.expand-facts-btn')
|
|
189
|
+
if (row && !e.target.closest('.expand-facts-btn')
|
|
190
|
+
&& !e.target.closest('.inline-feedback')) {
|
|
105
191
|
var idx = parseInt(row.getAttribute('data-mem-idx'), 10);
|
|
106
192
|
if (window._slmMemories && window._slmMemories[idx]) {
|
|
107
193
|
openMemoryDetail(window._slmMemories[idx]);
|
|
@@ -215,10 +215,80 @@ function openMemoryDetail(mem, source) {
|
|
|
215
215
|
};
|
|
216
216
|
actionsDiv.appendChild(editBtn);
|
|
217
217
|
|
|
218
|
-
//
|
|
218
|
+
// S9-DASH-08: Forget (soft-archive) — non-destructive; row stays
|
|
219
|
+
// in atomic_facts with archive_status='archived' and gets a
|
|
220
|
+
// payload copy in memory_archive for future restore.
|
|
221
|
+
var forgetBtn = document.createElement('button');
|
|
222
|
+
forgetBtn.className = 'btn btn-outline-warning btn-sm';
|
|
223
|
+
forgetBtn.innerHTML = '<i class="bi bi-archive"></i> Forget';
|
|
224
|
+
forgetBtn.title = 'Archive this memory — hidden from recall but recoverable';
|
|
225
|
+
forgetBtn.onclick = function() {
|
|
226
|
+
if (!confirm('Forget this memory? It will be archived '
|
|
227
|
+
+ '(hidden from recall but recoverable).')) return;
|
|
228
|
+
forgetBtn.disabled = true;
|
|
229
|
+
fetch('/api/memories/' + encodeURIComponent(mem.id) + '/forget',
|
|
230
|
+
{method: 'POST'})
|
|
231
|
+
.then(function(r) { return r.json(); })
|
|
232
|
+
.then(function(d) {
|
|
233
|
+
if (d.success) {
|
|
234
|
+
modal.hide();
|
|
235
|
+
if (typeof showToast === 'function') {
|
|
236
|
+
showToast('Memory archived');
|
|
237
|
+
}
|
|
238
|
+
if (typeof loadMemories === 'function') {
|
|
239
|
+
setTimeout(loadMemories, 300);
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
forgetBtn.disabled = false;
|
|
243
|
+
}
|
|
244
|
+
}).catch(function() { forgetBtn.disabled = false; });
|
|
245
|
+
};
|
|
246
|
+
actionsDiv.appendChild(forgetBtn);
|
|
247
|
+
|
|
248
|
+
// S9-DASH-08: Merge — this fact is a duplicate of another;
|
|
249
|
+
// keep the target, archive this one. Writes memory_merge_log.
|
|
250
|
+
var mergeBtn = document.createElement('button');
|
|
251
|
+
mergeBtn.className = 'btn btn-outline-info btn-sm';
|
|
252
|
+
mergeBtn.innerHTML = '<i class="bi bi-union"></i> Merge into...';
|
|
253
|
+
mergeBtn.title = 'Mark this as a duplicate of another fact';
|
|
254
|
+
mergeBtn.onclick = function() {
|
|
255
|
+
var target = prompt(
|
|
256
|
+
'Merge this memory INTO which fact_id?\n\n'
|
|
257
|
+
+ '(Paste the target fact_id — this memory will be '
|
|
258
|
+
+ 'archived and audit-logged to memory_merge_log.)'
|
|
259
|
+
);
|
|
260
|
+
if (!target || !target.trim()) return;
|
|
261
|
+
mergeBtn.disabled = true;
|
|
262
|
+
fetch('/api/memories/' + encodeURIComponent(mem.id) + '/merge', {
|
|
263
|
+
method: 'POST',
|
|
264
|
+
headers: {'Content-Type': 'application/json'},
|
|
265
|
+
body: JSON.stringify({into: target.trim()}),
|
|
266
|
+
}).then(function(r) { return r.json(); })
|
|
267
|
+
.then(function(d) {
|
|
268
|
+
if (d.success) {
|
|
269
|
+
modal.hide();
|
|
270
|
+
if (typeof showToast === 'function') {
|
|
271
|
+
showToast('Merged into ' + d.into);
|
|
272
|
+
}
|
|
273
|
+
if (typeof loadMemories === 'function') {
|
|
274
|
+
setTimeout(loadMemories, 300);
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
mergeBtn.disabled = false;
|
|
278
|
+
if (typeof showToast === 'function') {
|
|
279
|
+
showToast('Merge failed: '
|
|
280
|
+
+ (d && (d.error || d.detail) || 'unknown'));
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}).catch(function() { mergeBtn.disabled = false; });
|
|
284
|
+
};
|
|
285
|
+
actionsDiv.appendChild(mergeBtn);
|
|
286
|
+
|
|
287
|
+
// Delete button — always available (hard delete, irreversible)
|
|
219
288
|
var deleteBtn = document.createElement('button');
|
|
220
289
|
deleteBtn.className = 'btn btn-outline-danger btn-sm';
|
|
221
290
|
deleteBtn.innerHTML = '<i class="bi bi-trash"></i> Delete';
|
|
291
|
+
deleteBtn.title = 'Permanently delete (cannot be undone) — prefer Forget';
|
|
222
292
|
deleteBtn.onclick = function() {
|
|
223
293
|
if (!confirm('Delete this memory? This cannot be undone.')) return;
|
|
224
294
|
fetch('/api/memories/' + encodeURIComponent(mem.id), {method: 'DELETE'})
|