mem-os-python 1.6.27__py3-none-any.whl

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 (253) hide show
  1. mem_os_python-1.6.27.dist-info/METADATA +1032 -0
  2. mem_os_python-1.6.27.dist-info/RECORD +253 -0
  3. mem_os_python-1.6.27.dist-info/WHEEL +4 -0
  4. mem_os_python-1.6.27.dist-info/entry_points.txt +2 -0
  5. mem_os_python-1.6.27.dist-info/licenses/LICENSE +21 -0
  6. memos/__init__.py +16 -0
  7. memos/_js/adapters/anthropic.d.ts +279 -0
  8. memos/_js/adapters/anthropic.d.ts.map +1 -0
  9. memos/_js/adapters/anthropic.js +348 -0
  10. memos/_js/adapters/anthropic.js.map +1 -0
  11. memos/_js/adapters/base.d.ts +10 -0
  12. memos/_js/adapters/base.d.ts.map +1 -0
  13. memos/_js/adapters/base.js +2 -0
  14. memos/_js/adapters/base.js.map +1 -0
  15. memos/_js/adapters/openai.d.ts +264 -0
  16. memos/_js/adapters/openai.d.ts.map +1 -0
  17. memos/_js/adapters/openai.js +342 -0
  18. memos/_js/adapters/openai.js.map +1 -0
  19. memos/_js/apps/explorer.d.ts +244 -0
  20. memos/_js/apps/explorer.d.ts.map +1 -0
  21. memos/_js/apps/explorer.js +738 -0
  22. memos/_js/apps/explorer.js.map +1 -0
  23. memos/_js/citations.d.ts +65 -0
  24. memos/_js/citations.d.ts.map +1 -0
  25. memos/_js/citations.js +143 -0
  26. memos/_js/citations.js.map +1 -0
  27. memos/_js/cli-browse.d.ts +3 -0
  28. memos/_js/cli-browse.d.ts.map +1 -0
  29. memos/_js/cli-browse.js +129 -0
  30. memos/_js/cli-browse.js.map +1 -0
  31. memos/_js/cli-completion.d.ts +31 -0
  32. memos/_js/cli-completion.d.ts.map +1 -0
  33. memos/_js/cli-completion.js +340 -0
  34. memos/_js/cli-completion.js.map +1 -0
  35. memos/_js/cli-help.d.ts +18 -0
  36. memos/_js/cli-help.d.ts.map +1 -0
  37. memos/_js/cli-help.js +357 -0
  38. memos/_js/cli-help.js.map +1 -0
  39. memos/_js/cli.d.ts +28 -0
  40. memos/_js/cli.d.ts.map +1 -0
  41. memos/_js/cli.js +2880 -0
  42. memos/_js/cli.js.map +1 -0
  43. memos/_js/compression.d.ts +25 -0
  44. memos/_js/compression.d.ts.map +1 -0
  45. memos/_js/compression.js +319 -0
  46. memos/_js/compression.js.map +1 -0
  47. memos/_js/confidence-machine.d.ts +99 -0
  48. memos/_js/confidence-machine.d.ts.map +1 -0
  49. memos/_js/confidence-machine.js +222 -0
  50. memos/_js/confidence-machine.js.map +1 -0
  51. memos/_js/context-pack.d.ts +506 -0
  52. memos/_js/context-pack.d.ts.map +1 -0
  53. memos/_js/context-pack.js +1435 -0
  54. memos/_js/context-pack.js.map +1 -0
  55. memos/_js/contradictions.d.ts +160 -0
  56. memos/_js/contradictions.d.ts.map +1 -0
  57. memos/_js/contradictions.js +255 -0
  58. memos/_js/contradictions.js.map +1 -0
  59. memos/_js/dashboard.d.ts +30 -0
  60. memos/_js/dashboard.d.ts.map +1 -0
  61. memos/_js/dashboard.js +302 -0
  62. memos/_js/dashboard.js.map +1 -0
  63. memos/_js/ebbinghaus.d.ts +29 -0
  64. memos/_js/ebbinghaus.d.ts.map +1 -0
  65. memos/_js/ebbinghaus.js +32 -0
  66. memos/_js/ebbinghaus.js.map +1 -0
  67. memos/_js/embedding-queue.d.ts +190 -0
  68. memos/_js/embedding-queue.d.ts.map +1 -0
  69. memos/_js/embedding-queue.js +316 -0
  70. memos/_js/embedding-queue.js.map +1 -0
  71. memos/_js/embeddings.d.ts +187 -0
  72. memos/_js/embeddings.d.ts.map +1 -0
  73. memos/_js/embeddings.js +633 -0
  74. memos/_js/embeddings.js.map +1 -0
  75. memos/_js/entity-extraction.d.ts +54 -0
  76. memos/_js/entity-extraction.d.ts.map +1 -0
  77. memos/_js/entity-extraction.js +325 -0
  78. memos/_js/entity-extraction.js.map +1 -0
  79. memos/_js/entity-index.d.ts +93 -0
  80. memos/_js/entity-index.d.ts.map +1 -0
  81. memos/_js/entity-index.js +165 -0
  82. memos/_js/entity-index.js.map +1 -0
  83. memos/_js/event-memory.d.ts +147 -0
  84. memos/_js/event-memory.d.ts.map +1 -0
  85. memos/_js/event-memory.js +290 -0
  86. memos/_js/event-memory.js.map +1 -0
  87. memos/_js/external-import.d.ts +98 -0
  88. memos/_js/external-import.d.ts.map +1 -0
  89. memos/_js/external-import.js +669 -0
  90. memos/_js/external-import.js.map +1 -0
  91. memos/_js/fidelity.d.ts +367 -0
  92. memos/_js/fidelity.d.ts.map +1 -0
  93. memos/_js/fidelity.js +911 -0
  94. memos/_js/fidelity.js.map +1 -0
  95. memos/_js/graph-expansion.d.ts +119 -0
  96. memos/_js/graph-expansion.d.ts.map +1 -0
  97. memos/_js/graph-expansion.js +213 -0
  98. memos/_js/graph-expansion.js.map +1 -0
  99. memos/_js/graph-mermaid.d.ts +13 -0
  100. memos/_js/graph-mermaid.d.ts.map +1 -0
  101. memos/_js/graph-mermaid.js +32 -0
  102. memos/_js/graph-mermaid.js.map +1 -0
  103. memos/_js/graph.d.ts +170 -0
  104. memos/_js/graph.d.ts.map +1 -0
  105. memos/_js/graph.js +482 -0
  106. memos/_js/graph.js.map +1 -0
  107. memos/_js/harness.d.ts +63 -0
  108. memos/_js/harness.d.ts.map +1 -0
  109. memos/_js/harness.js +93 -0
  110. memos/_js/harness.js.map +1 -0
  111. memos/_js/importance.d.ts +118 -0
  112. memos/_js/importance.d.ts.map +1 -0
  113. memos/_js/importance.js +144 -0
  114. memos/_js/importance.js.map +1 -0
  115. memos/_js/index.d.ts +51 -0
  116. memos/_js/index.d.ts.map +1 -0
  117. memos/_js/index.js +53 -0
  118. memos/_js/index.js.map +1 -0
  119. memos/_js/mcp.d.ts +58 -0
  120. memos/_js/mcp.d.ts.map +1 -0
  121. memos/_js/mcp.js +1853 -0
  122. memos/_js/mcp.js.map +1 -0
  123. memos/_js/memory.d.ts +1762 -0
  124. memos/_js/memory.d.ts.map +1 -0
  125. memos/_js/memory.js +6283 -0
  126. memos/_js/memory.js.map +1 -0
  127. memos/_js/procedural.d.ts +74 -0
  128. memos/_js/procedural.d.ts.map +1 -0
  129. memos/_js/procedural.js +125 -0
  130. memos/_js/procedural.js.map +1 -0
  131. memos/_js/project-config.d.ts +68 -0
  132. memos/_js/project-config.d.ts.map +1 -0
  133. memos/_js/project-config.js +59 -0
  134. memos/_js/project-config.js.map +1 -0
  135. memos/_js/proof-of-forgetting.d.ts +57 -0
  136. memos/_js/proof-of-forgetting.d.ts.map +1 -0
  137. memos/_js/proof-of-forgetting.js +80 -0
  138. memos/_js/proof-of-forgetting.js.map +1 -0
  139. memos/_js/provenance.d.ts +122 -0
  140. memos/_js/provenance.d.ts.map +1 -0
  141. memos/_js/provenance.js +182 -0
  142. memos/_js/provenance.js.map +1 -0
  143. memos/_js/quarantine-commands.d.ts +23 -0
  144. memos/_js/quarantine-commands.d.ts.map +1 -0
  145. memos/_js/quarantine-commands.js +42 -0
  146. memos/_js/quarantine-commands.js.map +1 -0
  147. memos/_js/quarantine.d.ts +120 -0
  148. memos/_js/quarantine.d.ts.map +1 -0
  149. memos/_js/quarantine.js +873 -0
  150. memos/_js/quarantine.js.map +1 -0
  151. memos/_js/redact.d.ts +12 -0
  152. memos/_js/redact.d.ts.map +1 -0
  153. memos/_js/redact.js +26 -0
  154. memos/_js/redact.js.map +1 -0
  155. memos/_js/repl.d.ts +57 -0
  156. memos/_js/repl.d.ts.map +1 -0
  157. memos/_js/repl.js +295 -0
  158. memos/_js/repl.js.map +1 -0
  159. memos/_js/rerank-local.d.ts +61 -0
  160. memos/_js/rerank-local.d.ts.map +1 -0
  161. memos/_js/rerank-local.js +210 -0
  162. memos/_js/rerank-local.js.map +1 -0
  163. memos/_js/retain-filter.d.ts +98 -0
  164. memos/_js/retain-filter.d.ts.map +1 -0
  165. memos/_js/retain-filter.js +232 -0
  166. memos/_js/retain-filter.js.map +1 -0
  167. memos/_js/retrieval.d.ts +265 -0
  168. memos/_js/retrieval.d.ts.map +1 -0
  169. memos/_js/retrieval.js +1018 -0
  170. memos/_js/retrieval.js.map +1 -0
  171. memos/_js/revert.d.ts +77 -0
  172. memos/_js/revert.d.ts.map +1 -0
  173. memos/_js/revert.js +252 -0
  174. memos/_js/revert.js.map +1 -0
  175. memos/_js/scope.d.ts +30 -0
  176. memos/_js/scope.d.ts.map +1 -0
  177. memos/_js/scope.js +71 -0
  178. memos/_js/scope.js.map +1 -0
  179. memos/_js/semantic-screen.d.ts +152 -0
  180. memos/_js/semantic-screen.d.ts.map +1 -0
  181. memos/_js/semantic-screen.js +290 -0
  182. memos/_js/semantic-screen.js.map +1 -0
  183. memos/_js/session-hooks.d.ts +127 -0
  184. memos/_js/session-hooks.d.ts.map +1 -0
  185. memos/_js/session-hooks.js +482 -0
  186. memos/_js/session-hooks.js.map +1 -0
  187. memos/_js/storage/sqlite.d.ts +427 -0
  188. memos/_js/storage/sqlite.d.ts.map +1 -0
  189. memos/_js/storage/sqlite.js +2428 -0
  190. memos/_js/storage/sqlite.js.map +1 -0
  191. memos/_js/sync.d.ts +213 -0
  192. memos/_js/sync.d.ts.map +1 -0
  193. memos/_js/sync.js +389 -0
  194. memos/_js/sync.js.map +1 -0
  195. memos/_js/tamper-log.d.ts +98 -0
  196. memos/_js/tamper-log.d.ts.map +1 -0
  197. memos/_js/tamper-log.js +327 -0
  198. memos/_js/tamper-log.js.map +1 -0
  199. memos/_js/temporal.d.ts +92 -0
  200. memos/_js/temporal.d.ts.map +1 -0
  201. memos/_js/temporal.js +536 -0
  202. memos/_js/temporal.js.map +1 -0
  203. memos/_js/time-travel.d.ts +38 -0
  204. memos/_js/time-travel.d.ts.map +1 -0
  205. memos/_js/time-travel.js +46 -0
  206. memos/_js/time-travel.js.map +1 -0
  207. memos/_js/timestamp-format.d.ts +18 -0
  208. memos/_js/timestamp-format.d.ts.map +1 -0
  209. memos/_js/timestamp-format.js +52 -0
  210. memos/_js/timestamp-format.js.map +1 -0
  211. memos/_js/triple-extraction.d.ts +31 -0
  212. memos/_js/triple-extraction.d.ts.map +1 -0
  213. memos/_js/triple-extraction.js +116 -0
  214. memos/_js/triple-extraction.js.map +1 -0
  215. memos/_js/types.d.ts +2474 -0
  216. memos/_js/types.d.ts.map +1 -0
  217. memos/_js/types.js +20 -0
  218. memos/_js/types.js.map +1 -0
  219. memos/_js/updater.d.ts +95 -0
  220. memos/_js/updater.d.ts.map +1 -0
  221. memos/_js/updater.js +268 -0
  222. memos/_js/updater.js.map +1 -0
  223. memos/_js/version.d.ts +17 -0
  224. memos/_js/version.d.ts.map +1 -0
  225. memos/_js/version.js +34 -0
  226. memos/_js/version.js.map +1 -0
  227. memos/_js/vision/clip-query-cache.d.ts +58 -0
  228. memos/_js/vision/clip-query-cache.d.ts.map +1 -0
  229. memos/_js/vision/clip-query-cache.js +79 -0
  230. memos/_js/vision/clip-query-cache.js.map +1 -0
  231. memos/_js/vision/clip.d.ts +113 -0
  232. memos/_js/vision/clip.d.ts.map +1 -0
  233. memos/_js/vision/clip.js +224 -0
  234. memos/_js/vision/clip.js.map +1 -0
  235. memos/_js/vision/fake-clip.d.ts +55 -0
  236. memos/_js/vision/fake-clip.d.ts.map +1 -0
  237. memos/_js/vision/fake-clip.js +122 -0
  238. memos/_js/vision/fake-clip.js.map +1 -0
  239. memos/_js/vision/index.d.ts +15 -0
  240. memos/_js/vision/index.d.ts.map +1 -0
  241. memos/_js/vision/index.js +13 -0
  242. memos/_js/vision/index.js.map +1 -0
  243. memos/_js/vision/store.d.ts +137 -0
  244. memos/_js/vision/store.d.ts.map +1 -0
  245. memos/_js/vision/store.js +383 -0
  246. memos/_js/vision/store.js.map +1 -0
  247. memos/adapters/__init__.py +0 -0
  248. memos/adapters/crewai.py +385 -0
  249. memos/adapters/langchain.py +301 -0
  250. memos/adapters/ollama.py +396 -0
  251. memos/server/__init__.py +0 -0
  252. memos/server/main.py +547 -0
  253. memos/server/routes.py +360 -0
memos/_js/dashboard.js ADDED
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Local dashboard — a browser UI for the MemOS memory store.
3
+ *
4
+ * `memos dashboard` starts a local HTTP server (no external dependencies)
5
+ * serving a single-page dashboard for browsing, searching, and visualizing
6
+ * the memory graph. No MCP Apps host required.
7
+ *
8
+ * API:
9
+ * GET /api/memories?limit=50&offset=0 — list memories
10
+ * GET /api/search?q=...&limit=20 — search memories
11
+ * GET /api/memory/:id — get one memory
12
+ * GET /api/graph — graph nodes + edges
13
+ * GET /api/stats — store statistics
14
+ *
15
+ * @module @memos/dashboard
16
+ */
17
+ import { createServer, } from "node:http";
18
+ import { redactSecrets } from "./redact.js";
19
+ const DASHBOARD_HTML = `<!DOCTYPE html>
20
+ <html lang="en">
21
+ <head>
22
+ <meta charset="UTF-8">
23
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
24
+ <title>MemOS Dashboard</title>
25
+ <style>
26
+ * { box-sizing: border-box; margin: 0; padding: 0; }
27
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #0f1115; color: #e6e6e6; }
28
+ header { background: #1a1d24; padding: 1rem 2rem; border-bottom: 1px solid #2a2e38; display: flex; align-items: center; gap: 1rem; }
29
+ header h1 { font-size: 1.25rem; font-weight: 600; }
30
+ header .badge { background: #2d6a4f; color: #fff; padding: 0.15rem 0.6rem; border-radius: 1rem; font-size: 0.75rem; }
31
+ nav { display: flex; gap: 0.5rem; padding: 1rem 2rem; background: #14161b; border-bottom: 1px solid #2a2e38; }
32
+ nav button { background: #23262e; color: #e6e6e6; border: 1px solid #2a2e38; padding: 0.5rem 1rem; border-radius: 0.375rem; cursor: pointer; }
33
+ nav button.active { background: #2d6a4f; border-color: #2d6a4f; }
34
+ nav button:hover { background: #2e323c; }
35
+ nav button.active:hover { background: #2d6a4f; }
36
+ main { padding: 2rem; max-width: 1200px; margin: 0 auto; }
37
+ .search-bar { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; }
38
+ .search-bar input { flex: 1; background: #1a1d24; border: 1px solid #2a2e38; color: #e6e6e6; padding: 0.75rem 1rem; border-radius: 0.375rem; font-size: 1rem; }
39
+ .search-bar button { background: #2d6a4f; color: #fff; border: none; padding: 0.75rem 1.5rem; border-radius: 0.375rem; cursor: pointer; font-size: 1rem; }
40
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
41
+ .stat-card { background: #1a1d24; border: 1px solid #2a2e38; border-radius: 0.5rem; padding: 1.25rem; }
42
+ .stat-card .value { font-size: 2rem; font-weight: 700; color: #52b788; }
43
+ .stat-card .label { color: #888; font-size: 0.875rem; margin-top: 0.25rem; }
44
+ .memory-list { display: flex; flex-direction: column; gap: 0.75rem; }
45
+ .memory-card { background: #1a1d24; border: 1px solid #2a2e38; border-radius: 0.5rem; padding: 1rem 1.25rem; }
46
+ .memory-card .meta { display: flex; gap: 0.75rem; align-items: center; margin-bottom: 0.5rem; font-size: 0.8rem; color: #888; }
47
+ .memory-card .type { background: #2a2e38; padding: 0.15rem 0.5rem; border-radius: 0.25rem; }
48
+ .memory-card .content { line-height: 1.5; }
49
+ .memory-card .id { font-family: monospace; font-size: 0.75rem; }
50
+ #graph-canvas { width: 100%; height: 600px; background: #1a1d24; border: 1px solid #2a2e38; border-radius: 0.5rem; }
51
+ .empty { text-align: center; color: #666; padding: 3rem; }
52
+ .loading { text-align: center; color: #888; padding: 2rem; }
53
+ </style>
54
+ </head>
55
+ <body>
56
+ <header>
57
+ <h1>MemOS Dashboard</h1>
58
+ <span class="badge">local</span>
59
+ </header>
60
+ <nav>
61
+ <button data-view="browse" class="active">Browse</button>
62
+ <button data-view="search">Search</button>
63
+ <button data-view="graph">Graph</button>
64
+ <button data-view="stats">Stats</button>
65
+ </nav>
66
+ <main>
67
+ <div id="view-browse">
68
+ <div class="memory-list" id="memory-list"><div class="loading">Loading...</div></div>
69
+ </div>
70
+ <div id="view-search" style="display:none">
71
+ <div class="search-bar">
72
+ <input type="text" id="search-input" placeholder="Search memories...">
73
+ <button onclick="doSearch()">Search</button>
74
+ </div>
75
+ <div class="memory-list" id="search-results"></div>
76
+ </div>
77
+ <div id="view-graph" style="display:none">
78
+ <canvas id="graph-canvas"></canvas>
79
+ </div>
80
+ <div id="view-stats" style="display:none">
81
+ <div class="stats" id="stats-grid"><div class="loading">Loading...</div></div>
82
+ </div>
83
+ </main>
84
+ <script>
85
+ const views = ["browse", "search", "graph", "stats"];
86
+ document.querySelectorAll("nav button").forEach(btn => {
87
+ btn.addEventListener("click", () => {
88
+ document.querySelectorAll("nav button").forEach(b => b.classList.remove("active"));
89
+ btn.classList.add("active");
90
+ const view = btn.dataset.view;
91
+ views.forEach(v => document.getElementById("view-" + v).style.display = v === view ? "block" : "none");
92
+ if (view === "browse") loadMemories();
93
+ if (view === "graph") loadGraph();
94
+ if (view === "stats") loadStats();
95
+ });
96
+ });
97
+
98
+ function memoryCard(m) {
99
+ const date = m.createdAt ? new Date(m.createdAt).toLocaleString() : "";
100
+ return '<div class="memory-card">' +
101
+ '<div class="meta"><span class="type">' + esc(m.type || "note") + '</span>' +
102
+ '<span class="id">' + esc((m.id || "").slice(0, 8)) + '</span>' +
103
+ '<span>' + esc(date) + '</span></div>' +
104
+ '<div class="content">' + esc(m.content || "") + '</div></div>';
105
+ }
106
+ function esc(s) {
107
+ return String(s).replace(/[&<>"']/g, c => ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}[c]));
108
+ }
109
+
110
+ async function loadMemories() {
111
+ const el = document.getElementById("memory-list");
112
+ try {
113
+ const r = await fetch("/api/memories?limit=50");
114
+ const data = await r.json();
115
+ el.innerHTML = data.memories.length
116
+ ? data.memories.map(memoryCard).join("")
117
+ : '<div class="empty">No memories yet. Use <code>memos store</code> to add some.</div>';
118
+ } catch (e) { el.innerHTML = '<div class="empty">Failed to load: ' + esc(e.message) + '</div>'; }
119
+ }
120
+
121
+ async function doSearch() {
122
+ const q = document.getElementById("search-input").value.trim();
123
+ const el = document.getElementById("search-results");
124
+ if (!q) { el.innerHTML = ""; return; }
125
+ el.innerHTML = '<div class="loading">Searching...</div>';
126
+ try {
127
+ const r = await fetch("/api/search?q=" + encodeURIComponent(q) + "&limit=20");
128
+ const data = await r.json();
129
+ el.innerHTML = data.results.length
130
+ ? data.results.map(x => memoryCard(x.node || x)).join("")
131
+ : '<div class="empty">No results.</div>';
132
+ } catch (e) { el.innerHTML = '<div class="empty">Search failed: ' + esc(e.message) + '</div>'; }
133
+ }
134
+ document.getElementById("search-input").addEventListener("keydown", e => {
135
+ if (e.key === "Enter") doSearch();
136
+ });
137
+
138
+ async function loadGraph() {
139
+ const canvas = document.getElementById("graph-canvas");
140
+ const ctx = canvas.getContext("2d");
141
+ canvas.width = canvas.offsetWidth; canvas.height = 600;
142
+ ctx.fillStyle = "#888"; ctx.font = "14px sans-serif";
143
+ ctx.fillText("Loading graph...", 20, 30);
144
+ try {
145
+ const r = await fetch("/api/graph");
146
+ const data = await r.json();
147
+ drawGraph(ctx, canvas, data);
148
+ } catch (e) {
149
+ ctx.fillText("Failed: " + e.message, 20, 30);
150
+ }
151
+ }
152
+
153
+ function drawGraph(ctx, canvas, data) {
154
+ const nodes = data.nodes || [];
155
+ const edges = data.edges || [];
156
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
157
+ if (!nodes.length) {
158
+ ctx.fillStyle = "#666"; ctx.fillText("No memories to graph.", 20, 30);
159
+ return;
160
+ }
161
+ // Simple radial layout
162
+ const cx = canvas.width / 2, cy = canvas.height / 2;
163
+ const radius = Math.min(cx, cy) - 60;
164
+ const pos = {};
165
+ nodes.forEach((n, i) => {
166
+ const angle = (2 * Math.PI * i) / nodes.length;
167
+ pos[n.id] = { x: cx + radius * Math.cos(angle), y: cy + radius * Math.sin(angle) };
168
+ });
169
+ // Edges
170
+ ctx.strokeStyle = "#2a2e38"; ctx.lineWidth = 1;
171
+ edges.forEach(e => {
172
+ const a = pos[e.source] || pos[e.from], b = pos[e.target] || pos[e.to];
173
+ if (a && b) { ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.stroke(); }
174
+ });
175
+ // Nodes
176
+ nodes.forEach(n => {
177
+ const p = pos[n.id];
178
+ ctx.fillStyle = "#52b788";
179
+ ctx.beginPath(); ctx.arc(p.x, p.y, 8, 0, 2 * Math.PI); ctx.fill();
180
+ ctx.fillStyle = "#aaa"; ctx.font = "11px sans-serif";
181
+ const label = (n.content || n.id || "").slice(0, 24);
182
+ ctx.fillText(label, p.x + 12, p.y + 4);
183
+ });
184
+ }
185
+
186
+ async function loadStats() {
187
+ const el = document.getElementById("stats-grid");
188
+ try {
189
+ const r = await fetch("/api/stats");
190
+ const s = await r.json();
191
+ el.innerHTML =
192
+ statCard(s.total ?? 0, "Total memories") +
193
+ statCard(s.byType ? Object.keys(s.byType).length : 0, "Memory types") +
194
+ statCard(s.edges ?? 0, "Graph edges");
195
+ } catch (e) { el.innerHTML = '<div class="empty">Failed: ' + esc(e.message) + '</div>'; }
196
+ }
197
+ function statCard(value, label) {
198
+ return '<div class="stat-card"><div class="value">' + value + '</div><div class="label">' + esc(label) + '</div></div>';
199
+ }
200
+
201
+ loadMemories();
202
+ </script>
203
+ </body>
204
+ </html>`;
205
+ /**
206
+ * Start the local dashboard server.
207
+ * Returns the server instance and the URL.
208
+ */
209
+ export async function startDashboard(memos, opts = {}) {
210
+ const port = opts.port ?? 0; // 0 = random available port
211
+ const server = createServer(async (req, res) => {
212
+ // CORS for local dev
213
+ res.setHeader("Access-Control-Allow-Origin", "*");
214
+ const url = new URL(req.url ?? "/", `http://localhost`);
215
+ try {
216
+ // API routes
217
+ if (url.pathname === "/api/memories") {
218
+ const limit = parseInt(url.searchParams.get("limit") ?? "50", 10);
219
+ const offset = parseInt(url.searchParams.get("offset") ?? "0", 10);
220
+ const graph = await memos.getGraph();
221
+ const nodes = graph.nodes ?? [];
222
+ const memories = nodes.slice(offset, offset + limit);
223
+ sendJson(res, { memories, total: nodes.length });
224
+ return;
225
+ }
226
+ if (url.pathname === "/api/search") {
227
+ const q = url.searchParams.get("q") ?? "";
228
+ const limit = parseInt(url.searchParams.get("limit") ?? "20", 10);
229
+ const results = q ? (await memos.search(q)).slice(0, limit) : [];
230
+ sendJson(res, { results, query: q });
231
+ return;
232
+ }
233
+ if (url.pathname.startsWith("/api/memory/")) {
234
+ const id = decodeURIComponent(url.pathname.slice("/api/memory/".length));
235
+ const node = await memos.retrieve(id);
236
+ if (!node) {
237
+ sendJson(res, { error: "not found" }, 404);
238
+ }
239
+ else {
240
+ sendJson(res, { memory: node });
241
+ }
242
+ return;
243
+ }
244
+ if (url.pathname === "/api/graph") {
245
+ const graph = await memos.getGraph();
246
+ sendJson(res, {
247
+ nodes: graph.nodes ?? [],
248
+ edges: graph.edges ?? [],
249
+ });
250
+ return;
251
+ }
252
+ if (url.pathname === "/api/stats") {
253
+ const graph = await memos.getGraph();
254
+ const nodes = graph.nodes ?? [];
255
+ const byType = {};
256
+ for (const n of nodes) {
257
+ const t = n.type ?? "note";
258
+ byType[t] = (byType[t] ?? 0) + 1;
259
+ }
260
+ sendJson(res, {
261
+ total: nodes.length,
262
+ byType,
263
+ edges: (graph.edges ?? []).length,
264
+ });
265
+ return;
266
+ }
267
+ // Serve the dashboard HTML for all other routes
268
+ if (url.pathname === "/" || !url.pathname.startsWith("/api/")) {
269
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
270
+ res.end(DASHBOARD_HTML);
271
+ return;
272
+ }
273
+ sendJson(res, { error: "not found" }, 404);
274
+ }
275
+ catch (err) {
276
+ // Never echo raw error text to the HTTP client: it can carry stack
277
+ // traces or SQL fragments (CodeQL js/stack-trace-exposure). Log it
278
+ // server-side (localhost operator's own terminal, redacted) and
279
+ // return a generic 500.
280
+ console.error("[dashboard] request failed:", redactSecrets(err instanceof Error ? err.message : String(err)));
281
+ sendJson(res, { error: "internal error" }, 500);
282
+ }
283
+ });
284
+ await new Promise((resolve, reject) => {
285
+ server.once("error", reject);
286
+ server.listen(port, "127.0.0.1", () => resolve());
287
+ });
288
+ const address = server.address();
289
+ const actualPort = typeof address === "object" && address ? address.port : port;
290
+ const url = `http://127.0.0.1:${actualPort}`;
291
+ return {
292
+ url,
293
+ close: () => new Promise((resolve, reject) => {
294
+ server.close((err) => (err ? reject(err) : resolve()));
295
+ }),
296
+ };
297
+ }
298
+ function sendJson(res, data, status = 200) {
299
+ res.writeHead(status, { "Content-Type": "application/json" });
300
+ res.end(JSON.stringify(data));
301
+ }
302
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../src/dashboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Ebbinghaus forgetting-curve reinforcement for retrieval scoring.
3
+ *
4
+ * From MemoryBank (AAAI 2024): score memories by R = e^(-Δt/S), where
5
+ * Δt is days since last access and S is the access count (strength).
6
+ * Frequently-recalled memories stay strong; untouched old ones fade
7
+ * exponentially. Fixes stale preferences overriding recent corrections.
8
+ *
9
+ * Pure arithmetic, no LLM. Blended as a small additive term in scoring.
10
+ * Opt-in via `experimental.ebbinghaus`.
11
+ */
12
+ export interface EbbinghausNode {
13
+ accessCount: number;
14
+ lastAccessed: number;
15
+ }
16
+ /**
17
+ * Compute the Ebbinghaus recall strength for a node.
18
+ * Returns a value in (0, 1]: 1.0 for just-accessed, decaying exponentially.
19
+ *
20
+ * @param node - Node with accessCount and lastAccessed
21
+ * @param now - Current time in epoch ms (defaults to Date.now())
22
+ */
23
+ export declare function recallStrength(node: EbbinghausNode, now?: number): number;
24
+ /**
25
+ * Weight for blending Ebbinghaus score into fused results.
26
+ * Small by default to be safe; the fused score dominates.
27
+ */
28
+ export declare const EBBINGHAUS_WEIGHT = 0.1;
29
+ //# sourceMappingURL=ebbinghaus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ebbinghaus.d.ts","sourceRoot":"","sources":["../src/ebbinghaus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,cAAc,EACpB,GAAG,GAAE,MAAmB,GACvB,MAAM,CASR;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Ebbinghaus forgetting-curve reinforcement for retrieval scoring.
3
+ *
4
+ * From MemoryBank (AAAI 2024): score memories by R = e^(-Δt/S), where
5
+ * Δt is days since last access and S is the access count (strength).
6
+ * Frequently-recalled memories stay strong; untouched old ones fade
7
+ * exponentially. Fixes stale preferences overriding recent corrections.
8
+ *
9
+ * Pure arithmetic, no LLM. Blended as a small additive term in scoring.
10
+ * Opt-in via `experimental.ebbinghaus`.
11
+ */
12
+ /**
13
+ * Compute the Ebbinghaus recall strength for a node.
14
+ * Returns a value in (0, 1]: 1.0 for just-accessed, decaying exponentially.
15
+ *
16
+ * @param node - Node with accessCount and lastAccessed
17
+ * @param now - Current time in epoch ms (defaults to Date.now())
18
+ */
19
+ export function recallStrength(node, now = Date.now()) {
20
+ const accessCount = Math.max(1, node.accessCount || 1);
21
+ const lastAccessed = node.lastAccessed || now;
22
+ // Δt in days
23
+ const deltaDays = Math.max(0, (now - lastAccessed) / (1000 * 60 * 60 * 24));
24
+ // R = e^(-Δt/S)
25
+ return Math.exp(-deltaDays / accessCount);
26
+ }
27
+ /**
28
+ * Weight for blending Ebbinghaus score into fused results.
29
+ * Small by default to be safe; the fused score dominates.
30
+ */
31
+ export const EBBINGHAUS_WEIGHT = 0.1;
32
+ //# sourceMappingURL=ebbinghaus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ebbinghaus.js","sourceRoot":"","sources":["../src/ebbinghaus.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAoB,EACpB,GAAG,GAAW,IAAI,CAAC,GAAG,EAAE;IAExB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,CAAC;IACvD,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,GAAG,CAAC;IAE9C,aAAa;IACb,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAE5E,gBAAgB;IAChB,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,SAAS,GAAG,WAAW,CAAC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * EmbeddingQueue — bounded, retryable, event-emitting background worker
3
+ * pool for embedding generation.
4
+ *
5
+ * Why this exists: the previous MemOS engine called `provider.embed()` inline
6
+ * from `store()` and `update()`. For local-hash that costs nothing, but the
7
+ * moment a user points MemOS at Ollama, LM Studio, or any OpenAI-compatible
8
+ * endpoint, every write blocks for the full round-trip. This queue lets
9
+ * `store()` return as soon as the node is persisted, with the embedding work
10
+ * happening in the background under a bounded concurrency cap.
11
+ *
12
+ * Design:
13
+ * - One shared queue per MemOS instance.
14
+ * - `enqueue()` is fire-and-forget — returns a job id immediately.
15
+ * - Workers are pulled in FIFO order. Concurrency is bounded by
16
+ * `EmbeddingQueueConfig.concurrency` (default 2).
17
+ * - When `batchSize` > 1 and the provider implements `batchEmbed`,
18
+ * a worker drains up to `batchSize` pending jobs into one batched
19
+ * call; a failed batch falls back to per-job retries.
20
+ * - Failures are retried with exponential backoff up to `maxRetries`
21
+ * (default 3) before the job is marked `failed` and an
22
+ * `embedding:failed` event fires.
23
+ * - Backpressure: if `maxQueueSize` is exceeded, `enqueue()` throws
24
+ * instead of growing memory unbounded. Callers can `await flush()`
25
+ * to make room.
26
+ * - Lifecycle events: `embedding:queued`, `embedding:started`,
27
+ * `embedding:complete`, `embedding:failed`, `embedding:retry`.
28
+ *
29
+ * The queue does not know about storage. It only knows how to call the
30
+ * `EmbeddingProvider`. Persistence of the resulting vector is the
31
+ * responsibility of the caller (a `persist` callback) so this module
32
+ * stays storage-agnostic and easy to test.
33
+ *
34
+ * @module @memos/embedding-queue
35
+ */
36
+ import type { EmbeddingProvider, EmbeddingVector } from "./types.js";
37
+ /** Lifecycle states an embedding job can be in. */
38
+ export type EmbeddingJobStatus = "queued" | "running" | "complete" | "failed" | "retrying";
39
+ /** Reasons a job can be marked failed. */
40
+ export type EmbeddingFailureReason = "max_retries" | "aborted" | "provider_error";
41
+ /** A single embedding job, exposed for observability. */
42
+ export interface EmbeddingJob {
43
+ /** Stable id assigned at enqueue time. */
44
+ id: string;
45
+ /** Memory node this embedding is for. */
46
+ nodeId: string;
47
+ /** Text to embed. */
48
+ text: string;
49
+ /** Current status. */
50
+ status: EmbeddingJobStatus;
51
+ /** Number of times we have called the provider for this job. */
52
+ attempts: number;
53
+ /** Most recent error, if any. */
54
+ lastError: string | null;
55
+ /** Timestamp the job was enqueued (ms). */
56
+ enqueuedAt: number;
57
+ /** Timestamp of the most recent status change (ms). */
58
+ updatedAt: number;
59
+ /** Computed vector, set when status is `complete`. */
60
+ vector: EmbeddingVector | null;
61
+ /** Model id, set when status is `complete`. */
62
+ model: string | null;
63
+ }
64
+ /** Persist callback — the queue does not know how to write the vector. */
65
+ export type PersistEmbeddingFn = (nodeId: string, vector: EmbeddingVector, model: string) => Promise<void>;
66
+ /** Status-change callback — used by MemOS to track per-node status. */
67
+ export type EmbeddingStatusChangeFn = (nodeId: string, status: EmbeddingJobStatus, error: string | null) => void | Promise<void>;
68
+ /** Configuration for EmbeddingQueue. */
69
+ export interface EmbeddingQueueConfig {
70
+ /** Embedding provider. Required. */
71
+ provider: EmbeddingProvider;
72
+ /** Maximum number of concurrent in-flight embed calls. Default 2. */
73
+ concurrency?: number;
74
+ /**
75
+ * Maximum number of pending jobs drained into a single `batchEmbed()`
76
+ * call when the provider supports batching (e.g. Ollama and
77
+ * OpenAI-compatible servers accept array input). Default 1 = one
78
+ * `embed()` call per job (legacy behavior). Groups that fail as a batch
79
+ * fall back to the per-job retry path.
80
+ */
81
+ batchSize?: number;
82
+ /** Maximum number of jobs buffered when workers are saturated. Default 10000. */
83
+ maxQueueSize?: number;
84
+ /** Maximum number of retry attempts per job. Default 3. */
85
+ maxRetries?: number;
86
+ /** Base backoff in ms; retries use exponential backoff. Default 250. */
87
+ retryBackoffMs?: number;
88
+ /**
89
+ * Coalescing delay in ms before a fully-idle queue picks up newly-enqueued
90
+ * work. Default 5.
91
+ *
92
+ * Why: `schedule()` used to defer pickup with a bare `queueMicrotask`,
93
+ * so sequential `await store()` calls each formed a single-job group and
94
+ * `batchEmbed` was never called — one provider round trip per memory.
95
+ * A short linger lets rapid sequential enqueues coalesce into full
96
+ * `batchSize` groups (N round trips become N/batchSize). The linger only
97
+ * applies when no worker is busy; while workers run, pending jobs
98
+ * accumulate on their own and are picked up immediately. Ordering and
99
+ * completion guarantees are unchanged; `flush()` still drains
100
+ * everything. Set to 0 to restore the immediate microtask pickup.
101
+ */
102
+ batchLingerMs?: number;
103
+ /** Persist callback — invoked once per job on success. Optional. */
104
+ onPersist?: PersistEmbeddingFn;
105
+ /** Status-change callback — fires on every state transition. Optional. */
106
+ onStatusChange?: EmbeddingStatusChangeFn;
107
+ }
108
+ /**
109
+ * Bounded, retryable, non-blocking embedding queue.
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const queue = new EmbeddingQueue({
114
+ * provider,
115
+ * concurrency: 4,
116
+ * onPersist: async (nodeId, vector, model) => {
117
+ * await storage.saveEmbedding(nodeId, vector, model);
118
+ * },
119
+ * });
120
+ *
121
+ * const jobId = queue.enqueue(node.id, text);
122
+ * await queue.flush();
123
+ * await queue.close();
124
+ * ```
125
+ */
126
+ export declare class EmbeddingQueue {
127
+ private readonly provider;
128
+ private readonly concurrency;
129
+ private readonly batchSize;
130
+ private readonly maxQueueSize;
131
+ private readonly maxRetries;
132
+ private readonly retryBackoffMs;
133
+ private readonly batchLingerMs;
134
+ private readonly onPersist?;
135
+ private readonly onStatusChange?;
136
+ /** Pending jobs not yet picked up by a worker. */
137
+ private pending;
138
+ /** Currently running job groups, by worker slot index. */
139
+ private readonly running;
140
+ /** Job index by id, for status queries. */
141
+ private readonly jobs;
142
+ /** Resolvers for `flush()` callers, resolved when the queue drains. */
143
+ private readonly drainWaiters;
144
+ /** Set true after `close()` so enqueue can reject and workers exit. */
145
+ private closed;
146
+ /** Guards one-at-a-time deferred group pickup (see schedule()). */
147
+ private scheduleQueued;
148
+ constructor(config: EmbeddingQueueConfig);
149
+ /**
150
+ * Enqueue an embedding job. Returns a job id immediately.
151
+ *
152
+ * @throws if the queue is full or has been closed.
153
+ */
154
+ enqueue(nodeId: string, text: string): string;
155
+ /** Number of jobs currently waiting or running. */
156
+ size(): number;
157
+ /** Number of jobs currently being processed. */
158
+ activeCount(): number;
159
+ /** Return a snapshot of a job by id, or null if unknown. */
160
+ getJob(id: string): EmbeddingJob | null;
161
+ /** Return all jobs that are not in a terminal state. */
162
+ pendingJobs(): EmbeddingJob[];
163
+ /**
164
+ * Wait for the queue to drain. Resolves when both `pending` and
165
+ * `running` are empty. If the queue is already drained, resolves on
166
+ * the next microtask.
167
+ */
168
+ flush(): Promise<void>;
169
+ /**
170
+ * Permanently stop the queue. In-flight jobs are allowed to finish
171
+ * (and their persist callbacks run) but no new work is accepted.
172
+ */
173
+ close(): Promise<void>;
174
+ private notifyStatus;
175
+ /**
176
+ * Pull pending jobs into idle worker slots. The actual pickup is
177
+ * deferred by one microtask so a synchronous enqueue burst (e.g. an
178
+ * ingestion loop storing N memories back-to-back) coalesces into
179
+ * full `batchSize` groups instead of starting a half-empty group per
180
+ * enqueue.
181
+ */
182
+ private schedule;
183
+ private scheduleNow;
184
+ private runGroup;
185
+ private runJobWithRetries;
186
+ /** After a worker slot frees, schedule the next pending job and
187
+ * resolve any flush() waiters that are now satisfied. */
188
+ private afterJob;
189
+ }
190
+ //# sourceMappingURL=embedding-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding-queue.d.ts","sourceRoot":"","sources":["../src/embedding-queue.ts"],"names":[],"mappings":"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"}