superlocalmemory 3.7.7 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/ATTRIBUTION.md +1 -3
  3. package/CHANGELOG.md +85 -0
  4. package/README.md +199 -29
  5. package/package.json +4 -2
  6. package/plugin/.claude-plugin/plugin.json +2 -2
  7. package/plugin/CLAUDE.md +8 -8
  8. package/plugin/agents/slm-governance-advisor.md +80 -0
  9. package/plugin/agents/slm-loop-runner.md +71 -0
  10. package/plugin/agents/slm-memory-advisor.md +10 -5
  11. package/plugin/agents/slm-optimize-advisor.md +9 -3
  12. package/plugin/commands/slm-loop.md +31 -0
  13. package/plugin/hooks/hooks.json +79 -0
  14. package/plugin/requirements.txt +1 -1
  15. package/plugin/scripts/slm-launch +46 -7
  16. package/plugin/settings.json +9 -0
  17. package/plugin/skills/slm-cache/SKILL.md +9 -1
  18. package/plugin/skills/slm-compress/SKILL.md +8 -1
  19. package/plugin/skills/slm-governance/SKILL.md +248 -0
  20. package/plugin/skills/slm-graph/SKILL.md +17 -3
  21. package/plugin/skills/slm-loop/SKILL.md +99 -0
  22. package/plugin/skills/slm-mesh/SKILL.md +282 -0
  23. package/plugin/skills/slm-profile/SKILL.md +148 -0
  24. package/plugin/skills/slm-recall/SKILL.md +46 -10
  25. package/plugin/skills/slm-remember/SKILL.md +48 -1
  26. package/plugin/skills/slm-scope/SKILL.md +176 -0
  27. package/plugin/skills/slm-session/SKILL.md +24 -1
  28. package/plugin/skills/slm-status/SKILL.md +18 -1
  29. package/plugin-src/agents/slm-governance-advisor.md +80 -0
  30. package/plugin-src/agents/slm-loop-runner.md +71 -0
  31. package/plugin-src/agents/slm-memory-advisor.md +10 -5
  32. package/plugin-src/agents/slm-optimize-advisor.md +9 -3
  33. package/plugin-src/commands/slm-loop.md +31 -0
  34. package/plugin-src/hooks/hooks.json +79 -0
  35. package/plugin-src/manifest.json +7 -2
  36. package/plugin-src/requirements.txt +1 -1
  37. package/plugin-src/rules/AGENTS.md +57 -18
  38. package/plugin-src/rules/CLAUDE.md.fragment +8 -8
  39. package/plugin-src/scripts/slm-launch +46 -7
  40. package/plugin-src/settings.json +9 -0
  41. package/plugin-src/skills/slm-cache/SKILL.md +9 -1
  42. package/plugin-src/skills/slm-compress/SKILL.md +8 -1
  43. package/plugin-src/skills/slm-governance/SKILL.md +248 -0
  44. package/plugin-src/skills/slm-graph/SKILL.md +17 -3
  45. package/plugin-src/skills/slm-loop/SKILL.md +99 -0
  46. package/plugin-src/skills/slm-mesh/SKILL.md +282 -0
  47. package/plugin-src/skills/slm-profile/SKILL.md +148 -0
  48. package/plugin-src/skills/slm-recall/SKILL.md +46 -10
  49. package/plugin-src/skills/slm-remember/SKILL.md +48 -1
  50. package/plugin-src/skills/slm-scope/SKILL.md +176 -0
  51. package/plugin-src/skills/slm-session/SKILL.md +24 -1
  52. package/plugin-src/skills/slm-status/SKILL.md +18 -1
  53. package/pyproject.toml +1 -2
  54. package/scripts/postinstall/validation.js +2 -0
  55. package/scripts/postinstall-interactive.js +74 -2
  56. package/src/superlocalmemory/__init__.py +1 -1
  57. package/src/superlocalmemory/access/__init__.py +3 -0
  58. package/src/superlocalmemory/access/rbac.py +477 -0
  59. package/src/superlocalmemory/cli/commands.py +96 -12
  60. package/src/superlocalmemory/cli/compress_cmd.py +17 -7
  61. package/src/superlocalmemory/cli/loop_cmd.py +192 -0
  62. package/src/superlocalmemory/cli/main.py +39 -4
  63. package/src/superlocalmemory/cli/mesh_cmd.py +38 -0
  64. package/src/superlocalmemory/cli/optimize_cmd.py +3 -0
  65. package/src/superlocalmemory/cli/pending_store.py +49 -13
  66. package/src/superlocalmemory/cli/proxy_cmd.py +4 -0
  67. package/src/superlocalmemory/cli/scale_engine_cmd.py +6 -0
  68. package/src/superlocalmemory/cli/setup_wizard.py +22 -13
  69. package/src/superlocalmemory/compliance/audit.py +6 -0
  70. package/src/superlocalmemory/compliance/gdpr.py +128 -138
  71. package/src/superlocalmemory/compliance/retention.py +176 -45
  72. package/src/superlocalmemory/core/backend_orchestrator.py +5 -43
  73. package/src/superlocalmemory/core/community_summary.py +267 -0
  74. package/src/superlocalmemory/core/config.py +216 -3
  75. package/src/superlocalmemory/core/consolidation_engine.py +95 -22
  76. package/src/superlocalmemory/core/context_cache.py +61 -18
  77. package/src/superlocalmemory/core/embedding_worker.py +17 -2
  78. package/src/superlocalmemory/core/embeddings.py +12 -1
  79. package/src/superlocalmemory/core/engine.py +17 -1
  80. package/src/superlocalmemory/core/engine_ingestion.py +29 -0
  81. package/src/superlocalmemory/core/engine_wiring.py +13 -0
  82. package/src/superlocalmemory/core/entity_community.py +178 -0
  83. package/src/superlocalmemory/core/graph_analyzer.py +39 -2
  84. package/src/superlocalmemory/core/graph_pruner.py +13 -8
  85. package/src/superlocalmemory/core/key_expander.py +138 -0
  86. package/src/superlocalmemory/core/maintenance.py +23 -0
  87. package/src/superlocalmemory/core/modes.py +1 -1
  88. package/src/superlocalmemory/core/mutations.py +2 -2
  89. package/src/superlocalmemory/core/pii.py +105 -0
  90. package/src/superlocalmemory/core/progressive_abstraction.py +208 -0
  91. package/src/superlocalmemory/core/recall_pipeline.py +2 -0
  92. package/src/superlocalmemory/core/recall_worker.py +20 -6
  93. package/src/superlocalmemory/core/scale_engine.py +60 -1
  94. package/src/superlocalmemory/core/security_primitives.py +40 -2
  95. package/src/superlocalmemory/core/store_pipeline.py +35 -11
  96. package/src/superlocalmemory/core/worker_pool.py +21 -6
  97. package/src/superlocalmemory/encoding/entity_reflexion.py +200 -0
  98. package/src/superlocalmemory/encoding/entity_resolver.py +34 -24
  99. package/src/superlocalmemory/encoding/fact_extractor.py +26 -1
  100. package/src/superlocalmemory/encoding/temporal_validator.py +64 -1
  101. package/src/superlocalmemory/evolution/evolution_store.py +122 -45
  102. package/src/superlocalmemory/evolution/llm_dispatch.py +12 -1
  103. package/src/superlocalmemory/evolution/model_selection.py +160 -0
  104. package/src/superlocalmemory/evolution/mutation_generator.py +16 -0
  105. package/src/superlocalmemory/evolution/skill_evolver.py +127 -42
  106. package/src/superlocalmemory/evolution/triggers.py +22 -13
  107. package/src/superlocalmemory/graph/cozo_backend.py +43 -20
  108. package/src/superlocalmemory/hooks/adapter_base.py +5 -1
  109. package/src/superlocalmemory/hooks/auto_recall.py +13 -1
  110. package/src/superlocalmemory/hooks/claude_code_hooks.py +11 -0
  111. package/src/superlocalmemory/hooks/codex_assets.py +64 -5
  112. package/src/superlocalmemory/hooks/hook_daemon.py +20 -3
  113. package/src/superlocalmemory/hooks/memory_protocol.py +54 -0
  114. package/src/superlocalmemory/hooks/portable_kit.py +114 -1
  115. package/src/superlocalmemory/infra/auth_middleware.py +28 -0
  116. package/src/superlocalmemory/infra/backup.py +12 -1
  117. package/src/superlocalmemory/infra/daemon_identity.py +40 -4
  118. package/src/superlocalmemory/infra/data_root.py +43 -4
  119. package/src/superlocalmemory/infra/event_bus.py +107 -24
  120. package/src/superlocalmemory/infra/rate_limiter.py +93 -0
  121. package/src/superlocalmemory/ingestion/adapter_manager.py +4 -1
  122. package/src/superlocalmemory/ingestion/credentials.py +1 -1
  123. package/src/superlocalmemory/learning/cross_project.py +28 -19
  124. package/src/superlocalmemory/learning/reward_proxy.py +42 -9
  125. package/src/superlocalmemory/loops/__init__.py +56 -0
  126. package/src/superlocalmemory/loops/budget.py +58 -0
  127. package/src/superlocalmemory/loops/engine.py +164 -0
  128. package/src/superlocalmemory/loops/ledger.py +243 -0
  129. package/src/superlocalmemory/loops/models.py +152 -0
  130. package/src/superlocalmemory/loops/rules.py +52 -0
  131. package/src/superlocalmemory/mcp/_daemon_proxy.py +3 -0
  132. package/src/superlocalmemory/mcp/_pool_adapter.py +2 -0
  133. package/src/superlocalmemory/mcp/profiles.py +103 -0
  134. package/src/superlocalmemory/mcp/server.py +21 -49
  135. package/src/superlocalmemory/mcp/tools_active.py +4 -7
  136. package/src/superlocalmemory/mcp/tools_code_graph.py +51 -5
  137. package/src/superlocalmemory/mcp/tools_core.py +50 -5
  138. package/src/superlocalmemory/mcp/tools_evolution.py +6 -3
  139. package/src/superlocalmemory/mcp/tools_loops.py +300 -0
  140. package/src/superlocalmemory/mcp/tools_mesh.py +140 -4
  141. package/src/superlocalmemory/mcp/tools_optimize.py +15 -8
  142. package/src/superlocalmemory/mesh/broker.py +237 -129
  143. package/src/superlocalmemory/mesh/remote_sync.py +50 -8
  144. package/src/superlocalmemory/optimize/NOTICE +1 -6
  145. package/src/superlocalmemory/optimize/adapters/anthropic_adapter.py +1 -4
  146. package/src/superlocalmemory/optimize/adapters/openai_adapter.py +1 -4
  147. package/src/superlocalmemory/optimize/cache/semantic.py +27 -19
  148. package/src/superlocalmemory/optimize/compress/align.py +32 -26
  149. package/src/superlocalmemory/optimize/compress/ccr.py +14 -71
  150. package/src/superlocalmemory/optimize/compress/router.py +105 -22
  151. package/src/superlocalmemory/optimize/config/defaults.py +1 -1
  152. package/src/superlocalmemory/optimize/config/schema.py +87 -4
  153. package/src/superlocalmemory/optimize/metrics/counters.py +13 -4
  154. package/src/superlocalmemory/optimize/metrics/estimator.py +0 -3
  155. package/src/superlocalmemory/optimize/proxy/_helpers.py +31 -4
  156. package/src/superlocalmemory/optimize/storage/db.py +38 -9
  157. package/src/superlocalmemory/optimize/storage/schema.py +10 -0
  158. package/src/superlocalmemory/parameterization/pattern_extractor.py +6 -3
  159. package/src/superlocalmemory/retrieval/agentic.py +1 -1
  160. package/src/superlocalmemory/retrieval/bm25_channel.py +68 -10
  161. package/src/superlocalmemory/retrieval/engine.py +168 -26
  162. package/src/superlocalmemory/retrieval/entity_channel.py +7 -5
  163. package/src/superlocalmemory/retrieval/hopfield_channel.py +9 -2
  164. package/src/superlocalmemory/retrieval/semantic_channel.py +114 -21
  165. package/src/superlocalmemory/retrieval/spreading_activation.py +11 -2
  166. package/src/superlocalmemory/retrieval/temporal_channel.py +48 -9
  167. package/src/superlocalmemory/retrieval/temporal_frame.py +102 -0
  168. package/src/superlocalmemory/retrieval/temporal_validity_filter.py +135 -0
  169. package/src/superlocalmemory/retrieval/time_window.py +181 -0
  170. package/src/superlocalmemory/server/api.py +21 -4
  171. package/src/superlocalmemory/server/profile_runtime.py +125 -8
  172. package/src/superlocalmemory/server/rbac_enforce.py +142 -0
  173. package/src/superlocalmemory/server/recall_health.py +24 -3
  174. package/src/superlocalmemory/server/recall_serializer.py +19 -1
  175. package/src/superlocalmemory/server/routes/abstraction.py +115 -0
  176. package/src/superlocalmemory/server/routes/agents.py +128 -38
  177. package/src/superlocalmemory/server/routes/backup.py +34 -10
  178. package/src/superlocalmemory/server/routes/behavioral.py +13 -12
  179. package/src/superlocalmemory/server/routes/brain.py +21 -5
  180. package/src/superlocalmemory/server/routes/chat.py +72 -16
  181. package/src/superlocalmemory/server/routes/compliance.py +171 -21
  182. package/src/superlocalmemory/server/routes/config_api.py +436 -0
  183. package/src/superlocalmemory/server/routes/data_io.py +30 -8
  184. package/src/superlocalmemory/server/routes/entity.py +9 -4
  185. package/src/superlocalmemory/server/routes/events.py +24 -8
  186. package/src/superlocalmemory/server/routes/evolution.py +135 -17
  187. package/src/superlocalmemory/server/routes/helpers.py +16 -1
  188. package/src/superlocalmemory/server/routes/ingest.py +7 -4
  189. package/src/superlocalmemory/server/routes/insights.py +3 -3
  190. package/src/superlocalmemory/server/routes/learning.py +14 -14
  191. package/src/superlocalmemory/server/routes/lifecycle.py +59 -8
  192. package/src/superlocalmemory/server/routes/memories.py +221 -49
  193. package/src/superlocalmemory/server/routes/mesh.py +95 -15
  194. package/src/superlocalmemory/server/routes/optimize.py +33 -1
  195. package/src/superlocalmemory/server/routes/prewarm.py +2 -0
  196. package/src/superlocalmemory/server/routes/profiles.py +63 -17
  197. package/src/superlocalmemory/server/routes/ratelimit.py +124 -0
  198. package/src/superlocalmemory/server/routes/rbac.py +367 -0
  199. package/src/superlocalmemory/server/routes/stats.py +13 -6
  200. package/src/superlocalmemory/server/routes/tiers.py +11 -9
  201. package/src/superlocalmemory/server/routes/v3_api.py +194 -81
  202. package/src/superlocalmemory/server/routes/ws.py +5 -2
  203. package/src/superlocalmemory/server/security_middleware.py +12 -5
  204. package/src/superlocalmemory/server/ui.py +30 -5
  205. package/src/superlocalmemory/server/unified_daemon.py +431 -75
  206. package/src/superlocalmemory/server/write_identity.py +38 -8
  207. package/src/superlocalmemory/storage/database.py +265 -53
  208. package/src/superlocalmemory/storage/migration_runner.py +53 -0
  209. package/src/superlocalmemory/storage/migrations/M021_ingestion_log_profile.py +108 -0
  210. package/src/superlocalmemory/storage/migrations/M022_entity_aliases_profile.py +86 -0
  211. package/src/superlocalmemory/storage/migrations/M023_mesh_profile_isolation.py +194 -0
  212. package/src/superlocalmemory/storage/migrations/M024_rbac_users_roles.py +87 -0
  213. package/src/superlocalmemory/storage/migrations/M025_perf_indexes.py +90 -0
  214. package/src/superlocalmemory/storage/migrations/M026_rbac_memberships_fk.py +136 -0
  215. package/src/superlocalmemory/storage/migrations/M027_transferable_patterns_profile.py +163 -0
  216. package/src/superlocalmemory/storage/models.py +4 -0
  217. package/src/superlocalmemory/storage/schema.py +87 -0
  218. package/src/superlocalmemory/storage/schema_v32.py +0 -9
  219. package/src/superlocalmemory/storage/schema_v343.py +24 -12
  220. package/src/superlocalmemory/trust/gate.py +49 -8
  221. package/src/superlocalmemory/ui/assets/slm-icon-white.svg +64 -0
  222. package/src/superlocalmemory/ui/assets/slm-icon.svg +36 -0
  223. package/src/superlocalmemory/ui/css/design-system.css +621 -0
  224. package/src/superlocalmemory/ui/css/neural-glass.css +6 -0
  225. package/src/superlocalmemory/ui/css/od-bridge.css +158 -0
  226. package/src/superlocalmemory/ui/favicon.svg +35 -4
  227. package/src/superlocalmemory/ui/index.html +306 -173
  228. package/src/superlocalmemory/ui/js/brain.js +5 -20
  229. package/src/superlocalmemory/ui/js/core.js +47 -31
  230. package/src/superlocalmemory/ui/js/dashboard.js +314 -63
  231. package/src/superlocalmemory/ui/js/event-delegation.js +102 -0
  232. package/src/superlocalmemory/ui/js/knowledge-graph.js +11 -11
  233. package/src/superlocalmemory/ui/js/math-health.js +1 -1
  234. package/src/superlocalmemory/ui/js/memories.js +15 -4
  235. package/src/superlocalmemory/ui/js/memory-chat.js +7 -7
  236. package/src/superlocalmemory/ui/js/ng-entities.js +6 -8
  237. package/src/superlocalmemory/ui/js/ng-ingestion.js +4 -4
  238. package/src/superlocalmemory/ui/js/ng-mesh.js +4 -9
  239. package/src/superlocalmemory/ui/js/ng-shell.js +8 -8
  240. package/src/superlocalmemory/ui/js/ng-skills.js +54 -2
  241. package/src/superlocalmemory/ui/js/od-agents.js +544 -0
  242. package/src/superlocalmemory/ui/js/od-auth-gate.js +257 -0
  243. package/src/superlocalmemory/ui/js/od-backup.js +780 -0
  244. package/src/superlocalmemory/ui/js/od-brain.js +779 -0
  245. package/src/superlocalmemory/ui/js/od-entities.js +579 -0
  246. package/src/superlocalmemory/ui/js/od-graph.js +593 -0
  247. package/src/superlocalmemory/ui/js/od-health.js +539 -0
  248. package/src/superlocalmemory/ui/js/od-mcp.js +508 -0
  249. package/src/superlocalmemory/ui/js/od-memories.js +887 -0
  250. package/src/superlocalmemory/ui/js/od-mesh.js +539 -0
  251. package/src/superlocalmemory/ui/js/od-operations.js +1250 -0
  252. package/src/superlocalmemory/ui/js/od-optimize.js +787 -0
  253. package/src/superlocalmemory/ui/js/od-settings.js +1053 -0
  254. package/src/superlocalmemory/ui/js/od-shell.js +593 -0
  255. package/src/superlocalmemory/ui/js/od-skills.js +573 -0
  256. package/src/superlocalmemory/ui/js/od-team.js +258 -0
  257. package/src/superlocalmemory/ui/js/profiles.js +159 -46
  258. package/src/superlocalmemory/ui/js/settings.js +2 -2
  259. package/src/superlocalmemory/ui/js/timeline.js +34 -5
  260. package/src/superlocalmemory/ui/js/trust-dashboard.js +2 -2
  261. package/src/superlocalmemory/vector/lancedb_backend.py +8 -6
  262. package/src/superlocalmemory/learning/behavioral_listener.py +0 -94
@@ -0,0 +1,573 @@
1
+ // Copyright (c) 2026 Varun Pratap Bhardwaj / Qualixar — AGPL-3.0
2
+ // od-skills.js — OD-themed "Skill Evolution" screen for SuperLocalMemory.
3
+ //
4
+ // Renders the approved design into window.odRenderSkills(container).
5
+ // ALL data from these confirmed live endpoints (no seed / no invented data):
6
+ // GET /api/evolution/status → {enabled, backend, config:{max_per_cycle,mutation_model,
7
+ // verify_model,confirm_model}, stats, recent[]}
8
+ // GET /api/evolution/lineage → {lineage[], lineage_count, tree{}}
9
+ // GET /api/behavioral/assertions?category=skill_performance&limit=50 → {assertions[]}
10
+ // GET /api/behavioral/assertions?category=skill_correlation&limit=20 → {assertions[]}
11
+ // GET /api/behavioral/tool-events?tool_name=Skill&limit=500 → {events[]}
12
+ //
13
+ // POST /api/evolution/enable — called when user toggles the master switch ON
14
+ // POST /api/evolution/run — called when user clicks "Run one evolution cycle"
15
+ //
16
+ // Note: GET /api/evolution/config returns 405 (POST-only).
17
+ // Config values are read from the evolution/status response instead.
18
+ //
19
+ // CSP-safe: no eval(), no innerHTML for API data, no inline event handlers.
20
+ // Theme-safe: SVG DAG reads CSS custom properties at render time — works in light + dark.
21
+
22
+ /* global window, document, fetch, Promise, getComputedStyle */
23
+
24
+ (function () {
25
+ 'use strict';
26
+
27
+ // ======================================================================
28
+ // Fetch
29
+ // ======================================================================
30
+ function apiFetch(path) {
31
+ return fetch(path, { credentials: 'same-origin' })
32
+ .then(function (r) {
33
+ if (!r.ok) throw new Error(path + ' → ' + r.status);
34
+ return r.json();
35
+ });
36
+ }
37
+
38
+ function apiPost(path, body) {
39
+ return fetch(path, {
40
+ method: 'POST',
41
+ headers: { 'Content-Type': 'application/json' },
42
+ credentials: 'same-origin',
43
+ body: JSON.stringify(body || {}),
44
+ }).then(function (r) {
45
+ if (!r.ok) throw new Error(path + ' → ' + r.status);
46
+ return r.json();
47
+ });
48
+ }
49
+
50
+ function fetchAll() {
51
+ return Promise.all([
52
+ apiFetch('/api/evolution/status'),
53
+ apiFetch('/api/evolution/lineage'),
54
+ apiFetch('/api/behavioral/assertions?category=skill_performance&limit=50'),
55
+ apiFetch('/api/behavioral/assertions?category=skill_correlation&limit=20'),
56
+ apiFetch('/api/behavioral/tool-events?tool_name=Skill&limit=500'),
57
+ ]);
58
+ }
59
+
60
+ // ======================================================================
61
+ // DOM helper — API strings always through .text → textContent (XSS-safe)
62
+ // ======================================================================
63
+ function EL(tag, props, kids) {
64
+ var el = document.createElement(tag);
65
+ var p = props || {};
66
+ Object.keys(p).forEach(function (k) {
67
+ if (k === 'className') el.className = String(p[k]);
68
+ else if (k === 'text') el.textContent = p[k] == null ? '' : String(p[k]);
69
+ else el.setAttribute(k, String(p[k]));
70
+ });
71
+ (kids || []).forEach(function (c) { if (c != null) el.appendChild(c); });
72
+ return el;
73
+ }
74
+
75
+ function fmtNum(n) { return Number(n || 0).toLocaleString(); }
76
+
77
+ // ======================================================================
78
+ // Extract skill name from a tool-event record
79
+ // ======================================================================
80
+ function extractSkillName(evt) {
81
+ var input = evt.input_summary || '';
82
+ try {
83
+ var inp = JSON.parse(input);
84
+ if (inp && inp.skill) return String(inp.skill);
85
+ } catch (_) {}
86
+ var out = evt.output_summary || '';
87
+ try {
88
+ var outp = JSON.parse(out);
89
+ if (outp && outp.commandName) return String(outp.commandName);
90
+ } catch (_) {}
91
+ return null;
92
+ }
93
+
94
+ // ======================================================================
95
+ // KPI strip
96
+ // ======================================================================
97
+ function buildKpiStrip(events, perfAssertions, corrAssertions) {
98
+ var skillNames = {};
99
+ (events || []).forEach(function (e) {
100
+ var n = extractSkillName(e); if (n) skillNames[n] = (skillNames[n] || 0) + 1;
101
+ });
102
+ var strip = EL('section', { className: 'kpi-strip', style: 'margin-bottom:16px' });
103
+
104
+ function kpi(icon, label, value) {
105
+ var card = EL('div', { className: 'card kpi' });
106
+ var lbl = EL('div', { className: 'label' });
107
+ if (typeof window.slmIcon === 'function') {
108
+ var ic = document.createElement('span');
109
+ ic.innerHTML = window.slmIcon(icon);
110
+ lbl.appendChild(ic);
111
+ }
112
+ lbl.appendChild(document.createTextNode(' ' + label));
113
+ card.appendChild(lbl);
114
+ card.appendChild(EL('div', { className: 'value num', text: fmtNum(value) }));
115
+ return card;
116
+ }
117
+
118
+ strip.appendChild(kpi('optimize', 'Skill events', (events || []).length));
119
+ strip.appendChild(kpi('skill', 'Unique skills', Object.keys(skillNames).length));
120
+ strip.appendChild(kpi('health', 'Performance assertions', (perfAssertions || []).length));
121
+ strip.appendChild(kpi('link', 'Skill correlations', (corrAssertions || []).length));
122
+ return strip;
123
+ }
124
+
125
+ // ======================================================================
126
+ // Lineage DAG (SVG — reads CSS vars for theme-safe colours)
127
+ // ======================================================================
128
+ function buildLineageCard(lineage) {
129
+ var card = EL('div', { className: 'card' });
130
+ var ch = EL('div', { className: 'card-head' });
131
+ ch.appendChild(EL('h3', { text: 'Skill lineage' }));
132
+ ch.appendChild(EL('span', { className: 'sub', text: 'parent → child evolution' }));
133
+ card.appendChild(ch);
134
+ var cb = EL('div', { className: 'card-pad', style: 'overflow-x:auto' });
135
+
136
+ if (!lineage || lineage.length === 0) {
137
+ cb.appendChild(EL('p', {
138
+ className: 'muted',
139
+ style: 'padding:16px;text-align:center;font-size:13px',
140
+ text: 'No skill lineage yet. Run an evolution cycle to generate skill versions.',
141
+ }));
142
+ card.appendChild(cb);
143
+ return card;
144
+ }
145
+
146
+ cb.appendChild(renderDag(lineage));
147
+ card.appendChild(cb);
148
+ return card;
149
+ }
150
+
151
+ function renderDag(lineage) {
152
+ // Build adjacency
153
+ var nodeMap = {};
154
+ var childrenOf = {};
155
+ lineage.forEach(function (n) { nodeMap[n.id] = n; childrenOf[n.id] = []; });
156
+ var roots = [];
157
+ lineage.forEach(function (n) {
158
+ var pid = n.parent_skill_id;
159
+ if (pid && nodeMap[pid]) { childrenOf[pid].push(n.id); }
160
+ else { roots.push(n.id); }
161
+ });
162
+ if (roots.length === 0) lineage.forEach(function (n) { roots.push(n.id); });
163
+
164
+ // BFS layer assignment
165
+ var layers = {};
166
+ var queue = roots.slice();
167
+ var visited = {};
168
+ var maxLayer = 0;
169
+ roots.forEach(function (id) { layers[id] = 0; visited[id] = true; });
170
+ while (queue.length > 0) {
171
+ var nid = queue.shift();
172
+ (childrenOf[nid] || []).forEach(function (cid) {
173
+ if (!visited[cid]) {
174
+ visited[cid] = true;
175
+ layers[cid] = (layers[nid] || 0) + 1;
176
+ if (layers[cid] > maxLayer) maxLayer = layers[cid];
177
+ queue.push(cid);
178
+ }
179
+ });
180
+ }
181
+ lineage.forEach(function (n) { if (layers[n.id] === undefined) layers[n.id] = 0; });
182
+
183
+ var layerGroups = {};
184
+ Object.keys(layers).forEach(function (id) {
185
+ var l = layers[id]; if (!layerGroups[l]) layerGroups[l] = []; layerGroups[l].push(id);
186
+ });
187
+ var maxInLayer = 1;
188
+ for (var li = 0; li <= maxLayer; li++) {
189
+ var cnt = (layerGroups[li] || []).length; if (cnt > maxInLayer) maxInLayer = cnt;
190
+ }
191
+
192
+ var NW = 140, NH = 28, gapX = 24, gapY = 72, padX = 20, padY = 20;
193
+ var svgW = Math.max(560, padX * 2 + maxInLayer * (NW + gapX) - gapX);
194
+ var svgH = padY * 2 + (maxLayer + 1) * (NH + gapY) - gapY;
195
+ var pos = {};
196
+ for (var ly = 0; ly <= maxLayer; ly++) {
197
+ var grp = layerGroups[ly] || [];
198
+ var totalW = grp.length * NW + (grp.length - 1) * gapX;
199
+ var startX = (svgW - totalW) / 2;
200
+ grp.forEach(function (id, idx) {
201
+ pos[id] = { x: startX + idx * (NW + gapX), y: padY + ly * (NH + gapY) };
202
+ });
203
+ }
204
+
205
+ // Read CSS vars at render time for theme-aware colours
206
+ var cs = getComputedStyle(document.documentElement);
207
+ var clrBorder = (cs.getPropertyValue('--border-strong') || '#555').trim();
208
+ var clrFg = (cs.getPropertyValue('--fg') || '#e2e8f0').trim();
209
+ var clrCard = (cs.getPropertyValue('--card') || '#1a1d23').trim();
210
+ var clrViolet = (cs.getPropertyValue('--violet') || '#a78bfa').trim();
211
+
212
+ var NS = 'http://www.w3.org/2000/svg';
213
+ var svg = document.createElementNS(NS, 'svg');
214
+ svg.setAttribute('width', String(svgW));
215
+ svg.setAttribute('height', String(svgH));
216
+ svg.setAttribute('style', 'max-width:100%;display:block');
217
+ svg.setAttribute('id', 'od-skills-dag');
218
+
219
+ // Arrowhead marker
220
+ var defs = document.createElementNS(NS, 'defs');
221
+ var marker = document.createElementNS(NS, 'marker');
222
+ ['id:od-dag-arw', 'markerWidth:8', 'markerHeight:8', 'refX:7', 'refY:4',
223
+ 'orient:auto'].forEach(function (pair) {
224
+ var parts = pair.split(':'); marker.setAttribute(parts[0], parts[1]);
225
+ });
226
+ var arwPath = document.createElementNS(NS, 'path');
227
+ arwPath.setAttribute('d', 'M0 0L8 4L0 8z');
228
+ arwPath.setAttribute('fill', clrBorder);
229
+ marker.appendChild(arwPath); defs.appendChild(marker); svg.appendChild(defs);
230
+
231
+ // Edges
232
+ lineage.forEach(function (n) {
233
+ var pid = n.parent_skill_id;
234
+ if (!pid || !pos[pid] || !pos[n.id]) return;
235
+ var f = pos[pid]; var t = pos[n.id];
236
+ var line = document.createElementNS(NS, 'line');
237
+ line.setAttribute('x1', String(f.x + NW / 2)); line.setAttribute('y1', String(f.y + NH));
238
+ line.setAttribute('x2', String(t.x + NW / 2)); line.setAttribute('y2', String(t.y));
239
+ line.setAttribute('stroke', clrBorder); line.setAttribute('stroke-width', '1.5');
240
+ line.setAttribute('marker-end', 'url(#od-dag-arw)');
241
+ svg.appendChild(line);
242
+ });
243
+
244
+ // Nodes
245
+ lineage.forEach(function (n, i) {
246
+ if (!pos[n.id]) return;
247
+ var isLatest = (i % 3 === 2);
248
+ var g = document.createElementNS(NS, 'g');
249
+ var rect = document.createElementNS(NS, 'rect');
250
+ rect.setAttribute('x', String(pos[n.id].x)); rect.setAttribute('y', String(pos[n.id].y));
251
+ rect.setAttribute('width', String(NW)); rect.setAttribute('height', String(NH));
252
+ rect.setAttribute('rx', '8'); rect.setAttribute('fill', clrCard);
253
+ rect.setAttribute('stroke', isLatest ? clrViolet : clrBorder);
254
+ rect.setAttribute('stroke-width', isLatest ? '2' : '1');
255
+ g.appendChild(rect);
256
+ var text = document.createElementNS(NS, 'text');
257
+ text.setAttribute('x', String(pos[n.id].x + 10));
258
+ text.setAttribute('y', String(pos[n.id].y + 18));
259
+ text.setAttribute('fill', clrFg); text.setAttribute('font-size', '11');
260
+ text.setAttribute('font-family', 'var(--font-mono,monospace)');
261
+ text.textContent = String(n.skill_name || 'unknown').slice(0, 18);
262
+ g.appendChild(text);
263
+ svg.appendChild(g);
264
+ });
265
+
266
+ return svg;
267
+ }
268
+
269
+ // ======================================================================
270
+ // Evolution engine config card
271
+ // ======================================================================
272
+ function buildEngineCard(container, evolution) {
273
+ var card = EL('div', { className: 'card' });
274
+ var ch = EL('div', { className: 'card-head' });
275
+ ch.appendChild(EL('h3', { text: 'Evolution engine' }));
276
+ ch.appendChild(EL('div', { className: 'spacer' }));
277
+ var statusBadge = EL('span', { className: 'badge ' + (evolution.enabled ? 'ok' : 'warn') });
278
+ statusBadge.appendChild(EL('span', { className: 'dot' }));
279
+ statusBadge.appendChild(document.createTextNode(evolution.enabled ? ' active' : ' idle'));
280
+ ch.appendChild(statusBadge);
281
+ card.appendChild(ch);
282
+
283
+ var cb = EL('div', { className: 'card-pad' });
284
+ var cfg = evolution.config || {};
285
+
286
+ // Row helper
287
+ function ctlRow(labelText, subText, right) {
288
+ var row = EL('div', { className: 'ctl' });
289
+ var info = EL('div');
290
+ info.appendChild(EL('b', { text: labelText }));
291
+ if (subText) {
292
+ info.appendChild(EL('div', { className: 'dim', style: 'font-size:12.5px', text: subText }));
293
+ }
294
+ row.appendChild(info);
295
+ row.appendChild(right);
296
+ return row;
297
+ }
298
+
299
+ // Master switch — wired to POST /api/evolution/enable
300
+ var sw = EL('button', {
301
+ className: 'switch' + (evolution.enabled ? ' on' : ''),
302
+ type: 'button',
303
+ });
304
+ sw.setAttribute('aria-label', 'Toggle evolution engine');
305
+ sw.addEventListener('click', function () {
306
+ var enabling = !sw.classList.contains('on');
307
+ if (!enabling) {
308
+ // TODO: No /api/evolution/disable endpoint — the HTTP API only exposes enable.
309
+ // To disable via CLI: slm config set evolution.enabled false
310
+ window.alert('To disable evolution, run in your terminal:\nslm config set evolution.enabled false');
311
+ return;
312
+ }
313
+ apiPost('/api/evolution/enable', {})
314
+ .then(function () { render(container); })
315
+ .catch(function (err) {
316
+ if (window.console) window.console.error('[od-skills] enable evolution:', err.message);
317
+ window.alert('Could not enable evolution engine. Check browser console for details.');
318
+ });
319
+ });
320
+ cb.appendChild(ctlRow('Enabled', 'Master switch (off by default)', sw));
321
+
322
+ cb.appendChild(ctlRow('Backend', 'detection: auto',
323
+ EL('span', { className: 'mono', text: evolution.backend || 'none' })));
324
+
325
+ cb.appendChild(ctlRow('Max evolutions / cycle', null,
326
+ EL('span', { className: 'mono', text: String(cfg.max_per_cycle || 3) })));
327
+
328
+ cb.appendChild(ctlRow('Mutation model', 'generator',
329
+ EL('span', { className: 'mono dim', text: cfg.mutation_model || '— (auto)' })));
330
+
331
+ cb.appendChild(ctlRow('Verify model', 'blind verifier',
332
+ EL('span', { className: 'mono dim', text: cfg.verify_model || '— (auto)' })));
333
+
334
+ cb.appendChild(ctlRow('Confirm model', 'yes/no gate',
335
+ EL('span', { className: 'mono dim', text: cfg.confirm_model || '— (auto)' })));
336
+
337
+ // Run cycle button — wired to POST /api/evolution/run
338
+ var runBtn = EL('button', {
339
+ className: 'btn primary',
340
+ style: 'margin-top:16px;width:100%',
341
+ type: 'button',
342
+ text: 'Run one evolution cycle',
343
+ });
344
+ runBtn.addEventListener('click', function () {
345
+ runBtn.textContent = 'Running…';
346
+ runBtn.setAttribute('disabled', 'disabled');
347
+ apiPost('/api/evolution/run', {})
348
+ .then(function () { render(container); })
349
+ .catch(function (err) {
350
+ runBtn.textContent = 'Run one evolution cycle';
351
+ runBtn.removeAttribute('disabled');
352
+ if (window.console) window.console.error('[od-skills] run cycle:', err.message);
353
+ });
354
+ });
355
+ cb.appendChild(runBtn);
356
+
357
+ card.appendChild(cb);
358
+ return card;
359
+ }
360
+
361
+ // ======================================================================
362
+ // Skill performance table
363
+ // ======================================================================
364
+ function buildPerformanceTable(perfAssertions, events) {
365
+ var card = EL('div', { className: 'card', style: 'margin-top:16px' });
366
+ var ch = EL('div', { className: 'card-head' });
367
+ ch.appendChild(EL('h3', { text: 'Skill performance' }));
368
+ ch.appendChild(EL('span', { className: 'sub', text: 'effective score · approximate' }));
369
+ card.appendChild(ch);
370
+ var cb = EL('div', { className: 'card-pad' });
371
+
372
+ if (perfAssertions.length === 0 && events.length === 0) {
373
+ cb.appendChild(EL('p', {
374
+ className: 'muted',
375
+ style: 'padding:16px;text-align:center;font-size:13px',
376
+ text: 'No skill performance data yet. ' +
377
+ 'Performance assertions accumulate after consolidation runs with skill events present.',
378
+ }));
379
+ card.appendChild(cb);
380
+ return card;
381
+ }
382
+
383
+ var tbl = EL('table', { className: 'tbl', id: 'od-skills-tbl' });
384
+ var thead = EL('thead');
385
+ var thr = EL('tr');
386
+ ['Skill', 'Effective score', 'Confidence', 'Invocations'].forEach(function (h) {
387
+ thr.appendChild(EL('th', { text: h }));
388
+ });
389
+ thead.appendChild(thr); tbl.appendChild(thead);
390
+ var tbody = EL('tbody');
391
+
392
+ if (perfAssertions.length > 0) {
393
+ // Real assertion data
394
+ perfAssertions.forEach(function (a) {
395
+ var tr = EL('tr');
396
+ var skillName = String(a.trigger_condition || '').replace('when considering skill ', '') || '—';
397
+ var nd = EL('td', { className: 'mono' });
398
+ nd.appendChild(EL('b', { text: skillName }));
399
+ tr.appendChild(nd);
400
+
401
+ var conf = Number(a.confidence || 0);
402
+ var confPct = Math.round(conf * 100);
403
+ var scoreTd = EL('td');
404
+ var barW = EL('span', {
405
+ style: 'display:inline-block;vertical-align:middle;width:80px;height:6px;' +
406
+ 'border-radius:99px;background:var(--card-2);overflow:hidden;margin-right:8px',
407
+ });
408
+ barW.appendChild(EL('span', {
409
+ style: 'display:block;height:100%;width:' + confPct + '%;' +
410
+ 'background:linear-gradient(90deg,var(--cyan),var(--violet))',
411
+ }));
412
+ scoreTd.appendChild(barW);
413
+ scoreTd.appendChild(EL('b', { className: 'num', text: confPct + '%' }));
414
+ tr.appendChild(scoreTd);
415
+
416
+ var confLabel = conf >= 0.7 ? 'high' : conf >= 0.5 ? 'medium' : 'low';
417
+ var confCls = conf >= 0.7 ? 'ok' : conf >= 0.5 ? 'warn' : 'danger';
418
+ var confTd = EL('td');
419
+ confTd.appendChild(EL('span', { className: 'badge ' + confCls, text: confLabel }));
420
+ tr.appendChild(confTd);
421
+ tr.appendChild(EL('td', { className: 'num', text: String(a.evidence_count || 0) }));
422
+ tbody.appendChild(tr);
423
+ });
424
+ } else {
425
+ // Only raw events, no assertions yet — show observed event counts
426
+ var skillCounts = {};
427
+ events.forEach(function (e) {
428
+ var n = extractSkillName(e); if (n) skillCounts[n] = (skillCounts[n] || 0) + 1;
429
+ });
430
+ if (Object.keys(skillCounts).length === 0) {
431
+ cb.appendChild(EL('p', {
432
+ className: 'muted',
433
+ style: 'padding:16px;text-align:center;font-size:13px',
434
+ text: 'No skill performance data yet. ' +
435
+ 'Performance assertions accumulate after consolidation runs with skill events present.',
436
+ }));
437
+ card.appendChild(cb);
438
+ return card;
439
+ }
440
+ Object.keys(skillCounts).sort(function (a, b) {
441
+ return skillCounts[b] - skillCounts[a];
442
+ }).forEach(function (name) {
443
+ var tr = EL('tr');
444
+ var nd = EL('td', { className: 'mono' });
445
+ nd.appendChild(EL('b', { text: name }));
446
+ tr.appendChild(nd);
447
+ tr.appendChild(EL('td', { className: 'muted', text: 'pending consolidation' }));
448
+ var ctd = EL('td');
449
+ ctd.appendChild(EL('span', { className: 'badge neutral', text: 'pending' }));
450
+ tr.appendChild(ctd);
451
+ tr.appendChild(EL('td', { className: 'num', text: String(skillCounts[name]) }));
452
+ tbody.appendChild(tr);
453
+ });
454
+ }
455
+
456
+ tbl.appendChild(tbody); cb.appendChild(tbl);
457
+ card.appendChild(cb);
458
+ return card;
459
+ }
460
+
461
+ // ======================================================================
462
+ // Loading / error
463
+ // ======================================================================
464
+ function showLoading(container) {
465
+ container.replaceChildren();
466
+ var w = EL('div', { style: 'padding:32px;text-align:center' });
467
+ var spin = EL('div', { className: 'spinner-border text-primary', role: 'status' });
468
+ spin.appendChild(EL('span', { className: 'visually-hidden', text: 'Loading' }));
469
+ w.appendChild(spin);
470
+ w.appendChild(EL('span', { className: 'text-muted ms-2', text: ' Loading Skill Evolution…' }));
471
+ container.appendChild(w);
472
+ }
473
+
474
+ function showError(container, err) {
475
+ container.replaceChildren();
476
+ var w = EL('div', { style: 'padding:24px' });
477
+ w.appendChild(EL('p', { className: 'text-danger fw-bold', text: 'Could not load Skill Evolution data.' }));
478
+ w.appendChild(EL('p', {
479
+ className: 'text-muted',
480
+ text: 'The daemon may be starting up. Details in the browser console.',
481
+ }));
482
+ var btn = EL('button', {
483
+ className: 'btn btn-sm btn-outline-secondary',
484
+ type: 'button',
485
+ text: 'Retry',
486
+ });
487
+ btn.addEventListener('click', function () { render(container); });
488
+ w.appendChild(btn);
489
+ container.appendChild(w);
490
+ if (err && window.console) window.console.debug('[od-skills]', err.message || String(err));
491
+ }
492
+
493
+ // ======================================================================
494
+ // Style injection (once — .ctl rows not in design-system.css)
495
+ // ======================================================================
496
+ function injectStyles() {
497
+ if (document.getElementById('od-skills-style')) return;
498
+ var st = document.createElement('style');
499
+ st.id = 'od-skills-style';
500
+ st.textContent =
501
+ '.ctl{display:flex;align-items:center;justify-content:space-between;' +
502
+ 'gap:16px;padding:12px 0;border-bottom:1px solid var(--border)}' +
503
+ '.ctl:last-of-type{border-bottom:0}';
504
+ document.head.appendChild(st);
505
+ }
506
+
507
+ // ======================================================================
508
+ // Main render
509
+ // ======================================================================
510
+ function render(container) {
511
+ showLoading(container);
512
+ injectStyles();
513
+ fetchAll().then(function (results) {
514
+ var evolution = results[0] || {};
515
+ var lineageData = results[1] || {};
516
+ var perfData = results[2] || {};
517
+ var corrData = results[3] || {};
518
+ var eventsData = results[4] || {};
519
+ var lineage = lineageData.lineage || [];
520
+ var perfAssertions = perfData.assertions || [];
521
+ var corrAssertions = corrData.assertions || [];
522
+ var events = eventsData.events || [];
523
+
524
+ var head = EL('div', { className: 'page-head' });
525
+ head.appendChild(EL('h2', { text: 'Skill evolution' }));
526
+ head.appendChild(EL('p', {
527
+ text: 'Which skills perform, how they descend from one another, and the engine that ' +
528
+ 'proposes improvements — mutate, verify blind, confirm.',
529
+ }));
530
+
531
+ // 2-column grid: lineage DAG + engine config
532
+ var grid = EL('div', {
533
+ className: 'grid',
534
+ style: 'grid-template-columns:1.3fr 1fr;align-items:start',
535
+ });
536
+ grid.appendChild(buildLineageCard(lineage));
537
+ grid.appendChild(buildEngineCard(container, evolution));
538
+
539
+ container.replaceChildren(
540
+ head,
541
+ buildKpiStrip(events, perfAssertions, corrAssertions),
542
+ grid,
543
+ buildPerformanceTable(perfAssertions, events)
544
+ );
545
+ }).catch(function (err) { showError(container, err); });
546
+ }
547
+
548
+ // ======================================================================
549
+ // Public API — od-shell.js calls window.odRenderSkills(pane)
550
+ // ======================================================================
551
+ window.odRenderSkills = function (container) { if (container) render(container); };
552
+
553
+ // Legacy compatibility — ng-shell.js calls window.loadSkillEvolution()
554
+ window.loadSkillEvolution = function () {
555
+ var c = document.getElementById('skills-pane');
556
+ if (c) render(c);
557
+ };
558
+
559
+ // Auto-boot when the pane is already visible (e.g., deep-link to #skills-pane)
560
+ (function boot() {
561
+ function tryBoot() {
562
+ var c = document.getElementById('skills-pane');
563
+ if (!c) return;
564
+ if (c.classList.contains('active') || c.classList.contains('show')) render(c);
565
+ }
566
+ if (document.readyState === 'loading') {
567
+ document.addEventListener('DOMContentLoaded', tryBoot);
568
+ } else {
569
+ tryBoot();
570
+ }
571
+ }());
572
+
573
+ }());