superlocalmemory 3.6.13 → 3.6.14
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/.claude-plugin/marketplace.json +17 -0
- package/README.md +187 -741
- package/package.json +12 -5
- package/plugin/.claude-plugin/plugin.json +20 -0
- package/plugin/.mcp.json +12 -0
- package/plugin/CLAUDE.md +43 -0
- package/plugin/_GENERATED.md +6 -0
- package/plugin/agents/slm-memory-advisor.md +43 -0
- package/plugin/agents/slm-optimize-advisor.md +38 -0
- package/plugin/hooks/hooks.json +14 -0
- package/plugin/requirements.txt +1 -0
- package/plugin/scripts/ensure-venv.bat +122 -0
- package/plugin/scripts/ensure-venv.sh +105 -0
- package/plugin/scripts/slm-launch +15 -0
- package/plugin/scripts/slm-launch.bat +17 -0
- package/plugin/settings.json +16 -0
- package/plugin/skills/slm-cache/SKILL.md +140 -0
- package/plugin/skills/slm-compress/SKILL.md +143 -0
- package/plugin/skills/slm-graph/SKILL.md +300 -0
- package/plugin/skills/slm-recall/SKILL.md +196 -0
- package/plugin/skills/slm-remember/SKILL.md +182 -0
- package/plugin/skills/slm-session/SKILL.md +207 -0
- package/plugin/skills/slm-status/SKILL.md +149 -0
- package/plugin-src/.mcp.json +12 -0
- package/plugin-src/agents/slm-memory-advisor.md +43 -0
- package/plugin-src/agents/slm-optimize-advisor.md +38 -0
- package/plugin-src/commands/slm-optimize.md +22 -0
- package/plugin-src/commands/slm-recall.md +16 -0
- package/plugin-src/commands/slm-remember.md +16 -0
- package/plugin-src/commands/slm-status.md +15 -0
- package/plugin-src/hooks/.gitkeep +0 -0
- package/plugin-src/hooks/hooks.json +14 -0
- package/plugin-src/manifest.json +25 -0
- package/plugin-src/requirements.txt +1 -0
- package/plugin-src/rules/AGENTS.md +90 -0
- package/plugin-src/rules/CLAUDE.md.fragment +43 -0
- package/plugin-src/scripts/ensure-venv.bat +122 -0
- package/plugin-src/scripts/ensure-venv.sh +105 -0
- package/plugin-src/scripts/slm-launch +15 -0
- package/plugin-src/scripts/slm-launch.bat +17 -0
- package/plugin-src/settings.json +16 -0
- package/plugin-src/skills/slm-cache/SKILL.md +140 -0
- package/plugin-src/skills/slm-compress/SKILL.md +143 -0
- package/plugin-src/skills/slm-graph/SKILL.md +300 -0
- package/plugin-src/skills/slm-recall/SKILL.md +196 -0
- package/plugin-src/skills/slm-remember/SKILL.md +182 -0
- package/plugin-src/skills/slm-session/SKILL.md +207 -0
- package/plugin-src/skills/slm-status/SKILL.md +149 -0
- package/pyproject.toml +6 -2
- package/scripts/__tests__/build-plugin.test.mjs +613 -0
- package/scripts/_savings_math.py +270 -0
- package/scripts/build-plugin.js +742 -0
- package/scripts/dogfood_savings.py +490 -0
- package/scripts/install-skills.ps1 +4 -334
- package/scripts/install-skills.sh +4 -435
- package/scripts/postinstall-interactive.js +0 -27
- package/scripts/postinstall.js +21 -2
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/_lazy_init.py +115 -0
- package/src/superlocalmemory/cli/commands.py +348 -39
- package/src/superlocalmemory/cli/main.py +47 -4
- package/src/superlocalmemory/cli/setup_wizard.py +20 -6
- package/src/superlocalmemory/core/config.py +79 -9
- package/src/superlocalmemory/core/embeddings.py +10 -5
- package/src/superlocalmemory/core/engine.py +2 -2
- package/src/superlocalmemory/hooks/claude_code_hooks.py +27 -3
- package/src/superlocalmemory/hooks/portable_kit.py +506 -0
- package/src/superlocalmemory/infra/cloud_backup.py +99 -23
- package/src/superlocalmemory/mcp/cli_fallback.py +602 -0
- package/src/superlocalmemory/mcp/server.py +75 -4
- package/src/superlocalmemory/mcp/tools_code_graph.py +3 -3
- package/src/superlocalmemory/mcp/tools_core.py +12 -4
- package/src/superlocalmemory/optimize/cache/boundary_store.py +25 -6
- package/src/superlocalmemory/optimize/cache/centroid_store.py +27 -4
- package/src/superlocalmemory/optimize/cache/manager.py +92 -6
- package/src/superlocalmemory/optimize/cache/semantic.py +20 -1
- package/src/superlocalmemory/optimize/compress/ccr.py +12 -0
- package/src/superlocalmemory/optimize/compress/router.py +46 -13
- package/src/superlocalmemory/optimize/config/schema.py +6 -0
- package/src/superlocalmemory/optimize/proxy/_helpers.py +111 -8
- package/src/superlocalmemory/optimize/proxy/anthropic_surface.py +14 -4
- package/src/superlocalmemory/optimize/proxy/gemini_surface.py +23 -6
- package/src/superlocalmemory/optimize/proxy/openai_surface.py +10 -4
- package/src/superlocalmemory/optimize/proxy/server.py +11 -0
- package/src/superlocalmemory/optimize/proxy/vertex_surface.py +246 -0
- package/src/superlocalmemory/optimize/storage/db.py +30 -0
- package/src/superlocalmemory/server/recall_serializer.py +3 -1
- package/src/superlocalmemory/server/unified_daemon.py +24 -6
- package/src/superlocalmemory/ui/css/legacy-dashboard.css +18 -0
- package/src/superlocalmemory/ui/css/neural-glass.css +5 -0
- package/src/superlocalmemory/ui/index.html +2 -2
- package/src/superlocalmemory/ui/js/core.js +98 -0
- package/src/superlocalmemory/ui/js/dashboard.js +8 -1
- package/src/superlocalmemory/ui/js/ide-status.js +16 -3
- package/src/superlocalmemory/ui/js/math-health.js +15 -3
- package/src/superlocalmemory/ui/js/optimize.js +18 -2
- package/src/superlocalmemory/ui/js/trust-dashboard.js +10 -1
- package/src/superlocalmemory.egg-info/PKG-INFO +189 -742
- package/src/superlocalmemory.egg-info/SOURCES.txt +6 -9
- package/src/superlocalmemory.egg-info/requires.txt +1 -0
- package/ide/skills/slm-build-graph/SKILL.md +0 -423
- package/ide/skills/slm-list-recent/SKILL.md +0 -348
- package/ide/skills/slm-recall/SKILL.md +0 -326
- package/ide/skills/slm-remember/SKILL.md +0 -194
- package/ide/skills/slm-show-patterns/SKILL.md +0 -224
- package/ide/skills/slm-status/SKILL.md +0 -363
- package/ide/skills/slm-switch-profile/SKILL.md +0 -442
- package/skills/slm-build-graph/SKILL.md +0 -423
- package/skills/slm-list-recent/SKILL.md +0 -348
- package/skills/slm-optimize/README.md +0 -55
- package/skills/slm-optimize/SKILL.md +0 -139
- package/skills/slm-recall/SKILL.md +0 -343
- package/skills/slm-remember/SKILL.md +0 -194
- package/skills/slm-show-patterns/SKILL.md +0 -224
- package/skills/slm-status/SKILL.md +0 -363
- package/skills/slm-switch-profile/SKILL.md +0 -442
- package/src/superlocalmemory/cli/doctor_cmd.py +0 -152
- package/src/superlocalmemory/skills/slm-build-graph/SKILL.md +0 -423
- package/src/superlocalmemory/skills/slm-list-recent/SKILL.md +0 -348
- package/src/superlocalmemory/skills/slm-recall/SKILL.md +0 -343
- package/src/superlocalmemory/skills/slm-remember/SKILL.md +0 -194
- package/src/superlocalmemory/skills/slm-show-patterns/SKILL.md +0 -224
- package/src/superlocalmemory/skills/slm-status/SKILL.md +0 -363
- package/src/superlocalmemory/skills/slm-switch-profile/SKILL.md +0 -442
|
@@ -947,6 +947,36 @@ class CacheDB:
|
|
|
947
947
|
except sqlite3.Error as exc:
|
|
948
948
|
logger.warning("CacheDB.ccr_update_compressed failed: %s", exc)
|
|
949
949
|
|
|
950
|
+
def ccr_delete(self, ccr_id: str) -> None:
|
|
951
|
+
"""Delete a CCR row by ccr_id. Idempotent — warns on sqlite error, never raises.
|
|
952
|
+
|
|
953
|
+
WP-10 D6: defensive infra + sweep parity. Deleting a non-existent row is a no-op.
|
|
954
|
+
"""
|
|
955
|
+
try:
|
|
956
|
+
self._db.execute(
|
|
957
|
+
"DELETE FROM llmcache_ccr_originals WHERE ccr_id = ?",
|
|
958
|
+
(ccr_id,),
|
|
959
|
+
)
|
|
960
|
+
except sqlite3.Error as exc:
|
|
961
|
+
logger.warning("CacheDB.ccr_delete failed (non-fatal): %s", exc)
|
|
962
|
+
|
|
963
|
+
def ccr_count(self) -> int:
|
|
964
|
+
"""Return UNFILTERED count of rows in llmcache_ccr_originals.
|
|
965
|
+
|
|
966
|
+
WP-10 CRIT-2: Do NOT reuse TTL-filtered count at :646. A fresh no-expiry row
|
|
967
|
+
has ttl_expires=None, so the TTL filter returns 0 and D6 orphan tests would
|
|
968
|
+
falsely pass. This unfiltered count is test infrastructure only.
|
|
969
|
+
"""
|
|
970
|
+
try:
|
|
971
|
+
rows = self._db.execute(
|
|
972
|
+
"SELECT COUNT(*) AS n FROM llmcache_ccr_originals",
|
|
973
|
+
(),
|
|
974
|
+
)
|
|
975
|
+
return int(dict(rows[0])["n"]) if rows else 0
|
|
976
|
+
except sqlite3.Error as exc:
|
|
977
|
+
logger.warning("CacheDB.ccr_count failed: %s", exc)
|
|
978
|
+
return 0
|
|
979
|
+
|
|
950
980
|
# ---- v2 additions ----
|
|
951
981
|
|
|
952
982
|
def get_all_vectors(self, tenant_id: str) -> list[tuple[str, bytes, str]]:
|
|
@@ -23,6 +23,8 @@ from __future__ import annotations
|
|
|
23
23
|
import re
|
|
24
24
|
from typing import Any
|
|
25
25
|
|
|
26
|
+
from superlocalmemory.core.config import CANONICAL_RECALL_LIMIT
|
|
27
|
+
|
|
26
28
|
|
|
27
29
|
# ---------------------------------------------------------------------------
|
|
28
30
|
# F-2: Per-fact content clamp
|
|
@@ -161,7 +163,7 @@ def apply_source_content_discipline(
|
|
|
161
163
|
def serialize_recall_response(
|
|
162
164
|
response: Any,
|
|
163
165
|
*,
|
|
164
|
-
limit: int =
|
|
166
|
+
limit: int = CANONICAL_RECALL_LIMIT,
|
|
165
167
|
memory_map: dict[str, str] | None = None,
|
|
166
168
|
per_fact_max: int = 2400,
|
|
167
169
|
total_max: int = 12000,
|
|
@@ -50,6 +50,8 @@ from fastapi.middleware.cors import CORSMiddleware
|
|
|
50
50
|
from fastapi.middleware.gzip import GZipMiddleware
|
|
51
51
|
from pydantic import BaseModel
|
|
52
52
|
|
|
53
|
+
from superlocalmemory.core.config import CANONICAL_RECALL_LIMIT
|
|
54
|
+
|
|
53
55
|
logger = logging.getLogger("superlocalmemory.unified_daemon")
|
|
54
56
|
|
|
55
57
|
_DEFAULT_PORT = 8765
|
|
@@ -237,16 +239,32 @@ class ObserveBuffer:
|
|
|
237
239
|
try:
|
|
238
240
|
from superlocalmemory.hooks.auto_capture import AutoCapture
|
|
239
241
|
auto = AutoCapture(engine=self._engine)
|
|
242
|
+
captured_count = 0
|
|
243
|
+
failed_count = 0
|
|
240
244
|
for content in batch:
|
|
241
245
|
try:
|
|
242
246
|
decision = auto.evaluate(content)
|
|
243
247
|
if decision.capture:
|
|
244
248
|
auto.capture(content, category=decision.category)
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
249
|
+
# Stage-9: count only what was actually WRITTEN to memory.
|
|
250
|
+
# The prior 'processed N' counted skipped (capture=False)
|
|
251
|
+
# items as successes — a false-positive write count.
|
|
252
|
+
captured_count += 1
|
|
253
|
+
except Exception as exc:
|
|
254
|
+
failed_count += 1
|
|
255
|
+
logger.warning(
|
|
256
|
+
"ObserveBuffer: auto.capture failed for content %.40r: %s",
|
|
257
|
+
content,
|
|
258
|
+
exc,
|
|
259
|
+
)
|
|
260
|
+
logger.info(
|
|
261
|
+
"Observe debounce: evaluated=%d captured=%d failed=%d",
|
|
262
|
+
len(batch),
|
|
263
|
+
captured_count,
|
|
264
|
+
failed_count,
|
|
265
|
+
)
|
|
266
|
+
except Exception as exc:
|
|
267
|
+
logger.error("ObserveBuffer: flush batch failed: %s", exc)
|
|
250
268
|
|
|
251
269
|
def flush_sync(self) -> None:
|
|
252
270
|
"""Force flush for shutdown."""
|
|
@@ -1620,7 +1638,7 @@ def _register_daemon_routes(application: FastAPI) -> None:
|
|
|
1620
1638
|
@application.get("/recall")
|
|
1621
1639
|
async def recall(
|
|
1622
1640
|
request: Request,
|
|
1623
|
-
q: str = "", query: str = "", limit: int =
|
|
1641
|
+
q: str = "", query: str = "", limit: int = CANONICAL_RECALL_LIMIT,
|
|
1624
1642
|
session_id: str = "",
|
|
1625
1643
|
fast: bool = False,
|
|
1626
1644
|
full: bool = False,
|
|
@@ -202,6 +202,24 @@
|
|
|
202
202
|
margin-bottom: 12px;
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
+
/* Pane error state — WP-12 */
|
|
206
|
+
.pane-error {
|
|
207
|
+
text-align: center;
|
|
208
|
+
padding: 40px;
|
|
209
|
+
color: var(--bs-danger, #dc3545);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.pane-error i {
|
|
213
|
+
font-size: 2rem;
|
|
214
|
+
display: block;
|
|
215
|
+
margin-bottom: 8px;
|
|
216
|
+
opacity: 0.7;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.pane-error p {
|
|
220
|
+
margin-bottom: 12px;
|
|
221
|
+
}
|
|
222
|
+
|
|
205
223
|
.tooltip-custom {
|
|
206
224
|
position: absolute;
|
|
207
225
|
padding: 10px;
|
|
@@ -1242,7 +1242,7 @@
|
|
|
1242
1242
|
</div>
|
|
1243
1243
|
<div class="card-body">
|
|
1244
1244
|
<div class="row g-3">
|
|
1245
|
-
<div class="col-md-6">
|
|
1245
|
+
<div class="col-md-6" id="optimize-config-card">
|
|
1246
1246
|
<h6>Controls</h6>
|
|
1247
1247
|
<div class="form-check form-switch mb-2">
|
|
1248
1248
|
<input class="form-check-input" type="checkbox" id="opt-enabled">
|
|
@@ -1282,7 +1282,7 @@
|
|
|
1282
1282
|
<label class="form-check-label" for="opt-compress-prose">Prose Compression</label>
|
|
1283
1283
|
</div>
|
|
1284
1284
|
</div>
|
|
1285
|
-
<div class="col-md-6">
|
|
1285
|
+
<div class="col-md-6" id="optimize-savings-card">
|
|
1286
1286
|
<h6>Savings</h6>
|
|
1287
1287
|
<table class="table table-sm">
|
|
1288
1288
|
<tr><td>Tokens Saved</td><td id="opt-tokens-saved">-</td></tr>
|
|
@@ -199,6 +199,104 @@ function showEmpty(containerId, icon, message) {
|
|
|
199
199
|
el.appendChild(wrapper);
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Pane error state — WP-12
|
|
204
|
+
// showPaneError / clearPaneError / paneErrorMessage
|
|
205
|
+
// ============================================================================
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Map an HTTP status (or 0 for network failure) to a user-readable message.
|
|
209
|
+
* @param {number} status 0 = network/abort, ≥400 = HTTP error
|
|
210
|
+
* @returns {string}
|
|
211
|
+
*/
|
|
212
|
+
function paneErrorMessage(status) {
|
|
213
|
+
if (!status || status === 0) {
|
|
214
|
+
return 'Service unavailable — check network connection';
|
|
215
|
+
}
|
|
216
|
+
if (status >= 500) {
|
|
217
|
+
return 'Server error ' + status + ' — daemon may be down';
|
|
218
|
+
}
|
|
219
|
+
return 'Request failed ' + status;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Render an inline error banner inside a pane container.
|
|
224
|
+
*
|
|
225
|
+
* slotMode=false (container panes — math-health, ide-status):
|
|
226
|
+
* Clears the container and appends the error div directly.
|
|
227
|
+
*
|
|
228
|
+
* slotMode=true (field-scatter panes — dashboard, trust, optimize):
|
|
229
|
+
* Inserts/replaces a #<containerId>-error-slot div at the top of the
|
|
230
|
+
* container, leaving existing field values visible.
|
|
231
|
+
*
|
|
232
|
+
* @param {string} containerId - getElementById target
|
|
233
|
+
* @param {string} message - user-facing message (set via textContent — XSS-safe)
|
|
234
|
+
* @param {Function|null} onRetry - callback for Retry button; null = no button
|
|
235
|
+
* @param {boolean} slotMode - true for field-scatter panes
|
|
236
|
+
*/
|
|
237
|
+
function showPaneError(containerId, message, onRetry, slotMode) {
|
|
238
|
+
var el = document.getElementById(containerId);
|
|
239
|
+
if (!el) return;
|
|
240
|
+
|
|
241
|
+
// Build the error div
|
|
242
|
+
var errDiv = document.createElement('div');
|
|
243
|
+
errDiv.className = 'pane-error';
|
|
244
|
+
errDiv.setAttribute('role', 'alert');
|
|
245
|
+
|
|
246
|
+
var icon = document.createElement('i');
|
|
247
|
+
icon.className = 'bi bi-exclamation-triangle';
|
|
248
|
+
errDiv.appendChild(icon);
|
|
249
|
+
|
|
250
|
+
var p = document.createElement('p');
|
|
251
|
+
p.textContent = message; // textContent — no XSS risk
|
|
252
|
+
errDiv.appendChild(p);
|
|
253
|
+
|
|
254
|
+
if (typeof onRetry === 'function') {
|
|
255
|
+
var btn = document.createElement('button');
|
|
256
|
+
btn.className = 'btn btn-sm btn-outline-danger pane-error-retry';
|
|
257
|
+
btn.textContent = 'Retry';
|
|
258
|
+
// Capture onRetry lexically to avoid closure issues in IIFEs (CRIT-1)
|
|
259
|
+
(function(cb) {
|
|
260
|
+
btn.addEventListener('click', function() { cb(); });
|
|
261
|
+
}(onRetry));
|
|
262
|
+
errDiv.appendChild(btn);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (slotMode) {
|
|
266
|
+
// Insert/replace error slot at top of container
|
|
267
|
+
var slotId = containerId + '-error-slot';
|
|
268
|
+
errDiv.id = slotId;
|
|
269
|
+
var existing = document.getElementById(slotId);
|
|
270
|
+
if (existing) {
|
|
271
|
+
existing.parentNode.replaceChild(errDiv, existing);
|
|
272
|
+
} else {
|
|
273
|
+
el.insertBefore(errDiv, el.firstChild);
|
|
274
|
+
}
|
|
275
|
+
} else {
|
|
276
|
+
// Replace container contents
|
|
277
|
+
el.textContent = '';
|
|
278
|
+
el.appendChild(errDiv);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Remove the error state previously set by showPaneError.
|
|
284
|
+
*
|
|
285
|
+
* @param {string} containerId - getElementById target
|
|
286
|
+
* @param {boolean} slotMode - must match the slotMode used in showPaneError
|
|
287
|
+
*/
|
|
288
|
+
function clearPaneError(containerId, slotMode) {
|
|
289
|
+
if (slotMode) {
|
|
290
|
+
var slot = document.getElementById(containerId + '-error-slot');
|
|
291
|
+
if (slot && slot.parentNode) slot.parentNode.removeChild(slot);
|
|
292
|
+
} else {
|
|
293
|
+
var el = document.getElementById(containerId);
|
|
294
|
+
if (!el) return;
|
|
295
|
+
var err = el.querySelector('.pane-error');
|
|
296
|
+
if (err) el.removeChild(err);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
202
300
|
// ============================================================================
|
|
203
301
|
// Safe HTML builder — tagged template for escaped interpolation
|
|
204
302
|
// ============================================================================
|
|
@@ -17,11 +17,17 @@ window.addEventListener('hashchange', function() {
|
|
|
17
17
|
window.addEventListener('focus', function() { loadDashboard(); });
|
|
18
18
|
|
|
19
19
|
async function loadDashboard() {
|
|
20
|
+
var PANE_ID = 'dashboard-pane';
|
|
20
21
|
try {
|
|
21
22
|
var response = await fetch('/api/v3/dashboard');
|
|
22
|
-
if (!response.ok)
|
|
23
|
+
if (!response.ok) {
|
|
24
|
+
showPaneError(PANE_ID, paneErrorMessage(response.status), loadDashboard, true);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
23
27
|
var data = await response.json();
|
|
24
28
|
|
|
29
|
+
clearPaneError(PANE_ID, true);
|
|
30
|
+
|
|
25
31
|
document.getElementById('dashboard-mode').textContent = 'Mode ' + data.mode.toUpperCase();
|
|
26
32
|
document.getElementById('dashboard-mode-desc').textContent = data.mode_name + (data.provider !== 'none' ? ' — ' + data.provider : '');
|
|
27
33
|
document.getElementById('dashboard-memory-count').textContent = data.fact_count || data.memory_count || '0';
|
|
@@ -44,6 +50,7 @@ async function loadDashboard() {
|
|
|
44
50
|
btn.classList.toggle('active', btn.dataset.mode === data.mode);
|
|
45
51
|
});
|
|
46
52
|
} catch (e) {
|
|
53
|
+
showPaneError(PANE_ID, paneErrorMessage(0), loadDashboard, true);
|
|
47
54
|
console.log('Dashboard load error:', e);
|
|
48
55
|
}
|
|
49
56
|
}
|
|
@@ -2,14 +2,26 @@
|
|
|
2
2
|
// Displays detected IDEs and allows connecting them to SLM.
|
|
3
3
|
|
|
4
4
|
async function loadIDEStatus() {
|
|
5
|
+
var CID = 'ide-list-body';
|
|
5
6
|
try {
|
|
6
7
|
var response = await fetch('/api/v3/ide/status');
|
|
7
|
-
if (!response.ok)
|
|
8
|
+
if (!response.ok) {
|
|
9
|
+
showPaneError(CID, paneErrorMessage(response.status), loadIDEStatus, false);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
8
12
|
var data = await response.json();
|
|
9
13
|
|
|
10
|
-
var
|
|
14
|
+
var ides = data.ides || [];
|
|
15
|
+
|
|
16
|
+
// AC5: empty array → empty state
|
|
17
|
+
if (ides.length === 0) {
|
|
18
|
+
showEmpty(CID, 'laptop', 'No IDEs detected');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var tbody = document.getElementById(CID);
|
|
11
23
|
tbody.textContent = '';
|
|
12
|
-
|
|
24
|
+
ides.forEach(function(ide) {
|
|
13
25
|
var tr = document.createElement('tr');
|
|
14
26
|
|
|
15
27
|
// IDE name cell
|
|
@@ -52,6 +64,7 @@ async function loadIDEStatus() {
|
|
|
52
64
|
tbody.appendChild(tr);
|
|
53
65
|
});
|
|
54
66
|
} catch (e) {
|
|
67
|
+
showPaneError('ide-list-body', paneErrorMessage(0), loadIDEStatus, false);
|
|
55
68
|
console.log('IDE status error:', e);
|
|
56
69
|
}
|
|
57
70
|
}
|
|
@@ -2,15 +2,26 @@
|
|
|
2
2
|
// Displays status of Fisher-Rao, sheaf cohomology, and Langevin dynamics layers.
|
|
3
3
|
|
|
4
4
|
async function loadMathHealth() {
|
|
5
|
+
var CID = 'math-health-cards';
|
|
5
6
|
try {
|
|
6
7
|
var response = await fetch('/api/v3/math/health');
|
|
7
|
-
if (!response.ok)
|
|
8
|
+
if (!response.ok) {
|
|
9
|
+
showPaneError(CID, paneErrorMessage(response.status), loadMathHealth, false);
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
8
12
|
var data = await response.json();
|
|
9
13
|
|
|
10
|
-
var
|
|
14
|
+
var layers = data.health || {};
|
|
15
|
+
|
|
16
|
+
// AC5: empty object → empty state
|
|
17
|
+
if (!layers || Object.keys(layers).length === 0) {
|
|
18
|
+
showEmpty(CID, 'calculator', 'No math health data available');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
var container = document.getElementById(CID);
|
|
11
23
|
container.textContent = '';
|
|
12
24
|
|
|
13
|
-
var layers = data.health || {};
|
|
14
25
|
var colors = { fisher: 'primary', sheaf: 'success', langevin: 'info' };
|
|
15
26
|
var icons = { fisher: 'bi-graph-up', sheaf: 'bi-diagram-3', langevin: 'bi-activity' };
|
|
16
27
|
|
|
@@ -91,6 +102,7 @@ async function loadMathHealth() {
|
|
|
91
102
|
container.appendChild(col);
|
|
92
103
|
});
|
|
93
104
|
} catch (e) {
|
|
105
|
+
showPaneError('math-health-cards', paneErrorMessage(0), loadMathHealth, false);
|
|
94
106
|
console.log('Math health error:', e);
|
|
95
107
|
}
|
|
96
108
|
}
|
|
@@ -17,11 +17,17 @@
|
|
|
17
17
|
_pollTimer = setInterval(_loadSavings, 10000);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
var CFG_CARD = 'optimize-config-card';
|
|
21
|
+
|
|
20
22
|
async function _loadOptimizeConfig() {
|
|
21
23
|
try {
|
|
22
24
|
var resp = await fetch('/api/optimize/config');
|
|
23
|
-
if (!resp.ok)
|
|
25
|
+
if (!resp.ok) {
|
|
26
|
+
showPaneError(CFG_CARD, paneErrorMessage(resp.status), _loadOptimizeConfig, true);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
24
29
|
var cfg = await resp.json();
|
|
30
|
+
clearPaneError(CFG_CARD, true);
|
|
25
31
|
_setToggle('opt-enabled', cfg.enabled);
|
|
26
32
|
_setToggle('opt-proxy-enabled', cfg.proxy_enabled);
|
|
27
33
|
_setToggle('opt-cache-enabled', cfg.cache_enabled);
|
|
@@ -32,15 +38,23 @@
|
|
|
32
38
|
var verEl = document.getElementById('opt-config-version');
|
|
33
39
|
if (verEl) verEl.textContent = cfg.config_version || '-';
|
|
34
40
|
} catch (e) {
|
|
41
|
+
showPaneError(CFG_CARD, paneErrorMessage(0), _loadOptimizeConfig, true);
|
|
35
42
|
console.log('Optimize config load error:', e);
|
|
36
43
|
}
|
|
37
44
|
}
|
|
38
45
|
|
|
46
|
+
var SAV_CARD = 'optimize-savings-card';
|
|
47
|
+
|
|
39
48
|
async function _loadSavings() {
|
|
40
49
|
try {
|
|
41
50
|
var resp = await fetch('/api/optimize/savings');
|
|
42
|
-
if (!resp.ok)
|
|
51
|
+
if (!resp.ok) {
|
|
52
|
+
// D-3: no Retry on polling loader \u2014 auto-heals on next poll
|
|
53
|
+
showPaneError(SAV_CARD, paneErrorMessage(resp.status), null, true);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
43
56
|
var data = await resp.json();
|
|
57
|
+
clearPaneError(SAV_CARD, true);
|
|
44
58
|
var tokensSaved = (data.tokens_saved_input || 0) + (data.tokens_saved_output || 0) + (data.tokens_saved_compress || 0);
|
|
45
59
|
_setText('opt-tokens-saved', tokensSaved.toLocaleString());
|
|
46
60
|
var costSaved = data.cost_saved || {};
|
|
@@ -57,6 +71,8 @@
|
|
|
57
71
|
_setText('opt-stale-warning', 'Pricing data may be outdated');
|
|
58
72
|
}
|
|
59
73
|
} catch (e) {
|
|
74
|
+
// D-3: no Retry on polling loader
|
|
75
|
+
showPaneError(SAV_CARD, paneErrorMessage(0), null, true);
|
|
60
76
|
console.log('Savings load error:', e);
|
|
61
77
|
}
|
|
62
78
|
}
|
|
@@ -127,12 +127,20 @@
|
|
|
127
127
|
if (next) next.disabled = STATE.page >= maxPage;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
var TRUST_PANE_ID = 'operations-pane';
|
|
131
|
+
|
|
130
132
|
async function loadTrustDashboard() {
|
|
131
133
|
try {
|
|
132
134
|
var resp = await fetch('/api/v3/trust/dashboard');
|
|
133
|
-
if (!resp.ok)
|
|
135
|
+
if (!resp.ok) {
|
|
136
|
+
// CRIT-1: pass lexically-scoped loadTrustDashboard (inside IIFE)
|
|
137
|
+
showPaneError(TRUST_PANE_ID, paneErrorMessage(resp.status), loadTrustDashboard, true);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
134
140
|
var data = await resp.json();
|
|
135
141
|
|
|
142
|
+
clearPaneError(TRUST_PANE_ID, true);
|
|
143
|
+
|
|
136
144
|
var agents = data.agents || [];
|
|
137
145
|
STATE.agents = agents;
|
|
138
146
|
STATE.page = 0;
|
|
@@ -154,6 +162,7 @@
|
|
|
154
162
|
|
|
155
163
|
_renderPage();
|
|
156
164
|
} catch (e) {
|
|
165
|
+
showPaneError(TRUST_PANE_ID, paneErrorMessage(0), loadTrustDashboard, true);
|
|
157
166
|
if (window.console && window.console.debug) {
|
|
158
167
|
window.console.debug('Trust dashboard error:', e);
|
|
159
168
|
}
|