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/graph.js ADDED
@@ -0,0 +1,482 @@
1
+ /**
2
+ * In-memory graph engine for MemOS.
3
+ *
4
+ * Maintains an adjacency-list representation of the memory graph for
5
+ * fast traversal, clustering, and auto-linking. The graph mirrors
6
+ * what is persisted in the storage backend.
7
+ *
8
+ * @module @memos/graph
9
+ */
10
+ /**
11
+ * Default per-node cap on edges created by `autoLink()`.
12
+ *
13
+ * Without a cap, a corpus of similar memories converges to a complete
14
+ * graph (K_N): every new node links to every existing one, so edge
15
+ * writes grow quadratically and dominate ingestion time. 16 links is
16
+ * enough to preserve local cluster structure for `findClusters()` /
17
+ * `injectContext()` while keeping bulk ingestion near-linear. Pass `0`
18
+ * to `autoLink()` to restore uncapped (legacy) behavior.
19
+ */
20
+ export const DEFAULT_AUTO_LINK_CAP = 16;
21
+ /**
22
+ * Bag-of-words cosine similarity between two strings.
23
+ *
24
+ * This is intentionally lightweight — no external embedding model needed.
25
+ * For production use-cases requiring semantic similarity, supply your own
26
+ * embedding-based linker via the adapter interface.
27
+ *
28
+ * @param a — First text.
29
+ * @param b — Second text.
30
+ * @returns Cosine similarity in [0, 1].
31
+ */
32
+ export function textSimilarity(a, b) {
33
+ const tokensA = bagOfWords(a);
34
+ const tokensB = bagOfWords(b);
35
+ if (tokensA.size === 0 || tokensB.size === 0)
36
+ return 0;
37
+ return tokenSetSimilarity(tokensA, tokensB);
38
+ }
39
+ /**
40
+ * Cosine similarity of two pre-tokenized bags (binary term frequency).
41
+ * Exported for reuse by GraphEngine.autoLink, which tokenizes the new node
42
+ * once and reuses cached token sets for existing nodes instead of
43
+ * re-tokenizing both sides on every comparison.
44
+ */
45
+ export function tokenSetSimilarity(tokensA, tokensB) {
46
+ if (tokensA.size === 0 || tokensB.size === 0)
47
+ return 0;
48
+ let intersection = 0;
49
+ // Iterate the smaller set for fewer hash lookups.
50
+ const [small, large] = tokensA.size <= tokensB.size ? [tokensA, tokensB] : [tokensB, tokensA];
51
+ for (const token of small) {
52
+ if (large.has(token))
53
+ intersection += 1;
54
+ }
55
+ return intersection / Math.sqrt(tokensA.size * tokensB.size);
56
+ }
57
+ /**
58
+ * Tokenise text into a set of lowercased, de-stopped words.
59
+ */
60
+ // Module-level: bagOfWords runs O(N) times per autoLink() call, so the
61
+ // ~100-word Set must not be re-allocated on every invocation.
62
+ const BAG_OF_WORDS_STOP_WORDS = new Set([
63
+ "a",
64
+ "an",
65
+ "the",
66
+ "is",
67
+ "are",
68
+ "was",
69
+ "were",
70
+ "be",
71
+ "been",
72
+ "being",
73
+ "have",
74
+ "has",
75
+ "had",
76
+ "do",
77
+ "does",
78
+ "did",
79
+ "will",
80
+ "would",
81
+ "shall",
82
+ "should",
83
+ "may",
84
+ "might",
85
+ "must",
86
+ "can",
87
+ "could",
88
+ "to",
89
+ "of",
90
+ "in",
91
+ "for",
92
+ "on",
93
+ "with",
94
+ "at",
95
+ "by",
96
+ "from",
97
+ "as",
98
+ "into",
99
+ "through",
100
+ "during",
101
+ "before",
102
+ "after",
103
+ "above",
104
+ "below",
105
+ "between",
106
+ "and",
107
+ "but",
108
+ "or",
109
+ "nor",
110
+ "not",
111
+ "so",
112
+ "yet",
113
+ "both",
114
+ "either",
115
+ "neither",
116
+ "each",
117
+ "every",
118
+ "all",
119
+ "any",
120
+ "few",
121
+ "more",
122
+ "most",
123
+ "other",
124
+ "some",
125
+ "such",
126
+ "no",
127
+ "only",
128
+ "own",
129
+ "same",
130
+ "than",
131
+ "too",
132
+ "very",
133
+ "just",
134
+ "because",
135
+ "about",
136
+ "up",
137
+ "out",
138
+ "it",
139
+ "its",
140
+ "this",
141
+ "that",
142
+ "these",
143
+ "those",
144
+ "i",
145
+ "me",
146
+ "my",
147
+ "we",
148
+ "our",
149
+ "you",
150
+ "your",
151
+ "he",
152
+ "him",
153
+ "his",
154
+ "she",
155
+ "her",
156
+ "they",
157
+ "them",
158
+ "their",
159
+ ]);
160
+ function bagOfWords(text) {
161
+ return new Set(text
162
+ .toLowerCase()
163
+ .replace(/[^a-z0-9\s]/g, "")
164
+ .split(/\s+/)
165
+ .filter((w) => w.length > 1 && !BAG_OF_WORDS_STOP_WORDS.has(w)));
166
+ }
167
+ /**
168
+ * Bitemporal validity predicate: an edge is valid at `timeMs` when its
169
+ * event-time interval covers it. NULL/undefined bounds are open-ended.
170
+ * (eqeqeq-safe explicit null checks — the lint config forbids `== null`.)
171
+ */
172
+ function isEdgeValidAt(edge, timeMs) {
173
+ const fromOpen = edge.validFrom === undefined || edge.validFrom === null;
174
+ const toOpen = edge.validTo === undefined || edge.validTo === null;
175
+ return ((fromOpen || edge.validFrom <= timeMs) &&
176
+ (toOpen || edge.validTo > timeMs));
177
+ }
178
+ /**
179
+ * Graph engine managing the in-memory representation of the memory graph.
180
+ */
181
+ export class GraphEngine {
182
+ /** Adjacency list: nodeId → Set of edge IDs. */
183
+ adjacency = new Map();
184
+ /** Edge lookup: edgeId → MemoryEdge. */
185
+ edges = new Map();
186
+ /** Node lookup: nodeId → MemoryNode. */
187
+ nodes = new Map();
188
+ /**
189
+ * Lazily-computed bag-of-words token sets per node, for autoLink().
190
+ * Populated on first use and invalidated on update/remove — avoids
191
+ * re-tokenizing every existing node on every store().
192
+ */
193
+ tokenCache = new Map();
194
+ /**
195
+ * Add a node to the graph.
196
+ *
197
+ * @param node — The memory node to add.
198
+ */
199
+ addNode(node) {
200
+ this.nodes.set(node.id, node);
201
+ // Fresh content for this id — drop any cached token set.
202
+ this.tokenCache.delete(node.id);
203
+ if (!this.adjacency.has(node.id)) {
204
+ this.adjacency.set(node.id, new Set());
205
+ }
206
+ }
207
+ /**
208
+ * Remove a node and all its connected edges from the graph.
209
+ *
210
+ * @param id — Node ID to remove.
211
+ * @returns `true` if the node existed.
212
+ */
213
+ removeNode(id) {
214
+ if (!this.nodes.has(id))
215
+ return false;
216
+ // Remove connected edges
217
+ const edgeIds = this.adjacency.get(id);
218
+ if (edgeIds) {
219
+ for (const edgeId of [...edgeIds]) {
220
+ this.removeEdge(edgeId);
221
+ }
222
+ }
223
+ this.nodes.delete(id);
224
+ this.adjacency.delete(id);
225
+ this.tokenCache.delete(id);
226
+ return true;
227
+ }
228
+ /**
229
+ * Update a node in the graph.
230
+ */
231
+ updateNode(node) {
232
+ this.nodes.set(node.id, node);
233
+ // Content may have changed — drop the cached token set.
234
+ this.tokenCache.delete(node.id);
235
+ }
236
+ /**
237
+ * Add an edge to the graph.
238
+ *
239
+ * @param input — Edge creation input. `validFrom`/`validTo` define the
240
+ * bitemporal event-time validity interval (`createdAt` stays the
241
+ * transaction/ingest time); omitted bounds are open-ended.
242
+ * @returns The created edge.
243
+ */
244
+ addEdge(input) {
245
+ const edge = {
246
+ id: generateId(),
247
+ sourceId: input.sourceId,
248
+ targetId: input.targetId,
249
+ relation: input.relation ?? "relates_to",
250
+ weight: input.weight ?? 0.5,
251
+ metadata: input.metadata ?? {},
252
+ createdAt: Date.now(),
253
+ validFrom: input.validFrom ?? null,
254
+ validTo: input.validTo ?? null,
255
+ };
256
+ this.edges.set(edge.id, edge);
257
+ if (!this.adjacency.has(edge.sourceId)) {
258
+ this.adjacency.set(edge.sourceId, new Set());
259
+ }
260
+ this.adjacency.get(edge.sourceId).add(edge.id);
261
+ if (!this.adjacency.has(edge.targetId)) {
262
+ this.adjacency.set(edge.targetId, new Set());
263
+ }
264
+ this.adjacency.get(edge.targetId).add(edge.id);
265
+ return edge;
266
+ }
267
+ /**
268
+ * Remove an edge from the graph.
269
+ */
270
+ removeEdge(id) {
271
+ const edge = this.edges.get(id);
272
+ if (!edge)
273
+ return false;
274
+ this.adjacency.get(edge.sourceId)?.delete(id);
275
+ this.adjacency.get(edge.targetId)?.delete(id);
276
+ this.edges.delete(id);
277
+ return true;
278
+ }
279
+ /**
280
+ * Lazily-computed bag-of-words token set for a node, cached across
281
+ * autoLink() calls. The cache is invalidated on add/update/remove/clear.
282
+ */
283
+ tokensFor(node) {
284
+ let tokens = this.tokenCache.get(node.id);
285
+ if (!tokens) {
286
+ tokens = bagOfWords(node.content);
287
+ this.tokenCache.set(node.id, tokens);
288
+ }
289
+ return tokens;
290
+ }
291
+ /**
292
+ * Get all edges connected to a node.
293
+ */
294
+ getEdgesForNode(nodeId) {
295
+ const edgeIds = this.adjacency.get(nodeId);
296
+ if (!edgeIds)
297
+ return [];
298
+ const result = [];
299
+ for (const edgeId of edgeIds) {
300
+ const edge = this.edges.get(edgeId);
301
+ if (edge)
302
+ result.push(edge);
303
+ }
304
+ return result;
305
+ }
306
+ /**
307
+ * Get neighbours of a node (nodes connected by an edge).
308
+ */
309
+ getNeighbours(nodeId) {
310
+ const edges = this.getEdgesForNode(nodeId);
311
+ const neighbourIds = new Set();
312
+ for (const edge of edges) {
313
+ if (edge.sourceId === nodeId)
314
+ neighbourIds.add(edge.targetId);
315
+ else
316
+ neighbourIds.add(edge.sourceId);
317
+ }
318
+ const result = [];
319
+ for (const id of neighbourIds) {
320
+ const node = this.nodes.get(id);
321
+ if (node)
322
+ result.push(node);
323
+ }
324
+ return result;
325
+ }
326
+ /**
327
+ * Attempt to auto-link a node to existing nodes based on text similarity.
328
+ *
329
+ * Cost control: linking is O(N) per store, and every accepted link is a
330
+ * persisted row. Without a per-node cap a repetitive corpus converges to
331
+ * a complete graph — K_N edges for N nodes (measured: 100 similar nodes
332
+ * → 4,950 edges, and 300 stores took 2.7s vs 0.1s with linking off).
333
+ * The cap keeps the most similar `maxLinksPerNode` edges and skips the
334
+ * long tail, which preserves cluster structure (`findClusters`,
335
+ * `injectContext`) while making ingestion cost linear-ish in practice.
336
+ *
337
+ * @param node — The node to link.
338
+ * @param threshold — Minimum similarity score to create an edge [0, 1].
339
+ * @param maxLinksPerNode — Hard cap on edges created per call.
340
+ * `0` disables the cap (legacy complete-graph behavior).
341
+ * @returns Array of created edges.
342
+ */
343
+ autoLink(node, threshold, maxLinksPerNode = DEFAULT_AUTO_LINK_CAP) {
344
+ const created = [];
345
+ // Collect candidates first so the cap keeps the BEST links (highest
346
+ // similarity) rather than whichever happened to be iterated first.
347
+ const candidates = [];
348
+ // Tokenize the new node ONCE and reuse cached token sets for existing
349
+ // nodes: previously both sides were re-tokenized on every comparison
350
+ // (2 x N bagOfWords calls per store).
351
+ const tokensNew = this.tokensFor(node);
352
+ for (const [id, existing] of this.nodes) {
353
+ if (id === node.id)
354
+ continue;
355
+ const sim = tokenSetSimilarity(tokensNew, this.tokensFor(existing));
356
+ if (sim >= threshold) {
357
+ candidates.push({ id, sim });
358
+ }
359
+ }
360
+ // Cap AFTER collecting, keeping the strongest links.
361
+ const selected = maxLinksPerNode > 0 && candidates.length > maxLinksPerNode
362
+ ? candidates.sort((a, b) => b.sim - a.sim).slice(0, maxLinksPerNode)
363
+ : candidates;
364
+ for (const { id, sim } of selected) {
365
+ const edge = this.addEdge({
366
+ sourceId: node.id,
367
+ targetId: id,
368
+ relation: "relates_to",
369
+ weight: sim,
370
+ });
371
+ created.push(edge);
372
+ }
373
+ return created;
374
+ }
375
+ /**
376
+ * Find clusters of strongly connected nodes using a simple BFS approach.
377
+ *
378
+ * @param minClusterSize — Minimum number of nodes per cluster.
379
+ * @returns Array of clusters (each cluster is an array of node IDs).
380
+ */
381
+ findClusters(minClusterSize = 2) {
382
+ const visited = new Set();
383
+ const clusters = [];
384
+ for (const nodeId of this.nodes.keys()) {
385
+ if (visited.has(nodeId))
386
+ continue;
387
+ const cluster = [];
388
+ const queue = [nodeId];
389
+ while (queue.length > 0) {
390
+ const current = queue.shift();
391
+ if (visited.has(current))
392
+ continue;
393
+ visited.add(current);
394
+ cluster.push(current);
395
+ const neighbours = this.getNeighbours(current);
396
+ for (const n of neighbours) {
397
+ if (!visited.has(n.id))
398
+ queue.push(n.id);
399
+ }
400
+ }
401
+ if (cluster.length >= minClusterSize) {
402
+ clusters.push(cluster);
403
+ }
404
+ }
405
+ return clusters;
406
+ }
407
+ /**
408
+ * Get a node by ID.
409
+ */
410
+ getNode(id) {
411
+ return this.nodes.get(id);
412
+ }
413
+ /**
414
+ * Get all nodes.
415
+ */
416
+ getAllNodes() {
417
+ return [...this.nodes.values()];
418
+ }
419
+ /**
420
+ * Get all edges.
421
+ */
422
+ getAllEdges() {
423
+ return [...this.edges.values()];
424
+ }
425
+ /**
426
+ * Bitemporal as-of read: edges whose event-time validity interval covers
427
+ * `timeMs`. NULL bounds are open-ended. Superseded edges (whose
428
+ * `validTo` was stamped by invalidation) are excluded for timestamps
429
+ * at or after their `validTo`, but returned by as-of reads at earlier
430
+ * times — nothing is deleted.
431
+ *
432
+ * @param timeMs — The as-of timestamp (Unix ms).
433
+ */
434
+ edgesValidAt(timeMs) {
435
+ return [...this.edges.values()].filter((edge) => isEdgeValidAt(edge, timeMs));
436
+ }
437
+ /**
438
+ * Close (invalidate) the currently-valid edges incident to a node by
439
+ * stamping `validTo`. In-memory edges are mutated in place so the mirror
440
+ * stays consistent with storage; nothing is deleted (add-only).
441
+ *
442
+ * @param nodeId — Node whose incident edges to close.
443
+ * @param timeMs — The invalidation timestamp (Unix ms), usually `now`.
444
+ * @returns The edges that were closed.
445
+ */
446
+ closeEdgesForNode(nodeId, timeMs) {
447
+ const closed = [];
448
+ for (const edge of this.getEdgesForNode(nodeId)) {
449
+ if (isEdgeValidAt(edge, timeMs)) {
450
+ edge.validTo = timeMs;
451
+ closed.push(edge);
452
+ }
453
+ }
454
+ return closed;
455
+ }
456
+ /**
457
+ * Node count.
458
+ */
459
+ get size() {
460
+ return this.nodes.size;
461
+ }
462
+ /**
463
+ * Clear the entire graph.
464
+ */
465
+ clear() {
466
+ this.nodes.clear();
467
+ this.edges.clear();
468
+ this.adjacency.clear();
469
+ this.tokenCache.clear();
470
+ }
471
+ }
472
+ /**
473
+ * Generate a UUID v4.
474
+ */
475
+ export function generateId() {
476
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
477
+ const r = (Math.random() * 16) | 0;
478
+ const v = c === "x" ? r : (r & 0x3) | 0x8;
479
+ return v.toString(16);
480
+ });
481
+ }
482
+ //# sourceMappingURL=graph.js.map
memos/_js/graph.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph.js","sourceRoot":"","sources":["../src/graph.ts"],"names":[],"mappings":"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"}
memos/_js/harness.d.ts ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Harness attribution for MemOS — "one memory, every harness."
3
+ *
4
+ * Every memory records the agent harness (Claude Code, Cline, Codex,
5
+ * Gemini CLI, OpenClaw, …) that wrote it, stamped deterministically at
6
+ * write time. Detection is environment-based — pure, synchronous, no
7
+ * LLM anywhere — so a fresh process on the same DB file attributes
8
+ * identically to the process that wrote the memory.
9
+ *
10
+ * Detection rules, in priority order:
11
+ *
12
+ * 1. `MEMOS_HARNESS` — explicit override. Wins over everything; this is
13
+ * also the supported seam for tests and for deployments where the
14
+ * harness is known but leaves no env marker.
15
+ * 2. Known harness env markers (prefix match, first hit wins in the
16
+ * table order below).
17
+ * 3. `"unknown"` — unattributed (plain SDK use, legacy rows, tests).
18
+ *
19
+ * The tag is *attribution*, not isolation: it says which harness
20
+ * authored a memory version. Cross-harness reads default to `all`;
21
+ * `recall({ harness })` / `search({ harness })` scope to one harness.
22
+ * When harness B updates a memory written by harness A, the old version
23
+ * keeps its A tag and the replacement carries B — supersession is
24
+ * version-scoped, so the version ledger stays honest.
25
+ *
26
+ * @module @memos/harness
27
+ */
28
+ /** Attribution value for memories written with no detectable harness. */
29
+ export declare const UNKNOWN_HARNESS = "unknown";
30
+ /** Environment variable that explicitly overrides harness detection. */
31
+ export declare const MEMOS_HARNESS_ENV = "MEMOS_HARNESS";
32
+ /**
33
+ * Known harness env markers. Order is priority: the first matching
34
+ * entry wins, so more specific harnesses must precede less specific
35
+ * ones. `match` is either an exact variable name or a prefix
36
+ * (trailing `_` means "prefix match").
37
+ */
38
+ export declare const HARNESS_MARKERS: ReadonlyArray<{
39
+ /** Exact env var name or `PREFIX_` for a prefix match. */
40
+ match: string;
41
+ /** Normalized harness slug stamped on memories. */
42
+ harness: string;
43
+ }>;
44
+ /**
45
+ * Normalize a raw harness name to a stable slug: trimmed, lowercased,
46
+ * whitespace/underscores collapsed to `-`. Empty input normalizes to
47
+ * {@link UNKNOWN_HARNESS}.
48
+ */
49
+ export declare function normalizeHarness(raw: string): string;
50
+ /**
51
+ * Deterministically detect the current harness from the environment.
52
+ *
53
+ * Pure function of its input — pass `process.env` in production, a
54
+ * stub record in tests. Scans each marker in priority order; the first
55
+ * env var that is set and non-empty wins. `MEMOS_HARNESS` always wins.
56
+ *
57
+ * @param env — Environment mapping. Defaults to `process.env`.
58
+ * @returns A harness slug (`"claude-code"`, `"cline"`, `"codex"`,
59
+ * `"gemini-cli"`, `"openclaw"`, a `MEMOS_HARNESS` slug, or
60
+ * `"unknown"`).
61
+ */
62
+ export declare function detectHarness(env?: NodeJS.ProcessEnv | Record<string, string | undefined>): string;
63
+ //# sourceMappingURL=harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.d.ts","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,yEAAyE;AACzE,eAAO,MAAM,eAAe,YAAY,CAAC;AAEzC,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,aAAa,CAAC;IAC1C,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;CACjB,CAOA,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOpD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,GAAG,GAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACxE,MAAM,CAsBR"}
memos/_js/harness.js ADDED
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Harness attribution for MemOS — "one memory, every harness."
3
+ *
4
+ * Every memory records the agent harness (Claude Code, Cline, Codex,
5
+ * Gemini CLI, OpenClaw, …) that wrote it, stamped deterministically at
6
+ * write time. Detection is environment-based — pure, synchronous, no
7
+ * LLM anywhere — so a fresh process on the same DB file attributes
8
+ * identically to the process that wrote the memory.
9
+ *
10
+ * Detection rules, in priority order:
11
+ *
12
+ * 1. `MEMOS_HARNESS` — explicit override. Wins over everything; this is
13
+ * also the supported seam for tests and for deployments where the
14
+ * harness is known but leaves no env marker.
15
+ * 2. Known harness env markers (prefix match, first hit wins in the
16
+ * table order below).
17
+ * 3. `"unknown"` — unattributed (plain SDK use, legacy rows, tests).
18
+ *
19
+ * The tag is *attribution*, not isolation: it says which harness
20
+ * authored a memory version. Cross-harness reads default to `all`;
21
+ * `recall({ harness })` / `search({ harness })` scope to one harness.
22
+ * When harness B updates a memory written by harness A, the old version
23
+ * keeps its A tag and the replacement carries B — supersession is
24
+ * version-scoped, so the version ledger stays honest.
25
+ *
26
+ * @module @memos/harness
27
+ */
28
+ /** Attribution value for memories written with no detectable harness. */
29
+ export const UNKNOWN_HARNESS = "unknown";
30
+ /** Environment variable that explicitly overrides harness detection. */
31
+ export const MEMOS_HARNESS_ENV = "MEMOS_HARNESS";
32
+ /**
33
+ * Known harness env markers. Order is priority: the first matching
34
+ * entry wins, so more specific harnesses must precede less specific
35
+ * ones. `match` is either an exact variable name or a prefix
36
+ * (trailing `_` means "prefix match").
37
+ */
38
+ export const HARNESS_MARKERS = [
39
+ { match: "CLAUDECODE", harness: "claude-code" },
40
+ { match: "CLAUDE_CODE_", harness: "claude-code" },
41
+ { match: "CLINE_", harness: "cline" },
42
+ { match: "CODEX_", harness: "codex" },
43
+ { match: "GEMINI_", harness: "gemini-cli" },
44
+ { match: "OPENCLAW_", harness: "openclaw" },
45
+ ];
46
+ /**
47
+ * Normalize a raw harness name to a stable slug: trimmed, lowercased,
48
+ * whitespace/underscores collapsed to `-`. Empty input normalizes to
49
+ * {@link UNKNOWN_HARNESS}.
50
+ */
51
+ export function normalizeHarness(raw) {
52
+ const slug = raw
53
+ .trim()
54
+ .toLowerCase()
55
+ .replace(/[\s_]+/g, "-")
56
+ .replace(/[^a-z0-9-]/g, "");
57
+ return slug.length > 0 ? slug : UNKNOWN_HARNESS;
58
+ }
59
+ /**
60
+ * Deterministically detect the current harness from the environment.
61
+ *
62
+ * Pure function of its input — pass `process.env` in production, a
63
+ * stub record in tests. Scans each marker in priority order; the first
64
+ * env var that is set and non-empty wins. `MEMOS_HARNESS` always wins.
65
+ *
66
+ * @param env — Environment mapping. Defaults to `process.env`.
67
+ * @returns A harness slug (`"claude-code"`, `"cline"`, `"codex"`,
68
+ * `"gemini-cli"`, `"openclaw"`, a `MEMOS_HARNESS` slug, or
69
+ * `"unknown"`).
70
+ */
71
+ export function detectHarness(env = process.env) {
72
+ const override = env[MEMOS_HARNESS_ENV];
73
+ if (override !== undefined && override.trim().length > 0) {
74
+ return normalizeHarness(override);
75
+ }
76
+ const names = Object.keys(env);
77
+ for (const marker of HARNESS_MARKERS) {
78
+ if (marker.match.endsWith("_")) {
79
+ // Prefix marker, e.g. CLAUDE_CODE_ matches CLAUDE_CODE_ENTRYPOINT.
80
+ const hit = names.some((name) => name.startsWith(marker.match) && (env[name] ?? "").trim().length > 0);
81
+ if (hit)
82
+ return marker.harness;
83
+ }
84
+ else {
85
+ const value = env[marker.match];
86
+ if (value !== undefined && value.trim().length > 0) {
87
+ return marker.harness;
88
+ }
89
+ }
90
+ }
91
+ return UNKNOWN_HARNESS;
92
+ }
93
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../src/harness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC;AAEzC,wEAAwE;AACxE,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAKvB;IACH,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE;IAC/C,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,aAAa,EAAE;IACjD,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE;IACrC,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE;IACrC,EAAE,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE;IAC3C,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,EAAE;CAC5C,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,MAAM,IAAI,GAAG,GAAG;SACb,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;AAClD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAG,GAA2D,OAAO,CAAC,GAAG;IAEzE,MAAM,QAAQ,GAAG,GAAG,CAAC,iBAAiB,CAAC,CAAC;IACxC,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzD,OAAO,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACpC,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,MAAM,MAAM,IAAI,eAAe,EAAE,CAAC;QACrC,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/B,mEAAmE;YACnE,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CACpB,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CACvE,CAAC;YACF,IAAI,GAAG;gBAAE,OAAO,MAAM,CAAC,OAAO,CAAC;QACjC,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnD,OAAO,MAAM,CAAC,OAAO,CAAC;YACxB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC"}