opencode-episodic-memory 0.1.2 → 0.1.3
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/README.md +71 -25
- package/package.json +4 -3
- package/plugin/episodic-memory.ts +19 -46
- package/skills/remembering-conversations/SKILL.md +2 -1
- package/src/cli.ts +78 -80
- package/src/format.ts +70 -0
- package/src/indexer.ts +7 -6
- package/src/parser.ts +4 -3
- package/src/reader.ts +19 -1
- package/src/store.ts +222 -33
- package/src/parser.test.ts +0 -64
- package/src/reader.test.ts +0 -205
- package/src/store.test.ts +0 -72
package/src/store.test.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { describe, test, expect, afterAll } from "bun:test";
|
|
2
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
3
|
-
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
import { openIndex, replaceSessionChunks, search, textSearch, getIndexedSession } from "./store";
|
|
6
|
-
|
|
7
|
-
const dir = mkdtempSync(join(tmpdir(), "episodic-store-test-"));
|
|
8
|
-
const db = openIndex(join(dir, "index.db"));
|
|
9
|
-
afterAll(() => rmSync(dir, { recursive: true, force: true }));
|
|
10
|
-
|
|
11
|
-
const meta = {
|
|
12
|
-
id: "ses_test", project_id: "p", parent_id: null,
|
|
13
|
-
title: "Test session", directory: "/tmp",
|
|
14
|
-
time_created: 1000, source_time_updated: 1000,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
describe("store", () => {
|
|
18
|
-
test("replaceSessionChunks + search round-trip ranks by cosine", () => {
|
|
19
|
-
replaceSessionChunks(db, meta, [
|
|
20
|
-
{ seq: 0, time_created: 1000, text: "alpha chunk", embedding: new Float32Array([1, 0]) },
|
|
21
|
-
{ seq: 1, time_created: 1001, text: "beta chunk", embedding: new Float32Array([0, 1]) },
|
|
22
|
-
]);
|
|
23
|
-
const hits = search(db, new Float32Array([1, 0]));
|
|
24
|
-
expect(hits).toHaveLength(2);
|
|
25
|
-
expect(hits[0].text).toBe("alpha chunk");
|
|
26
|
-
expect(hits[0].score).toBeCloseTo(1);
|
|
27
|
-
expect(hits[1].score).toBeCloseTo(0);
|
|
28
|
-
expect(getIndexedSession(db, "ses_test")?.title).toBe("Test session");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test("search skips embeddings with mismatched dims instead of crashing", () => {
|
|
32
|
-
// 4-byte blob while the query is 2 dims (8 bytes) — must be skipped.
|
|
33
|
-
db.run("INSERT INTO chunks (session_id, seq, time_created, text, embedding) VALUES (?, ?, ?, ?, ?)",
|
|
34
|
-
["ses_test", 99, 1002, "stale wrong-dims chunk", new Float32Array([0.5])]);
|
|
35
|
-
const hits = search(db, new Float32Array([1, 0]));
|
|
36
|
-
expect(hits.map((h) => h.text)).not.toContain("stale wrong-dims chunk");
|
|
37
|
-
expect(hits).toHaveLength(2);
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("re-embedding a session replaces its chunks", () => {
|
|
41
|
-
replaceSessionChunks(db, meta, [
|
|
42
|
-
{ seq: 0, time_created: 1000, text: "only chunk now", embedding: new Float32Array([1, 0]) },
|
|
43
|
-
]);
|
|
44
|
-
const hits = search(db, new Float32Array([1, 0]));
|
|
45
|
-
expect(hits.map((h) => h.text)).toEqual(["only chunk now"]);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("textSearch does exact substring matching", () => {
|
|
49
|
-
expect(textSearch(db, "only chunk")).toHaveLength(1);
|
|
50
|
-
expect(textSearch(db, "no such phrase")).toHaveLength(0);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
test("LIKE wildcards in user input are escaped (treated literally)", () => {
|
|
54
|
-
replaceSessionChunks(db, meta, [
|
|
55
|
-
{ seq: 0, time_created: 1000, text: "progress at 50% done", embedding: new Float32Array([1, 0]) },
|
|
56
|
-
{ seq: 1, time_created: 1001, text: "snake_case name here", embedding: new Float32Array([0, 1]) },
|
|
57
|
-
{ seq: 2, time_created: 1002, text: "path \\tmp", embedding: new Float32Array([1, 0]) },
|
|
58
|
-
]);
|
|
59
|
-
// % must match literally, not as a wildcard
|
|
60
|
-
expect(textSearch(db, "50%").map((h) => h.text)).toEqual(["progress at 50% done"]);
|
|
61
|
-
// _ must match literally, not as a single-char wildcard
|
|
62
|
-
expect(textSearch(db, "snake_case").map((h) => h.text)).toEqual(["snake_case name here"]);
|
|
63
|
-
// a bare % should NOT match everything (would if unescaped)
|
|
64
|
-
expect(textSearch(db, "%")).toHaveLength(1);
|
|
65
|
-
expect(textSearch(db, "_")).toHaveLength(1);
|
|
66
|
-
// a literal backslash must match only the row containing one — escapeLike
|
|
67
|
-
// escapes the escape char itself, so this would break if that were missed
|
|
68
|
-
expect(textSearch(db, "\\").map((h) => h.text)).toEqual(["path \\tmp"]);
|
|
69
|
-
// search() text filter should also escape
|
|
70
|
-
expect(search(db, new Float32Array([1, 0]), { text: "50%" })).toHaveLength(1);
|
|
71
|
-
});
|
|
72
|
-
});
|