@tensakulabs/memory 0.1.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.
package/dist/stage.js ADDED
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env bun
2
+ // @bun
3
+ var __defProp = Object.defineProperty;
4
+ var __export = (target, all) => {
5
+ for (var name in all)
6
+ __defProp(target, name, {
7
+ get: all[name],
8
+ enumerable: true,
9
+ configurable: true,
10
+ set: (newValue) => all[name] = () => newValue
11
+ });
12
+ };
13
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
14
+ var __require = import.meta.require;
15
+
16
+ // lib.ts
17
+ var exports_lib = {};
18
+ __export(exports_lib, {
19
+ readJsonl: () => readJsonl,
20
+ loadConfig: () => loadConfig,
21
+ generateId: () => generateId,
22
+ coldSearch: () => coldSearch,
23
+ coldAdd: () => coldAdd
24
+ });
25
+ import { readFileSync, existsSync } from "fs";
26
+ import { join, resolve, dirname } from "path";
27
+ var {$ } = globalThis.Bun;
28
+ function loadConfig(explicitPath) {
29
+ const home = process.env.HOME || "~";
30
+ const candidates = [];
31
+ if (explicitPath) {
32
+ candidates.push(resolve(explicitPath));
33
+ }
34
+ if (process.env.MEMORY_CONFIG) {
35
+ candidates.push(resolve(process.env.MEMORY_CONFIG));
36
+ }
37
+ candidates.push(join(home, ".pai", "memory", CONFIG_FILENAME));
38
+ candidates.push(join(dirname(Bun.main), CONFIG_FILENAME));
39
+ candidates.push(resolve(CONFIG_FILENAME));
40
+ const configPath = candidates.find((p) => existsSync(p));
41
+ if (!configPath) {
42
+ console.error(`Config not found. Searched:`);
43
+ for (const c of candidates)
44
+ console.error(` - ${c}`);
45
+ console.error(`
46
+ Run: memory init`);
47
+ process.exit(1);
48
+ }
49
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
50
+ const configDir = dirname(configPath);
51
+ const config = {
52
+ agent: raw.agent || "unknown",
53
+ hot: {
54
+ path: resolvePath(configDir, raw.hot?.path || "./MEMORY.md")
55
+ },
56
+ warm: {
57
+ path: resolvePath(configDir, raw.warm?.path || "./warm.jsonl"),
58
+ ttlDays: raw.warm?.ttlDays ?? 7
59
+ },
60
+ cold: {
61
+ mode: raw.cold?.mode || "mcp",
62
+ endpoint: raw.cold?.endpoint,
63
+ userId: raw.cold?.userId || "justin"
64
+ },
65
+ remSleep: {
66
+ maxColdWrites: raw.remSleep?.maxColdWrites ?? 5,
67
+ dedupThreshold: raw.remSleep?.dedupThreshold ?? 0.85,
68
+ stagingPath: resolvePath(configDir, raw.remSleep?.stagingPath || "./rem-staging.jsonl")
69
+ }
70
+ };
71
+ if (config.cold.mode === "http" && !config.cold.endpoint) {
72
+ console.error("cold.mode is 'http' but no cold.endpoint specified.");
73
+ process.exit(1);
74
+ }
75
+ return { config, configDir };
76
+ }
77
+ function resolvePath(base, p) {
78
+ if (p.startsWith("/") || p.startsWith("~"))
79
+ return p.replace(/^~/, process.env.HOME || "~");
80
+ return resolve(base, p);
81
+ }
82
+ async function coldSearch(config, query) {
83
+ if (config.cold.mode === "mcp") {
84
+ return coldSearchMcp(query, config.cold.userId);
85
+ } else {
86
+ return coldSearchHttp(config.cold.endpoint, query, config.cold.userId);
87
+ }
88
+ }
89
+ async function coldAdd(config, fact) {
90
+ if (config.cold.mode === "mcp") {
91
+ return coldAddMcp(fact, config.cold.userId);
92
+ } else {
93
+ return coldAddHttp(config.cold.endpoint, fact, config.cold.userId);
94
+ }
95
+ }
96
+ async function coldSearchMcp(query, userId) {
97
+ try {
98
+ const escaped = query.replace(/"/g, "\\\"");
99
+ const prompt = `Search mem0 for this fact and report if it already exists. Use search_memories with query: "${escaped}" and user_id "${userId}". If results exist with high relevance, respond ONLY with: FOUND|<score>|<matching text>. If no close match, respond ONLY with: NOT_FOUND|0|none. Do not explain.`;
100
+ const result = await $`claude -p ${prompt} --allowedTools "mcp__mem0__search_memories" 2>/dev/null`.text();
101
+ const line = result.trim().split(`
102
+ `).pop() || "";
103
+ if (line.startsWith("FOUND|")) {
104
+ const parts = line.split("|");
105
+ return { score: parseFloat(parts[1]) || 0, text: parts[2] || "" };
106
+ }
107
+ return { score: 0, text: "" };
108
+ } catch {
109
+ return { score: 0, text: "" };
110
+ }
111
+ }
112
+ async function coldAddMcp(fact, userId) {
113
+ try {
114
+ const escaped = fact.replace(/"/g, "\\\"");
115
+ const prompt = `Add this fact to mem0. Use add_memory with text: "${escaped}" and user_id "${userId}". Respond ONLY with: DONE or FAILED.`;
116
+ const result = await $`claude -p ${prompt} --allowedTools "mcp__mem0__add_memory" 2>/dev/null`.text();
117
+ return result.trim().includes("DONE");
118
+ } catch {
119
+ return false;
120
+ }
121
+ }
122
+ async function coldSearchHttp(endpoint, query, userId) {
123
+ try {
124
+ const res = await fetch(`${endpoint}/v1/memories/search/`, {
125
+ method: "POST",
126
+ headers: { "Content-Type": "application/json" },
127
+ body: JSON.stringify({ query, user_id: userId })
128
+ });
129
+ if (!res.ok)
130
+ return { score: 0, text: "" };
131
+ const data = await res.json();
132
+ const top = data.results?.[0];
133
+ if (top) {
134
+ return { score: top.score ?? 0, text: top.memory ?? "" };
135
+ }
136
+ return { score: 0, text: "" };
137
+ } catch {
138
+ return { score: 0, text: "" };
139
+ }
140
+ }
141
+ async function coldAddHttp(endpoint, fact, userId) {
142
+ try {
143
+ const res = await fetch(`${endpoint}/v1/memories/`, {
144
+ method: "POST",
145
+ headers: { "Content-Type": "application/json" },
146
+ body: JSON.stringify({ text: fact, user_id: userId })
147
+ });
148
+ return res.ok;
149
+ } catch {
150
+ return false;
151
+ }
152
+ }
153
+ function generateId() {
154
+ return `rem-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
155
+ }
156
+ function readJsonl(path) {
157
+ if (!existsSync(path))
158
+ return [];
159
+ return readFileSync(path, "utf-8").trim().split(`
160
+ `).filter(Boolean).map((line) => JSON.parse(line));
161
+ }
162
+ var CONFIG_FILENAME = "memory-config.json";
163
+ var init_lib = () => {};
164
+
165
+ // stage.ts
166
+ var exports_stage = {};
167
+ __export(exports_stage, {
168
+ run: () => run
169
+ });
170
+ import { appendFileSync } from "fs";
171
+ function parseArgs(args) {
172
+ if (args.includes("--list"))
173
+ return { command: "list" };
174
+ if (args.includes("--count"))
175
+ return { command: "count" };
176
+ const fact = args.find((a) => !a.startsWith("--"));
177
+ if (!fact) {
178
+ console.error('Usage: memory stage "fact text" [--context "..."] [--tier hot|warm|cold]');
179
+ process.exit(1);
180
+ }
181
+ let context = "";
182
+ const ctxIdx = args.indexOf("--context");
183
+ if (ctxIdx !== -1 && args[ctxIdx + 1])
184
+ context = args[ctxIdx + 1];
185
+ let tierHint = "auto";
186
+ const tierIdx = args.indexOf("--tier");
187
+ if (tierIdx !== -1 && args[tierIdx + 1]) {
188
+ const t = args[tierIdx + 1];
189
+ if (t === "hot" || t === "warm" || t === "cold")
190
+ tierHint = t;
191
+ }
192
+ return { command: "stage", fact, context, tierHint };
193
+ }
194
+ async function run(args) {
195
+ const { config } = loadConfig();
196
+ const STAGING = config.remSleep.stagingPath;
197
+ const parsed = parseArgs(args);
198
+ if (parsed.command === "list") {
199
+ const entries = readJsonl(STAGING).filter((e) => !e.processed);
200
+ if (entries.length === 0) {
201
+ console.log("No staged facts.");
202
+ } else {
203
+ for (const e of entries) {
204
+ const hint = e.tier_hint === "auto" ? "" : ` [${e.tier_hint}]`;
205
+ console.log(`${e.id}: ${e.fact}${hint}`);
206
+ }
207
+ console.log(`
208
+ ${entries.length} fact(s) pending.`);
209
+ }
210
+ } else if (parsed.command === "count") {
211
+ console.log(readJsonl(STAGING).filter((e) => !e.processed).length);
212
+ } else {
213
+ const entry = {
214
+ id: generateId(),
215
+ fact: parsed.fact,
216
+ timestamp: new Date().toISOString(),
217
+ context: parsed.context || "",
218
+ tier_hint: parsed.tierHint || "auto",
219
+ processed: false
220
+ };
221
+ appendFileSync(STAGING, JSON.stringify(entry) + `
222
+ `);
223
+ console.log(`Staged: ${entry.id}`);
224
+ }
225
+ }
226
+ var init_stage = __esm(() => {
227
+ init_lib();
228
+ if (import.meta.main) {
229
+ run(process.argv.slice(2));
230
+ }
231
+ });
232
+ init_stage();
233
+
234
+ export {
235
+ run
236
+ };
@@ -0,0 +1,19 @@
1
+ {
2
+ "agent": "atlas",
3
+ "hot": {
4
+ "path": "/home/openclaw/memory/MEMORY.md"
5
+ },
6
+ "warm": {
7
+ "path": "/home/openclaw/memory/warm.jsonl"
8
+ },
9
+ "cold": {
10
+ "mode": "http",
11
+ "endpoint": "http://localhost:8080",
12
+ "userId": "justin"
13
+ },
14
+ "remSleep": {
15
+ "maxColdWrites": 5,
16
+ "dedupThreshold": 0.85,
17
+ "stagingPath": "/home/openclaw/memory/rem-staging.jsonl"
18
+ }
19
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "agent": "sage",
3
+ "hot": {
4
+ "path": "../MEMORY.md"
5
+ },
6
+ "warm": {
7
+ "path": "./warm.jsonl"
8
+ },
9
+ "cold": {
10
+ "mode": "mcp",
11
+ "userId": "justin"
12
+ },
13
+ "remSleep": {
14
+ "maxColdWrites": 5,
15
+ "dedupThreshold": 0.85,
16
+ "stagingPath": "./rem-staging.jsonl"
17
+ }
18
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "agent": "your-agent-name",
3
+ "hot": {
4
+ "path": "./MEMORY.md"
5
+ },
6
+ "warm": {
7
+ "path": "./warm.jsonl"
8
+ },
9
+ "cold": {
10
+ "mode": "mcp",
11
+ "endpoint": "http://localhost:8080",
12
+ "userId": "your-user-id"
13
+ },
14
+ "remSleep": {
15
+ "maxColdWrites": 5,
16
+ "dedupThreshold": 0.85,
17
+ "stagingPath": "./rem-staging.jsonl"
18
+ }
19
+ }
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@tensakulabs/memory",
3
+ "version": "0.1.0",
4
+ "description": "Three-tier memory system (hot/warm/cold) with REM Sleep batch consolidation for AI agents",
5
+ "type": "module",
6
+ "main": "./dist/lib.js",
7
+ "types": "./dist/lib.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/lib.js",
11
+ "types": "./dist/lib.d.ts"
12
+ },
13
+ "./stage": {
14
+ "import": "./dist/stage.js",
15
+ "types": "./dist/stage.d.ts"
16
+ },
17
+ "./rem-sleep": {
18
+ "import": "./dist/rem-sleep.js",
19
+ "types": "./dist/rem-sleep.d.ts"
20
+ }
21
+ },
22
+ "bin": {
23
+ "memory": "./dist/cli.js"
24
+ },
25
+ "scripts": {
26
+ "build": "bun build cli.ts lib.ts stage.ts rem-sleep.ts --outdir dist --target bun && tsc --emitDeclarationOnly",
27
+ "prepublishOnly": "bun run build",
28
+ "typecheck": "tsc --noEmit"
29
+ },
30
+ "files": [
31
+ "dist/",
32
+ "memory-config.example.json",
33
+ "examples/",
34
+ "README.md"
35
+ ],
36
+ "engines": {
37
+ "bun": ">=1.0.0"
38
+ },
39
+ "keywords": ["memory", "ai", "agent", "mem0", "batch", "three-tier", "rem-sleep"],
40
+ "author": "Tensaku Labs",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/tensakulabs/memory.git"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/tensakulabs/memory/issues"
48
+ },
49
+ "homepage": "https://github.com/tensakulabs/memory#readme",
50
+ "devDependencies": {
51
+ "bun-types": "^1.3.9",
52
+ "typescript": "^5.9.3"
53
+ }
54
+ }