superlocalmemory 4.0.7 → 4.0.8
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 +73 -0
- package/README.md +3 -3
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/CLAUDE.md +3 -3
- package/plugin/agents/slm-governance-advisor.md +1 -1
- package/plugin/agents/slm-loop-runner.md +1 -1
- package/plugin/agents/slm-memory-advisor.md +1 -1
- package/plugin/agents/slm-optimize-advisor.md +1 -1
- package/plugin/requirements.txt +1 -1
- package/plugin/skills/slm-cache/SKILL.md +1 -1
- package/plugin/skills/slm-compress/SKILL.md +1 -1
- package/plugin/skills/slm-governance/SKILL.md +1 -1
- package/plugin/skills/slm-graph/SKILL.md +1 -1
- package/plugin/skills/slm-loop/SKILL.md +1 -1
- package/plugin/skills/slm-mesh/SKILL.md +1 -1
- package/plugin/skills/slm-profile/SKILL.md +1 -1
- package/plugin/skills/slm-recall/SKILL.md +1 -1
- package/plugin/skills/slm-remember/SKILL.md +1 -1
- package/plugin/skills/slm-scope/SKILL.md +1 -1
- package/plugin/skills/slm-session/SKILL.md +1 -1
- package/plugin/skills/slm-status/SKILL.md +1 -1
- package/plugin-src/rules/AGENTS.md +1 -1
- package/pyproject.toml +1 -1
- package/src/superlocalmemory/__init__.py +1 -1
- package/src/superlocalmemory/cli/summary_cmd.py +23 -3
- package/src/superlocalmemory/code_graph/bridge/maintenance.py +7 -1
- package/src/superlocalmemory/core/consolidation_engine.py +14 -15
- package/src/superlocalmemory/core/recall_worker.py +4 -0
- package/src/superlocalmemory/evolution/skill_evolver.py +16 -1
- package/src/superlocalmemory/hooks/hook_handlers.py +38 -11
- package/src/superlocalmemory/learning/pattern_miner.py +12 -7
- package/src/superlocalmemory/mcp/profiles.py +10 -3
- package/src/superlocalmemory/mcp/server.py +7 -0
- package/src/superlocalmemory/mcp/tools_summaries.py +147 -0
- package/src/superlocalmemory/server/consolidation_runner.py +140 -0
- package/src/superlocalmemory/server/recall_serializer.py +34 -2
- package/src/superlocalmemory/server/routes/agents.py +52 -8
- package/src/superlocalmemory/server/routes/brain.py +108 -0
- package/src/superlocalmemory/server/routes/memories.py +153 -0
- package/src/superlocalmemory/server/routes/v3_api.py +24 -46
- package/src/superlocalmemory/server/unified_daemon.py +107 -0
- package/src/superlocalmemory/summaries/base.py +159 -0
- package/src/superlocalmemory/summaries/daily_reflection.py +55 -8
- package/src/superlocalmemory/summaries/project_work_log.py +23 -7
- package/src/superlocalmemory/summaries/session_summary.py +9 -5
- package/src/superlocalmemory/ui/index.html +9 -3
- package/src/superlocalmemory/ui/js/od-boundedloops.js +324 -0
- package/src/superlocalmemory/ui/js/od-memories.js +337 -12
- package/src/superlocalmemory/ui/js/od-mesh.js +97 -5
- package/src/superlocalmemory/ui/js/od-operations.js +1 -150
- package/src/superlocalmemory/ui/js/od-optimize.js +36 -9
- package/src/superlocalmemory/ui/js/od-shell.js +10 -0
|
@@ -39,8 +39,12 @@ from collections import Counter
|
|
|
39
39
|
from pathlib import Path
|
|
40
40
|
|
|
41
41
|
from .base import (
|
|
42
|
+
clean_llm_summary,
|
|
43
|
+
format_highlight,
|
|
44
|
+
SUMMARY_SYSTEM_PROMPT,
|
|
42
45
|
COVERAGE_FULL,
|
|
43
46
|
COVERAGE_INSUFFICIENT,
|
|
47
|
+
COVERAGE_PARTIAL,
|
|
44
48
|
COVERAGE_UNAVAILABLE,
|
|
45
49
|
GENERATED_BY_EXTRACTIVE,
|
|
46
50
|
GENERATED_BY_LLM_B,
|
|
@@ -121,8 +125,19 @@ def generate_project_work_log(
|
|
|
121
125
|
event_count = len(tool_rows)
|
|
122
126
|
fact_count = len(facts_rows)
|
|
123
127
|
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
# A project work log has two inputs: what was DONE (tool events) and what was
|
|
129
|
+
# LEARNED (facts). "full" means both were there.
|
|
130
|
+
#
|
|
131
|
+
# The previous rule was `events >= 1 OR facts >= 1`, which reported "full" for
|
|
132
|
+
# a project with 86 tool events and zero facts — rendered in the dashboard as
|
|
133
|
+
# "Built from 0 memories · coverage: full". Claiming full coverage of nothing
|
|
134
|
+
# is precisely the dishonest summary issue #113 exists to prevent.
|
|
135
|
+
if event_count >= _MIN_EVENTS and fact_count >= _MIN_FACTS:
|
|
136
|
+
coverage = COVERAGE_FULL
|
|
137
|
+
elif event_count >= _MIN_EVENTS or fact_count >= _MIN_FACTS:
|
|
138
|
+
coverage = COVERAGE_PARTIAL
|
|
139
|
+
else:
|
|
140
|
+
coverage = COVERAGE_INSUFFICIENT
|
|
126
141
|
|
|
127
142
|
extractive_content = _build_extractive_content(
|
|
128
143
|
project_path, tool_rows, facts_rows, event_count, fact_count
|
|
@@ -302,8 +317,7 @@ def _build_extractive_content(
|
|
|
302
317
|
lines.append("Key facts from project sessions:")
|
|
303
318
|
for f in facts_rows[:_TOP_FACTS]:
|
|
304
319
|
content = f.get("content", "")
|
|
305
|
-
|
|
306
|
-
content = content[:_MAX_FACT_CHARS - 3] + "..."
|
|
320
|
+
content = format_highlight(content)
|
|
307
321
|
lines.append(f" - {content}")
|
|
308
322
|
if fact_count > _TOP_FACTS:
|
|
309
323
|
lines.append(f" ... and {fact_count - _TOP_FACTS} more facts.")
|
|
@@ -373,6 +387,7 @@ def _call_ollama(
|
|
|
373
387
|
payload = json.dumps({
|
|
374
388
|
"model": model,
|
|
375
389
|
"prompt": full_prompt,
|
|
390
|
+
"system": SUMMARY_SYSTEM_PROMPT,
|
|
376
391
|
"stream": False,
|
|
377
392
|
"options": {"num_predict": 300},
|
|
378
393
|
}).encode()
|
|
@@ -383,7 +398,7 @@ def _call_ollama(
|
|
|
383
398
|
)
|
|
384
399
|
resp = urllib.request.urlopen(req, timeout=timeout)
|
|
385
400
|
data = json.loads(resp.read().decode())
|
|
386
|
-
text = data.get("response", "")
|
|
401
|
+
text = clean_llm_summary(data.get("response", ""))
|
|
387
402
|
return text if text and len(text) > 20 else None
|
|
388
403
|
except Exception as exc:
|
|
389
404
|
logger.debug("Ollama project work log failed: %s", exc)
|
|
@@ -414,11 +429,12 @@ def _call_cloud_llm(
|
|
|
414
429
|
)
|
|
415
430
|
text = llm.generate(
|
|
416
431
|
prompt=full_prompt,
|
|
417
|
-
system=
|
|
432
|
+
system=SUMMARY_SYSTEM_PROMPT,
|
|
418
433
|
max_tokens=300,
|
|
419
434
|
temperature=0.1,
|
|
420
435
|
)
|
|
421
|
-
|
|
436
|
+
cleaned = clean_llm_summary(text or "")
|
|
437
|
+
return cleaned if len(cleaned) > 20 else None
|
|
422
438
|
except Exception as exc:
|
|
423
439
|
logger.debug("Cloud LLM project work log failed: %s", exc)
|
|
424
440
|
return None
|
|
@@ -37,6 +37,9 @@ from datetime import date, timezone
|
|
|
37
37
|
from pathlib import Path
|
|
38
38
|
|
|
39
39
|
from .base import (
|
|
40
|
+
clean_llm_summary,
|
|
41
|
+
format_highlight,
|
|
42
|
+
SUMMARY_SYSTEM_PROMPT,
|
|
40
43
|
COVERAGE_FULL,
|
|
41
44
|
COVERAGE_INSUFFICIENT,
|
|
42
45
|
COVERAGE_NO_SESSION,
|
|
@@ -209,8 +212,7 @@ def _build_extractive_content(
|
|
|
209
212
|
]
|
|
210
213
|
for f in facts[:_BODY_FACTS]:
|
|
211
214
|
content = f.get("content", "")
|
|
212
|
-
|
|
213
|
-
content = content[:_MAX_FACT_CHARS - 3] + "..."
|
|
215
|
+
content = format_highlight(content)
|
|
214
216
|
lines.append(f" - {content}")
|
|
215
217
|
if fact_count > _BODY_FACTS:
|
|
216
218
|
lines.append(f" ... and {fact_count - _BODY_FACTS} more facts.")
|
|
@@ -263,6 +265,7 @@ def _call_ollama(
|
|
|
263
265
|
payload = json.dumps({
|
|
264
266
|
"model": model,
|
|
265
267
|
"prompt": full_prompt,
|
|
268
|
+
"system": SUMMARY_SYSTEM_PROMPT,
|
|
266
269
|
"stream": False,
|
|
267
270
|
"options": {"num_predict": 200},
|
|
268
271
|
}).encode()
|
|
@@ -273,7 +276,7 @@ def _call_ollama(
|
|
|
273
276
|
)
|
|
274
277
|
resp = urllib.request.urlopen(req, timeout=timeout)
|
|
275
278
|
data = json.loads(resp.read().decode())
|
|
276
|
-
text = data.get("response", "")
|
|
279
|
+
text = clean_llm_summary(data.get("response", ""))
|
|
277
280
|
return text if text and len(text) > 20 else None
|
|
278
281
|
except Exception as exc:
|
|
279
282
|
logger.debug("Ollama session summary failed: %s", exc)
|
|
@@ -297,11 +300,12 @@ def _call_cloud_llm(
|
|
|
297
300
|
full_prompt = f"{prompt}\n\nFacts:\n{fact_texts}\n\nRespond in 2-4 sentences."
|
|
298
301
|
text = llm.generate(
|
|
299
302
|
prompt=full_prompt,
|
|
300
|
-
system=
|
|
303
|
+
system=SUMMARY_SYSTEM_PROMPT,
|
|
301
304
|
max_tokens=200,
|
|
302
305
|
temperature=0.1,
|
|
303
306
|
)
|
|
304
|
-
|
|
307
|
+
cleaned = clean_llm_summary(text or "")
|
|
308
|
+
return cleaned if len(cleaned) > 20 else None
|
|
305
309
|
except Exception as exc:
|
|
306
310
|
logger.debug("Cloud LLM session summary failed: %s", exc)
|
|
307
311
|
return None
|
|
@@ -1017,6 +1017,11 @@
|
|
|
1017
1017
|
<div id="skill-detail-panel" style="display:none;margin-top:24px"></div>
|
|
1018
1018
|
</div>
|
|
1019
1019
|
|
|
1020
|
+
<!-- Bounded Loops (v4.0.8) — moved out of Governance: it is a
|
|
1021
|
+
separate product SLM optionally observes, i.e. an integration.
|
|
1022
|
+
Rendered entirely by od-boundedloops.js (window.odRenderLoops). -->
|
|
1023
|
+
<div class="tab-pane fade" id="loops-pane"></div>
|
|
1024
|
+
|
|
1020
1025
|
<!-- Mesh Peers (v3.4.3 — Neural Glass) -->
|
|
1021
1026
|
<div class="tab-pane fade" id="mesh-pane">
|
|
1022
1027
|
<div class="ng-content-header">
|
|
@@ -1599,13 +1604,14 @@
|
|
|
1599
1604
|
<script src="static/js/od-ops-health.js?v=400"></script>
|
|
1600
1605
|
<script src="static/js/od-team.js?v=379"></script>
|
|
1601
1606
|
<script src="static/js/od-graph.js?v=6812bf6c"></script>
|
|
1602
|
-
<script src="static/js/od-memories.js?v=
|
|
1607
|
+
<script src="static/js/od-memories.js?v=022ff653"></script>
|
|
1603
1608
|
<script src="static/js/od-entities.js?v=379"></script>
|
|
1604
1609
|
<!-- Multi-Agent Memory pane (v3.8.0): visualises memory written by multiple agents -->
|
|
1605
1610
|
<script src="static/js/od-agents.js?v=c75ff3c2"></script>
|
|
1606
1611
|
<script src="static/js/od-skills.js?v=379"></script>
|
|
1607
|
-
<script src="static/js/od-
|
|
1608
|
-
<script src="static/js/od-
|
|
1612
|
+
<script src="static/js/od-boundedloops.js?v=b4a014a1"></script>
|
|
1613
|
+
<script src="static/js/od-mesh.js?v=a7bc694a"></script>
|
|
1614
|
+
<script src="static/js/od-optimize.js?v=1f511698"></script>
|
|
1609
1615
|
<script src="static/js/od-settings.js?v=386"></script>
|
|
1610
1616
|
<script src="static/js/od-backup.js?v=379"></script>
|
|
1611
1617
|
<!-- MCP & Integrations pane (v3.8.0): shows exposed MCP tool profile + counts -->
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
// Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar
|
|
2
|
+
// Licensed under AGPL-3.0-or-later - see LICENSE file
|
|
3
|
+
// Part of SuperLocalMemory | https://qualixar.com
|
|
4
|
+
//
|
|
5
|
+
// Bounded Loops pane.
|
|
6
|
+
//
|
|
7
|
+
// Moved out of Governance in 4.0.8. Every other Governance tab governs SLM's
|
|
8
|
+
// OWN data — lifecycle, access, trust, compliance, ingestion. Bounded Loops
|
|
9
|
+
// governs none of it: it is a SEPARATE product that SLM optionally observes
|
|
10
|
+
// over the published contract bounded-loops.dev/slm-bridge/v1. Neither product
|
|
11
|
+
// depends on the other and installing either alone is complete, which makes
|
|
12
|
+
// this an integration, not a governance function.
|
|
13
|
+
//
|
|
14
|
+
// Reads GET /api/v3/bounded-loops/evidence for live bridge status and observed
|
|
15
|
+
// terminal runs. The guarantees are rendered FROM THE DATA rather than asserted
|
|
16
|
+
// in prose: every document carries eligible_for_learning=false, so the pane can
|
|
17
|
+
// show that SLM is not permitted to learn from these runs instead of merely
|
|
18
|
+
// promising that it does not.
|
|
19
|
+
|
|
20
|
+
(function () {
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
function esc(s) {
|
|
24
|
+
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
|
25
|
+
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function scaffold() {
|
|
30
|
+
return (
|
|
31
|
+
|
|
32
|
+
// ══════ BOUNDED LOOPS TAB ════════════════════════════════════════
|
|
33
|
+
// Static info card — no backend calls.
|
|
34
|
+
// A bounded loop advances only when an INDEPENDENT gate passes, not
|
|
35
|
+
// when the agent claims success. Laps are persisted as SLM memory
|
|
36
|
+
// (tag loop:<name>) so the full run history is queryable.
|
|
37
|
+
'<div>' +
|
|
38
|
+
|
|
39
|
+
// Live bridge status first: whether Bounded Loops is installed, the
|
|
40
|
+
// negotiated contract, and which runs were observed. The explanation
|
|
41
|
+
// below it is the same for everyone; this part is about YOUR install.
|
|
42
|
+
'<div class="card" style="margin-bottom:16px">' +
|
|
43
|
+
'<div class="card-head"><h3>Bridge status</h3>' +
|
|
44
|
+
'<span class="sub">observation only \u2014 never learns from these runs</span>' +
|
|
45
|
+
'</div>' +
|
|
46
|
+
'<div class="card-pad" id="od-bl-evidence">' +
|
|
47
|
+
'<div style="font-size:13px;color:var(--fg-2)">Checking bridge\u2026</div>' +
|
|
48
|
+
'</div>' +
|
|
49
|
+
'</div>' +
|
|
50
|
+
|
|
51
|
+
'<div class="page-head" style="margin-bottom:16px">' +
|
|
52
|
+
'<h2 style="font-size:20px;margin-bottom:6px">Bounded Loops</h2>' +
|
|
53
|
+
'<p style="font-size:13.5px">An iteration control pattern for agentic ' +
|
|
54
|
+
'frameworks — the loop advances only when an <strong>independent ' +
|
|
55
|
+
'gate</strong> passes, not when the agent claims success. Prevents ' +
|
|
56
|
+
'rationalisation: the model cannot self-certify completion.</p>' +
|
|
57
|
+
'</div>' +
|
|
58
|
+
|
|
59
|
+
// Two-column: concept + CLI reference
|
|
60
|
+
'<div class="grid" style="grid-template-columns:1fr 1fr;align-items:start;margin-bottom:16px">' +
|
|
61
|
+
|
|
62
|
+
'<div class="card">' +
|
|
63
|
+
'<div class="card-head"><h3>How it works</h3></div>' +
|
|
64
|
+
'<div class="card-pad">' +
|
|
65
|
+
'<p style="font-size:13px;line-height:1.6;margin-bottom:12px">' +
|
|
66
|
+
'Standard agentic loops let the model declare itself done — ' +
|
|
67
|
+
'a known failure mode when the model rationalises instead of verifying. ' +
|
|
68
|
+
'A bounded loop separates <em>execution</em> (the agent) from ' +
|
|
69
|
+
'<em>verification</em> (an independent gate such as a test suite, ' +
|
|
70
|
+
'linter, or judge LLM).' +
|
|
71
|
+
'</p>' +
|
|
72
|
+
'<p style="font-size:13px;line-height:1.6;margin-bottom:12px">' +
|
|
73
|
+
'The loop terminates only when the gate returns <code>DONE</code>, ' +
|
|
74
|
+
'or when a hard lap cap is reached (<code>HALT</code>). Each lap is ' +
|
|
75
|
+
'persisted as a queryable SLM memory tagged ' +
|
|
76
|
+
'<code>loop:<name></code>.' +
|
|
77
|
+
'</p>' +
|
|
78
|
+
'<div style="background:var(--card-2);border-radius:var(--r-md);' +
|
|
79
|
+
'padding:10px 14px;font-size:12.5px;line-height:1.7">' +
|
|
80
|
+
'<div><span class="badge ok" style="margin-right:8px">DONE</span>' +
|
|
81
|
+
'Gate passed — loop succeeded cleanly</div>' +
|
|
82
|
+
'<div style="margin-top:6px"><span class="badge warn" style="margin-right:8px">HALT</span>' +
|
|
83
|
+
'Lap cap reached — hard stop applied</div>' +
|
|
84
|
+
'<div style="margin-top:6px"><span class="badge cyan" style="margin-right:8px">PAUSE</span>' +
|
|
85
|
+
'Awaiting external input or approval</div>' +
|
|
86
|
+
'<div style="margin-top:6px"><span class="badge danger" style="margin-right:8px">KILLED</span>' +
|
|
87
|
+
'Manually stopped by the operator</div>' +
|
|
88
|
+
'<div style="margin-top:6px"><span class="badge neutral" style="margin-right:8px">ERROR</span>' +
|
|
89
|
+
'Unrecoverable failure during a lap</div>' +
|
|
90
|
+
'</div>' +
|
|
91
|
+
'</div>' +
|
|
92
|
+
'</div>' +
|
|
93
|
+
|
|
94
|
+
'<div class="card">' +
|
|
95
|
+
'<div class="card-head"><h3>Run it: CLI · command · MCP</h3></div>' +
|
|
96
|
+
'<div class="card-pad">' +
|
|
97
|
+
'<p style="font-size:13px;margin-bottom:14px">' +
|
|
98
|
+
'Bounded loops ship on three surfaces — the same engine and ' +
|
|
99
|
+
'the same queryable ledger behind each.' +
|
|
100
|
+
'</p>' +
|
|
101
|
+
|
|
102
|
+
'<div style="font-size:12px;font-weight:600;color:var(--fg-2);margin-bottom:6px">CLI</div>' +
|
|
103
|
+
'<div style="display:flex;flex-direction:column;gap:8px;margin-bottom:14px">' +
|
|
104
|
+
'<div class="list-row">' +
|
|
105
|
+
'<span class="mono" style="min-width:190px;font-size:13px">slm loop demo</span>' +
|
|
106
|
+
'<span style="font-size:12.5px;color:var(--fg-2)">Run a live demo bounded loop</span>' +
|
|
107
|
+
'</div>' +
|
|
108
|
+
'<div class="list-row">' +
|
|
109
|
+
'<span class="mono" style="min-width:190px;font-size:13px">slm loop history</span>' +
|
|
110
|
+
'<span style="font-size:12.5px;color:var(--fg-2)">List loop runs for this profile</span>' +
|
|
111
|
+
'</div>' +
|
|
112
|
+
'<div class="list-row">' +
|
|
113
|
+
'<span class="mono" style="min-width:190px;font-size:13px">slm loop show <run_id></span>' +
|
|
114
|
+
'<span style="font-size:12.5px;color:var(--fg-2)">Inspect a run lap-by-lap</span>' +
|
|
115
|
+
'</div>' +
|
|
116
|
+
'</div>' +
|
|
117
|
+
|
|
118
|
+
'<div style="font-size:12px;font-weight:600;color:var(--fg-2);margin-bottom:6px">Command (Claude Code / Codex)</div>' +
|
|
119
|
+
'<div style="display:flex;flex-direction:column;gap:8px;margin-bottom:14px">' +
|
|
120
|
+
'<div class="list-row">' +
|
|
121
|
+
'<span class="mono" style="min-width:190px;font-size:13px">/slm-loop</span>' +
|
|
122
|
+
'<span style="font-size:12.5px;color:var(--fg-2)">Slash command bound to the slm-loop skill + runner agent</span>' +
|
|
123
|
+
'</div>' +
|
|
124
|
+
'</div>' +
|
|
125
|
+
|
|
126
|
+
'<div style="font-size:12px;font-weight:600;color:var(--fg-2);margin-bottom:6px">MCP tools (code / full / power profiles)</div>' +
|
|
127
|
+
'<div style="display:flex;flex-direction:column;gap:8px">' +
|
|
128
|
+
'<div class="list-row">' +
|
|
129
|
+
'<span class="mono" style="min-width:190px;font-size:13px">slm_loop_run</span>' +
|
|
130
|
+
'<span style="font-size:12.5px;color:var(--fg-2)">Run a gated loop — the gate is an independent SLM recall</span>' +
|
|
131
|
+
'</div>' +
|
|
132
|
+
'<div class="list-row">' +
|
|
133
|
+
'<span class="mono" style="min-width:190px;font-size:13px">slm_loop_history</span>' +
|
|
134
|
+
'<span style="font-size:12.5px;color:var(--fg-2)">List runs (read-only)</span>' +
|
|
135
|
+
'</div>' +
|
|
136
|
+
'<div class="list-row">' +
|
|
137
|
+
'<span class="mono" style="min-width:190px;font-size:13px">slm_loop_show</span>' +
|
|
138
|
+
'<span style="font-size:12.5px;color:var(--fg-2)">Show a run lap-by-lap (read-only)</span>' +
|
|
139
|
+
'</div>' +
|
|
140
|
+
'</div>' +
|
|
141
|
+
'<div style="margin-top:18px;padding:10px 14px;background:var(--card-2);' +
|
|
142
|
+
'border-radius:var(--r-md);font-size:12.5px;line-height:1.6">' +
|
|
143
|
+
'<b>Memory tagging:</b> each lap is stored with tag ' +
|
|
144
|
+
'<code>loop:<name></code>. Recall the full history with ' +
|
|
145
|
+
'<br><code>slm recall --tag loop:my-loop-name</code>' +
|
|
146
|
+
'</div>' +
|
|
147
|
+
'</div>' +
|
|
148
|
+
'</div>' +
|
|
149
|
+
|
|
150
|
+
'</div>' + // end two-column grid
|
|
151
|
+
|
|
152
|
+
// Framework adapters note
|
|
153
|
+
'<div class="card">' +
|
|
154
|
+
'<div class="card-head"><h3>Framework adapters</h3></div>' +
|
|
155
|
+
'<div class="card-pad">' +
|
|
156
|
+
'<p style="font-size:13px;line-height:1.6;margin-bottom:14px">' +
|
|
157
|
+
'Bounded loops integrate with any agentic framework that supports ' +
|
|
158
|
+
'tool-call round-trips. The gate is an ordinary SLM memory check — ' +
|
|
159
|
+
'no special framework wiring required. Each framework stamps ' +
|
|
160
|
+
'<code>SLM_AGENT_ID</code> so lap history is attribution-aware.' +
|
|
161
|
+
'</p>' +
|
|
162
|
+
'<div style="display:flex;flex-wrap:wrap;gap:8px">' +
|
|
163
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
164
|
+
'background:var(--card-2);color:var(--fg-2)">CrewAI</span>' +
|
|
165
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
166
|
+
'background:var(--card-2);color:var(--fg-2)">LangChain</span>' +
|
|
167
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
168
|
+
'background:var(--card-2);color:var(--fg-2)">LangGraph</span>' +
|
|
169
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
170
|
+
'background:var(--card-2);color:var(--fg-2)">Semantic Kernel</span>' +
|
|
171
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
172
|
+
'background:var(--card-2);color:var(--fg-2)">LlamaIndex</span>' +
|
|
173
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
174
|
+
'background:var(--card-2);color:var(--fg-2)">Microsoft Agent Framework</span>' +
|
|
175
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
176
|
+
'background:var(--card-2);color:var(--fg-2)">AutoGen</span>' +
|
|
177
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
178
|
+
'background:var(--card-2);color:var(--fg-2)">Google ADK</span>' +
|
|
179
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
180
|
+
'background:var(--card-2);color:var(--fg-2)">OpenAI Agents</span>' +
|
|
181
|
+
'<span style="padding:4px 12px;border-radius:99px;font-size:12px;' +
|
|
182
|
+
'background:var(--card-2);color:var(--fg-2)">Any MCP-compatible agent</span>' +
|
|
183
|
+
'</div>' +
|
|
184
|
+
'<p style="margin-top:12px;font-size:12.5px;color:var(--fg-2)">' +
|
|
185
|
+
'Learn the full pattern: run <code>/slm-loop</code> (the slm-loop skill) ' +
|
|
186
|
+
'inside Claude Code for an interactive walkthrough with live examples.' +
|
|
187
|
+
'</p>' +
|
|
188
|
+
'</div>' +
|
|
189
|
+
'</div>' +
|
|
190
|
+
|
|
191
|
+
'</div>'
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Bounded Loops bridge status + observed runs.
|
|
196
|
+
*
|
|
197
|
+
* Until 4.0.8 this tab was a static essay about loop gating. It said nothing
|
|
198
|
+
* about the bridge, which is the part that concerns SLM: Bounded Loops is a
|
|
199
|
+
* SEPARATE product, and what SLM does with it is import read-only evidence
|
|
200
|
+
* over the published contract bounded-loops.dev/slm-bridge/v1.
|
|
201
|
+
*
|
|
202
|
+
* The guarantees are the point, and they must be stated from the DATA rather
|
|
203
|
+
* than asserted in prose — `eligible_for_learning` is a hard field in every
|
|
204
|
+
* document, always false in v1, so the pane can show that SLM is not allowed
|
|
205
|
+
* to learn from these runs rather than merely promising it doesn't.
|
|
206
|
+
*/
|
|
207
|
+
var _blLoaded = false;
|
|
208
|
+
function loadEvidence() {
|
|
209
|
+
var box = document.getElementById('od-bl-evidence');
|
|
210
|
+
if (!box || _blLoaded) return;
|
|
211
|
+
_blLoaded = true;
|
|
212
|
+
|
|
213
|
+
fetch('/internal/token', { credentials: 'same-origin' })
|
|
214
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
215
|
+
.catch(function () { return null; })
|
|
216
|
+
.then(function (t) {
|
|
217
|
+
var tok = t && (t.token || t.install_token);
|
|
218
|
+
return fetch('/api/v3/bounded-loops/evidence', {
|
|
219
|
+
credentials: 'same-origin',
|
|
220
|
+
headers: tok ? { 'X-Install-Token': tok } : {},
|
|
221
|
+
});
|
|
222
|
+
})
|
|
223
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
224
|
+
.then(function (d) {
|
|
225
|
+
if (!d) { box.innerHTML = _blNote('Bridge status unavailable.'); return; }
|
|
226
|
+
box.innerHTML = renderBoundedLoopsEvidence(d);
|
|
227
|
+
})
|
|
228
|
+
.catch(function () {
|
|
229
|
+
_blLoaded = false; // allow a retry on the next tab visit
|
|
230
|
+
box.innerHTML = _blNote('Could not read bridge status.');
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function _blNote(msg) {
|
|
235
|
+
return '<div style="font-size:13px;color:var(--fg-2)">' + esc(msg) + '</div>';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function renderBoundedLoopsEvidence(d) {
|
|
239
|
+
var installed = d.installed;
|
|
240
|
+
var runs = d.runs || [];
|
|
241
|
+
var badge = installed === true
|
|
242
|
+
? '<span class="badge ok">installed</span>'
|
|
243
|
+
: (installed === false
|
|
244
|
+
? '<span class="badge neutral">not installed</span>'
|
|
245
|
+
: '<span class="badge warn">unknown</span>');
|
|
246
|
+
|
|
247
|
+
var head =
|
|
248
|
+
'<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px">' +
|
|
249
|
+
badge +
|
|
250
|
+
'<span class="mono" style="font-size:12px;color:var(--fg-2)">' +
|
|
251
|
+
esc(d.contract || '') + '</span>' +
|
|
252
|
+
'</div>';
|
|
253
|
+
|
|
254
|
+
// Optional by design — "not installed" is a normal, complete state.
|
|
255
|
+
if (installed === false) {
|
|
256
|
+
return head + '<p style="font-size:13px;line-height:1.6;color:var(--fg-2)">' +
|
|
257
|
+
'Bounded Loops is a separate product and SuperLocalMemory does not ' +
|
|
258
|
+
'require it. Install it and SLM can import read-only evidence about ' +
|
|
259
|
+
'finished runs — it never sends anything back.</p>';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
var guarantees =
|
|
263
|
+
'<div style="background:var(--card-2);border-radius:var(--r-md);' +
|
|
264
|
+
'padding:12px 14px;font-size:12.5px;line-height:1.75;margin-bottom:12px">' +
|
|
265
|
+
'<div><strong>Observation only.</strong> Evidence records what a run did. ' +
|
|
266
|
+
'It never authorises SLM to learn from it, re-rank memories, or change ' +
|
|
267
|
+
'routing — every document carries <code>eligible_for_learning: false</code>.</div>' +
|
|
268
|
+
'<div style="margin-top:6px"><strong>No paths leave the workspace.</strong> ' +
|
|
269
|
+
'Locations travel as digests, so a project directory name never reaches ' +
|
|
270
|
+
'the memory store. Gate text and artifact contents are excluded at source.</div>' +
|
|
271
|
+
'<div style="margin-top:6px"><strong>Tamper-evident, not verified.</strong> ' +
|
|
272
|
+
'Receipts form a local append-only hash chain (<code>local_hash_chain_only</code>). ' +
|
|
273
|
+
'That makes edits detectable — it is not authentication or independent audit.</div>' +
|
|
274
|
+
'</div>';
|
|
275
|
+
|
|
276
|
+
if (!runs.length) {
|
|
277
|
+
return head + guarantees +
|
|
278
|
+
'<p style="font-size:13px;color:var(--fg-2)">' +
|
|
279
|
+
'No runs observed yet. Import one with the <code>observe_bounded_loop_evidence</code> ' +
|
|
280
|
+
'tool — nothing is imported automatically.</p>';
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
var demoNote = d.demonstration_count
|
|
284
|
+
? '<div style="font-size:12px;color:var(--fg-3);margin-bottom:8px">' +
|
|
285
|
+
esc(String(d.demonstration_count)) + ' of ' + esc(String(d.total)) +
|
|
286
|
+
' observed run' + (d.total === 1 ? '' : 's') + ' ' +
|
|
287
|
+
(d.demonstration_count === 1 ? 'is a demonstration' : 'are demonstrations') +
|
|
288
|
+
' — wiring proofs, not real work.</div>'
|
|
289
|
+
: '';
|
|
290
|
+
|
|
291
|
+
var rows = runs.map(function (r) {
|
|
292
|
+
var cls = r.outcome === 'SUCCEEDED' ? 'ok'
|
|
293
|
+
: (r.outcome === 'CANCELLED' ? 'neutral' : 'danger');
|
|
294
|
+
// run_state, not just outcome: HALTED (budget/policy stop) and FAILED
|
|
295
|
+
// (gate rejected the work) are different events and both map to FAILED.
|
|
296
|
+
var state = r.run_state && r.run_state !== r.outcome
|
|
297
|
+
? ' <span style="color:var(--fg-3)">(' + esc(r.run_state) + ')</span>' : '';
|
|
298
|
+
// min-width:0 + ellipsis: a run ref is a single unbroken token, and in a
|
|
299
|
+
// narrow pane `flex:1` alone shreds it one character per line
|
|
300
|
+
// ("demo / — / proo / f"). Truncate with the full value in the tooltip.
|
|
301
|
+
return '<div class="list-row" style="align-items:baseline;gap:8px">' +
|
|
302
|
+
'<span class="mono" title="' + esc(r.run_ref || r.run_id) + '" ' +
|
|
303
|
+
'style="flex:1;min-width:0;font-size:12.5px;overflow:hidden;' +
|
|
304
|
+
'text-overflow:ellipsis;white-space:nowrap">' +
|
|
305
|
+
esc(r.run_ref || r.run_id) + '</span>' +
|
|
306
|
+
(r.demonstration
|
|
307
|
+
? '<span class="badge neutral" style="margin-right:8px">demo</span>' : '') +
|
|
308
|
+
'<span class="badge ' + cls + '" style="margin-right:8px">' + esc(r.outcome) + '</span>' +
|
|
309
|
+
'<span style="font-size:12px;color:var(--fg-2)">' + state + '</span>' +
|
|
310
|
+
'<span style="font-size:12px;color:var(--fg-3);margin-left:10px">seq ' +
|
|
311
|
+
esc(String(r.receipt_sequence)) + '</span>' +
|
|
312
|
+
'</div>';
|
|
313
|
+
}).join('');
|
|
314
|
+
|
|
315
|
+
return head + guarantees + demoNote + rows;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
window.odRenderLoops = function (container) {
|
|
319
|
+
if (!container) return;
|
|
320
|
+
container.innerHTML = scaffold();
|
|
321
|
+
_blLoaded = false;
|
|
322
|
+
loadEvidence();
|
|
323
|
+
};
|
|
324
|
+
})();
|