Skip to content

Commit c7fc7e1

Browse files
committed
feat: added delete memories
1 parent ae737a3 commit c7fc7e1

File tree

8 files changed

+106
-4
lines changed

8 files changed

+106
-4
lines changed

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ from openmemory.client import Memory
5353
mem = Memory()
5454
mem.add("user prefers dark mode", user_id="u1")
5555
results = mem.search("preferences", user_id="u1")
56+
await mem.delete("memory_id")
5657
```
5758

5859
> Note: `add`, `search`, `get`, `delete` are async. Use `await` in async contexts.
@@ -101,6 +102,7 @@ import { Memory } from "openmemory-js"
101102
const mem = new Memory()
102103
await mem.add("user likes spicy food", { user_id: "u1" })
103104
const results = await mem.search("food?", { user_id: "u1" })
105+
await mem.delete("memory_id")
104106
```
105107

106108
Drop this into:
@@ -388,17 +390,31 @@ The `opm` CLI talks directly to the engine / server.
388390
### Install
389391

390392
```bash
391-
cd backend
393+
cd packages/openmemory-js
392394
npm install
395+
npm run build
393396
npm link # adds `opm` to your PATH
394397
```
395398

399+
### Usage
400+
401+
```bash
402+
# Start the API server
403+
opm serve
404+
405+
# In another terminal:
406+
opm health
407+
opm add "Recall that I prefer TypeScript over Python" --tags preference
408+
opm query "language preference"
409+
```
410+
396411
### Commands
397412

398413
```bash
399414
opm add "user prefers dark mode" --user u1 --tags prefs
400415
opm query "preferences" --user u1 --limit 5
401416
opm list --user u1
417+
opm delete <id>
402418
opm reinforce <id>
403419
opm stats
404420
```

packages/openmemory-js/bin/opm.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ commands:
3838
user <id> get user summary
3939
health ping server
4040
mcp start mcp server (stdio)
41+
serve start api server
4142
4243
options:
4344
--user <id> user id
@@ -256,6 +257,16 @@ for (let i = 1; i < argv.length; i++) {
256257
process.exit(1);
257258
}
258259
break;
260+
case 'serve':
261+
try {
262+
console.log('[opm] passing control to server...');
263+
require('../dist/server/index.js');
264+
} catch (e) {
265+
console.error('[error] failed to start server. ensure project is built (npm run build).');
266+
console.error(e.message);
267+
process.exit(1);
268+
}
269+
break;
259270
default:
260271
console.error(`[error] unknown command: ${cmd}`);
261272
console.log(helptext);

packages/openmemory-js/src/ai/mcp.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
add_hsg_memory,
99
hsg_query,
1010
reinforce_memory,
11+
delete_memory,
1112
sector_configs,
1213
} from "../memory/hsg";
1314
import { q, all_async, memories_table, vector_store } from "../core/db";
@@ -444,6 +445,37 @@ export const create_mcp_srv = () => {
444445
},
445446
);
446447

448+
registry.tool(
449+
"openmemory_delete",
450+
"Delete a memory by identifier",
451+
{
452+
id: z.string().min(1).describe("Memory identifier to delete"),
453+
user_id: z.string().trim().min(1).optional().describe("Validate ownership"),
454+
},
455+
async ({ id, user_id }) => {
456+
const u = uid(user_id);
457+
if (u) {
458+
// Pre-check ownership if user_id provided
459+
const mem = await q.get_mem.get(id);
460+
if (mem && mem.user_id !== u) {
461+
throw new Error(`Memory ${id} not found for user ${u}`);
462+
}
463+
}
464+
465+
const success = await delete_memory(id);
466+
if (!success) {
467+
return {
468+
content: [{ type: "text", text: `Memory ${id} not found or could not be deleted.` }],
469+
isError: true
470+
};
471+
}
472+
473+
return {
474+
content: [{ type: "text", text: `Memory ${id} successfully deleted.` }],
475+
};
476+
},
477+
);
478+
447479
registry.tool(
448480
"openmemory_list",
449481
"List recent memories for quick inspection",

packages/openmemory-js/src/memory/hsg.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,21 @@ export async function add_hsg_memory(
11551155
throw error;
11561156
}
11571157
}
1158+
export async function delete_memory(id: string): Promise<boolean> {
1159+
const mem = await q.get_mem.get(id);
1160+
if (!mem) return false;
1161+
await transaction.begin();
1162+
try {
1163+
await q.del_mem.run(id);
1164+
await q.del_waypoints.run(id, id);
1165+
await vector_store.deleteVectors(id);
1166+
await transaction.commit();
1167+
return true;
1168+
} catch (error) {
1169+
await transaction.rollback();
1170+
throw error;
1171+
}
1172+
}
11581173
export async function reinforce_memory(
11591174
id: string,
11601175
boost: number = 0.1,

packages/openmemory-js/src/ops/compress.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ class MemoryCompressionEngine {
214214
if (!t || t.length < 50) return this.compress(t, "semantic");
215215
const code =
216216
/\b(function|const|let|var|def|class|import|export)\b/.test(t);
217-
const urls = /https?:\/\
217+
const urls = /https?:\/\//.test(t);
218218
const verb = t.split(/\s+/).length > 100;
219219
let a: "semantic" | "syntactic" | "aggressive";
220220
if (code || urls) a = "aggressive";

packages/openmemory-js/src/sources/github.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export class github_source extends base_source {
3434
}
3535

3636
const [owner, repo] = filters.repo.split('/');
37-
const path = filters.path?.replace(/^\
37+
const path = filters.path?.replace(/^\//, "") || "";
3838
const include_issues = filters.include_issues || false;
3939

4040
const results: source_item[] = [];

packages/openmemory-js/src/sources/web_crawler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ export class web_crawler_source extends base_source {
9999
const full_url = new URL(href, url);
100100
if (full_url.hostname !== base_domain) return;
101101

102-
const clean_url = `${full_url.protocol}
102+
const clean_url = `${full_url.protocol}//${full_url.host}${full_url.pathname}`;
103103
if (!this.visited.has(clean_url)) {
104104
to_visit.push({ url: clean_url, depth: depth + 1 });
105105
}

packages/openmemory-py/src/openmemory/ai/mcp.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ async def handle_list_tools() -> list[Tool]:
6363
},
6464
"required": ["id"]
6565
}
66+
}
67+
),
68+
Tool(
69+
name="openmemory_delete",
70+
description="Delete a memory by ID",
71+
inputSchema={
72+
"type": "object",
73+
"properties": {
74+
"id": {"type": "string"},
75+
"user_id": {"type": "string"}
76+
},
77+
"required": ["id"]
78+
}
6679
),
6780
Tool(
6881
name="openmemory_list",
@@ -121,6 +134,21 @@ async def handle_call_tool(name: str, arguments: dict | None) -> list[TextConten
121134
return [TextContent(type="text", text=f"Memory {mid} not found")]
122135
return [TextContent(type="text", text=json.dumps(dict(m), default=str, indent=2))]
123136

137+
elif name == "openmemory_delete":
138+
mid = args.get("id")
139+
uid = args.get("user_id")
140+
141+
# Check exist/ownership
142+
m = await mem.get(mid)
143+
if not m:
144+
return [TextContent(type="text", text=f"Memory {mid} not found")]
145+
146+
if uid and m["user_id"] != uid:
147+
return [TextContent(type="text", text=f"Memory {mid} not found for user {uid}")]
148+
149+
await mem.delete(mid)
150+
return [TextContent(type="text", text=f"Memory {mid} deleted")]
151+
124152
elif name == "openmemory_list":
125153
limit = args.get("limit", 20)
126154
uid = args.get("user_id")

0 commit comments

Comments
 (0)