@mneme-ai/core 0.22.2 → 0.23.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/retrieve/ddtree.d.ts +67 -0
- package/dist/retrieve/ddtree.d.ts.map +1 -0
- package/dist/retrieve/ddtree.js +156 -0
- package/dist/retrieve/ddtree.js.map +1 -0
- package/dist/retrieve/ddtree.test.d.ts +2 -0
- package/dist/retrieve/ddtree.test.d.ts.map +1 -0
- package/dist/retrieve/ddtree.test.js +181 -0
- package/dist/retrieve/ddtree.test.js.map +1 -0
- package/dist/retrieve/index.d.ts +3 -0
- package/dist/retrieve/index.d.ts.map +1 -1
- package/dist/retrieve/index.js +3 -0
- package/dist/retrieve/index.js.map +1 -1
- package/dist/retrieve/leviathan.d.ts +68 -0
- package/dist/retrieve/leviathan.d.ts.map +1 -0
- package/dist/retrieve/leviathan.js +192 -0
- package/dist/retrieve/leviathan.js.map +1 -0
- package/dist/retrieve/leviathan.test.d.ts +2 -0
- package/dist/retrieve/leviathan.test.d.ts.map +1 -0
- package/dist/retrieve/leviathan.test.js +124 -0
- package/dist/retrieve/leviathan.test.js.map +1 -0
- package/dist/retrieve/search.d.ts +7 -0
- package/dist/retrieve/search.d.ts.map +1 -1
- package/dist/retrieve/search.js +31 -3
- package/dist/retrieve/search.js.map +1 -1
- package/dist/retrieve/stream.d.ts +93 -0
- package/dist/retrieve/stream.d.ts.map +1 -0
- package/dist/retrieve/stream.js +52 -0
- package/dist/retrieve/stream.js.map +1 -0
- package/dist/retrieve/stream.test.d.ts +2 -0
- package/dist/retrieve/stream.test.d.ts.map +1 -0
- package/dist/retrieve/stream.test.js +118 -0
- package/dist/retrieve/stream.test.js.map +1 -0
- package/dist/retrieve/synthesize.d.ts.map +1 -1
- package/dist/retrieve/synthesize.js +17 -1
- package/dist/retrieve/synthesize.js.map +1 -1
- package/dist/util/constraint-pruner.d.ts +76 -0
- package/dist/util/constraint-pruner.d.ts.map +1 -0
- package/dist/util/constraint-pruner.js +89 -0
- package/dist/util/constraint-pruner.js.map +1 -0
- package/dist/util/constraint-pruner.test.d.ts +2 -0
- package/dist/util/constraint-pruner.test.d.ts.map +1 -0
- package/dist/util/constraint-pruner.test.js +101 -0
- package/dist/util/constraint-pruner.test.js.map +1 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.d.ts.map +1 -1
- package/dist/util/index.js +1 -0
- package/dist/util/index.js.map +1 -1
- package/dist/wisdom/index.d.ts +2 -0
- package/dist/wisdom/index.d.ts.map +1 -1
- package/dist/wisdom/index.js +2 -0
- package/dist/wisdom/index.js.map +1 -1
- package/dist/wisdom/mutant-adapt.d.ts +69 -0
- package/dist/wisdom/mutant-adapt.d.ts.map +1 -0
- package/dist/wisdom/mutant-adapt.js +216 -0
- package/dist/wisdom/mutant-adapt.js.map +1 -0
- package/dist/wisdom/mutant-adapt.test.d.ts +2 -0
- package/dist/wisdom/mutant-adapt.test.d.ts.map +1 -0
- package/dist/wisdom/mutant-adapt.test.js +184 -0
- package/dist/wisdom/mutant-adapt.test.js.map +1 -0
- package/dist/wisdom/session.d.ts +60 -0
- package/dist/wisdom/session.d.ts.map +1 -0
- package/dist/wisdom/session.js +193 -0
- package/dist/wisdom/session.js.map +1 -0
- package/dist/wisdom/session.test.d.ts +2 -0
- package/dist/wisdom/session.test.d.ts.map +1 -0
- package/dist/wisdom/session.test.js +161 -0
- package/dist/wisdom/session.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversational session memory — path-aware ask context across invocations.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: when a user runs `mneme ask` twice in a row, the second
|
|
5
|
+
* question almost always builds on the first. Without session memory, every
|
|
6
|
+
* ask is a cold start; with it, Q2 search is constrained by the commits and
|
|
7
|
+
* files surfaced in Q1, and topic vocabulary accumulates.
|
|
8
|
+
*
|
|
9
|
+
* Storage is a small JSON file at `<repoRoot>/.mneme/session.json`. We keep
|
|
10
|
+
* it deliberately separate from the SQLite store: sessions are short-lived
|
|
11
|
+
* (1-hour idle expiry), human-readable, and cheap to nuke. SQLite is the
|
|
12
|
+
* permanent record; this is the scratch pad.
|
|
13
|
+
*
|
|
14
|
+
* Concurrency: writes go through a temp-file rename so a crashed/half-written
|
|
15
|
+
* write never corrupts the file readers see.
|
|
16
|
+
*/
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
|
+
/** Maximum turns retained in a single session. */
|
|
20
|
+
export const MAX_TURNS = 20;
|
|
21
|
+
/** Idle timeout in milliseconds (1 hour). */
|
|
22
|
+
export const SESSION_IDLE_MS = 60 * 60 * 1000;
|
|
23
|
+
/** Number of recent turns considered when building context. */
|
|
24
|
+
const CONTEXT_TURN_WINDOW = 5;
|
|
25
|
+
function sessionPath(repoRoot) {
|
|
26
|
+
return join(repoRoot, ".mneme", "session.json");
|
|
27
|
+
}
|
|
28
|
+
/** Read session from .mneme/session.json — returns null if expired/missing. */
|
|
29
|
+
export function readSession(repoRoot, nowMs = Date.now()) {
|
|
30
|
+
const path = sessionPath(repoRoot);
|
|
31
|
+
if (!existsSync(path))
|
|
32
|
+
return null;
|
|
33
|
+
let raw;
|
|
34
|
+
try {
|
|
35
|
+
raw = readFileSync(path, "utf8");
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (!parsed || typeof parsed !== "object")
|
|
48
|
+
return null;
|
|
49
|
+
const s = parsed;
|
|
50
|
+
if (typeof s.startedAt !== "string" || typeof s.lastActivityAt !== "string" || !Array.isArray(s.turns)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const lastMs = Date.parse(s.lastActivityAt);
|
|
54
|
+
if (Number.isNaN(lastMs) || nowMs - lastMs > SESSION_IDLE_MS) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
// Defensive normalization — discard any malformed turns rather than throw.
|
|
58
|
+
const turns = [];
|
|
59
|
+
for (const t of s.turns) {
|
|
60
|
+
if (!t || typeof t !== "object")
|
|
61
|
+
continue;
|
|
62
|
+
const turn = t;
|
|
63
|
+
if (typeof turn.at !== "string" ||
|
|
64
|
+
typeof turn.question !== "string" ||
|
|
65
|
+
!Array.isArray(turn.topHashes) ||
|
|
66
|
+
!Array.isArray(turn.topFiles) ||
|
|
67
|
+
typeof turn.confidence !== "string") {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
turns.push({
|
|
71
|
+
at: turn.at,
|
|
72
|
+
question: turn.question,
|
|
73
|
+
topHashes: turn.topHashes.filter((x) => typeof x === "string"),
|
|
74
|
+
topFiles: turn.topFiles.filter((x) => typeof x === "string"),
|
|
75
|
+
confidence: turn.confidence,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return { startedAt: s.startedAt, lastActivityAt: s.lastActivityAt, turns };
|
|
79
|
+
}
|
|
80
|
+
function atomicWrite(path, contents) {
|
|
81
|
+
const dir = dirname(path);
|
|
82
|
+
if (!existsSync(dir)) {
|
|
83
|
+
try {
|
|
84
|
+
mkdirSync(dir, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// If we cannot create the .mneme dir (e.g. read-only fs in tests), no-op.
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Per-pid + timestamp temp suffix avoids two concurrent writers clobbering
|
|
92
|
+
// each other's tmp file before the rename.
|
|
93
|
+
const tmp = `${path}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
|
|
94
|
+
try {
|
|
95
|
+
writeFileSync(tmp, contents, "utf8");
|
|
96
|
+
renameSync(tmp, path);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Cleanup any orphan temp file; swallow — session is best-effort.
|
|
100
|
+
try {
|
|
101
|
+
rmSync(tmp, { force: true });
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
/* ignore */
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Append a turn + write back. Auto-rotates when >MAX_TURNS turns. */
|
|
109
|
+
export function appendTurn(repoRoot, turn) {
|
|
110
|
+
const nowIso = new Date().toISOString();
|
|
111
|
+
const existing = readSession(repoRoot, Date.now());
|
|
112
|
+
const session = existing
|
|
113
|
+
? {
|
|
114
|
+
startedAt: existing.startedAt,
|
|
115
|
+
lastActivityAt: nowIso,
|
|
116
|
+
turns: [...existing.turns, turn],
|
|
117
|
+
}
|
|
118
|
+
: {
|
|
119
|
+
startedAt: nowIso,
|
|
120
|
+
lastActivityAt: nowIso,
|
|
121
|
+
turns: [turn],
|
|
122
|
+
};
|
|
123
|
+
if (session.turns.length > MAX_TURNS) {
|
|
124
|
+
session.turns = session.turns.slice(session.turns.length - MAX_TURNS);
|
|
125
|
+
}
|
|
126
|
+
atomicWrite(sessionPath(repoRoot), JSON.stringify(session, null, 2));
|
|
127
|
+
}
|
|
128
|
+
/** Reset session (e.g. user runs `mneme reset-session`). */
|
|
129
|
+
export function clearSession(repoRoot) {
|
|
130
|
+
const path = sessionPath(repoRoot);
|
|
131
|
+
if (!existsSync(path))
|
|
132
|
+
return;
|
|
133
|
+
try {
|
|
134
|
+
rmSync(path, { force: true });
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
/* ignore — best-effort cleanup */
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Cheap noun-phrase extraction. We are not building an NLP pipeline; we just
|
|
142
|
+
* want a stable bag of topic tokens that survives across turns. Anything
|
|
143
|
+
* fancier (POS tagging, real chunkers) would dwarf the rest of `mneme ask`.
|
|
144
|
+
*/
|
|
145
|
+
const STOP_WORDS = new Set([
|
|
146
|
+
"the", "a", "an", "and", "or", "but", "if", "of", "in", "on", "at", "to",
|
|
147
|
+
"for", "with", "by", "from", "as", "is", "are", "was", "were", "be", "been",
|
|
148
|
+
"being", "this", "that", "these", "those", "it", "its", "i", "you", "we",
|
|
149
|
+
"they", "he", "she", "do", "does", "did", "can", "could", "should", "would",
|
|
150
|
+
"will", "shall", "may", "might", "must", "have", "has", "had", "what", "why",
|
|
151
|
+
"how", "when", "where", "who", "which", "there", "here", "not", "no", "yes",
|
|
152
|
+
"so", "than", "then", "about", "into", "out", "up", "down", "over", "under",
|
|
153
|
+
"very", "just", "only", "also", "any", "some", "all", "each", "every", "my",
|
|
154
|
+
"your", "our", "their", "his", "her", "me", "us", "them", "him",
|
|
155
|
+
]);
|
|
156
|
+
function extractTopics(question) {
|
|
157
|
+
const out = [];
|
|
158
|
+
for (const raw of question.toLowerCase().split(/[^a-z0-9_]+/)) {
|
|
159
|
+
if (raw.length < 3)
|
|
160
|
+
continue;
|
|
161
|
+
if (STOP_WORDS.has(raw))
|
|
162
|
+
continue;
|
|
163
|
+
if (/^\d+$/.test(raw))
|
|
164
|
+
continue;
|
|
165
|
+
out.push(raw);
|
|
166
|
+
}
|
|
167
|
+
return out;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Build a "session context" for retrieval — accumulates hashes/files/topics
|
|
171
|
+
* from recent turns to bias search. Used by the next ask.
|
|
172
|
+
*/
|
|
173
|
+
export function buildSessionContext(session) {
|
|
174
|
+
const ctx = {
|
|
175
|
+
recentHashes: new Set(),
|
|
176
|
+
recentFiles: new Set(),
|
|
177
|
+
recentTopics: new Map(),
|
|
178
|
+
};
|
|
179
|
+
if (!session)
|
|
180
|
+
return ctx;
|
|
181
|
+
const turns = session.turns.slice(-CONTEXT_TURN_WINDOW);
|
|
182
|
+
for (const t of turns) {
|
|
183
|
+
for (const h of t.topHashes)
|
|
184
|
+
ctx.recentHashes.add(h);
|
|
185
|
+
for (const f of t.topFiles)
|
|
186
|
+
ctx.recentFiles.add(f);
|
|
187
|
+
for (const topic of extractTopics(t.question)) {
|
|
188
|
+
ctx.recentTopics.set(topic, (ctx.recentTopics.get(topic) ?? 0) + 1);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return ctx;
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/wisdom/session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.test.d.ts","sourceRoot":"","sources":["../../src/wisdom/session.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, readFileSync, rmSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { appendTurn, buildSessionContext, clearSession, readSession, SESSION_IDLE_MS, } from "./session.js";
|
|
6
|
+
let tmpDir;
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
tmpDir = mkdtempSync(join(tmpdir(), "mneme-session-test-"));
|
|
9
|
+
});
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
12
|
+
});
|
|
13
|
+
const sampleTurn = (i) => ({
|
|
14
|
+
at: new Date(2026, 4, 1, 12, i).toISOString(),
|
|
15
|
+
question: `Why does payment retry fail in handler ${i}?`,
|
|
16
|
+
topHashes: [`hash${i}a`, `hash${i}b`],
|
|
17
|
+
topFiles: [`src/payment/handler${i}.ts`],
|
|
18
|
+
confidence: "medium",
|
|
19
|
+
});
|
|
20
|
+
describe("session — round-trip", () => {
|
|
21
|
+
it("appends a turn and reads it back", () => {
|
|
22
|
+
appendTurn(tmpDir, sampleTurn(1));
|
|
23
|
+
const s = readSession(tmpDir);
|
|
24
|
+
expect(s).not.toBeNull();
|
|
25
|
+
expect(s.turns).toHaveLength(1);
|
|
26
|
+
expect(s.turns[0].question).toContain("payment");
|
|
27
|
+
expect(s.turns[0].topHashes).toEqual(["hash1a", "hash1b"]);
|
|
28
|
+
});
|
|
29
|
+
it("preserves turn order across multiple appends", () => {
|
|
30
|
+
appendTurn(tmpDir, sampleTurn(1));
|
|
31
|
+
appendTurn(tmpDir, sampleTurn(2));
|
|
32
|
+
appendTurn(tmpDir, sampleTurn(3));
|
|
33
|
+
const s = readSession(tmpDir);
|
|
34
|
+
expect(s.turns.map((t) => t.topHashes[0])).toEqual(["hash1a", "hash2a", "hash3a"]);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("session — expiration", () => {
|
|
38
|
+
it("returns null when last activity is older than 1 hour", () => {
|
|
39
|
+
appendTurn(tmpDir, sampleTurn(1));
|
|
40
|
+
// The session was just written. Advance "now" by > 1 hour and read.
|
|
41
|
+
const future = Date.now() + SESSION_IDLE_MS + 60_000;
|
|
42
|
+
expect(readSession(tmpDir, future)).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
it("returns the session when within the idle window", () => {
|
|
45
|
+
appendTurn(tmpDir, sampleTurn(1));
|
|
46
|
+
const future = Date.now() + SESSION_IDLE_MS - 60_000;
|
|
47
|
+
expect(readSession(tmpDir, future)).not.toBeNull();
|
|
48
|
+
});
|
|
49
|
+
it("returns null when file is missing", () => {
|
|
50
|
+
expect(readSession(tmpDir)).toBeNull();
|
|
51
|
+
});
|
|
52
|
+
it("returns null when file is corrupt JSON", () => {
|
|
53
|
+
const path = join(tmpDir, ".mneme", "session.json");
|
|
54
|
+
mkdirSync(join(tmpDir, ".mneme"), { recursive: true });
|
|
55
|
+
writeFileSync(path, "{ not valid json", "utf8");
|
|
56
|
+
expect(readSession(tmpDir)).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe("session — turn cap", () => {
|
|
60
|
+
it("retains only the last 20 turns when 25 are appended", () => {
|
|
61
|
+
for (let i = 1; i <= 25; i++) {
|
|
62
|
+
appendTurn(tmpDir, sampleTurn(i));
|
|
63
|
+
}
|
|
64
|
+
const s = readSession(tmpDir);
|
|
65
|
+
expect(s.turns).toHaveLength(20);
|
|
66
|
+
// Oldest 5 dropped — first remaining is turn #6.
|
|
67
|
+
expect(s.turns[0].topHashes[0]).toBe("hash6a");
|
|
68
|
+
expect(s.turns[19].topHashes[0]).toBe("hash25a");
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe("session — context building", () => {
|
|
72
|
+
it("returns an empty context for a null session", () => {
|
|
73
|
+
const ctx = buildSessionContext(null);
|
|
74
|
+
expect(ctx.recentHashes.size).toBe(0);
|
|
75
|
+
expect(ctx.recentFiles.size).toBe(0);
|
|
76
|
+
expect(ctx.recentTopics.size).toBe(0);
|
|
77
|
+
});
|
|
78
|
+
it("aggregates hashes, files, and topics from recent turns", () => {
|
|
79
|
+
appendTurn(tmpDir, {
|
|
80
|
+
at: new Date().toISOString(),
|
|
81
|
+
question: "Why does payment retry fail?",
|
|
82
|
+
topHashes: ["abc", "def"],
|
|
83
|
+
topFiles: ["src/pay.ts"],
|
|
84
|
+
confidence: "high",
|
|
85
|
+
});
|
|
86
|
+
appendTurn(tmpDir, {
|
|
87
|
+
at: new Date().toISOString(),
|
|
88
|
+
question: "How does the payment retry interact with billing?",
|
|
89
|
+
topHashes: ["def", "ghi"],
|
|
90
|
+
topFiles: ["src/bill.ts"],
|
|
91
|
+
confidence: "medium",
|
|
92
|
+
});
|
|
93
|
+
const s = readSession(tmpDir);
|
|
94
|
+
const ctx = buildSessionContext(s);
|
|
95
|
+
// Hashes union'd.
|
|
96
|
+
expect([...ctx.recentHashes].sort()).toEqual(["abc", "def", "ghi"]);
|
|
97
|
+
// Files union'd.
|
|
98
|
+
expect([...ctx.recentFiles].sort()).toEqual(["src/bill.ts", "src/pay.ts"]);
|
|
99
|
+
// "payment" appears in both questions, so its weight should be >= 2.
|
|
100
|
+
expect(ctx.recentTopics.get("payment")).toBeGreaterThanOrEqual(2);
|
|
101
|
+
expect(ctx.recentTopics.get("retry")).toBeGreaterThanOrEqual(2);
|
|
102
|
+
// Stop words must NOT make it into topics.
|
|
103
|
+
expect(ctx.recentTopics.has("the")).toBe(false);
|
|
104
|
+
expect(ctx.recentTopics.has("does")).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it("only considers the last 5 turns when building topics", () => {
|
|
107
|
+
for (let i = 1; i <= 7; i++) {
|
|
108
|
+
appendTurn(tmpDir, {
|
|
109
|
+
at: new Date(2026, 4, 1, 12, i).toISOString(),
|
|
110
|
+
question: `unique${i}word topic${i}`,
|
|
111
|
+
topHashes: [`h${i}`],
|
|
112
|
+
topFiles: [`f${i}.ts`],
|
|
113
|
+
confidence: "low",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const ctx = buildSessionContext(readSession(tmpDir));
|
|
117
|
+
// The first two turns' unique tokens should be evicted by the 5-turn window.
|
|
118
|
+
expect(ctx.recentTopics.has("unique1word")).toBe(false);
|
|
119
|
+
expect(ctx.recentTopics.has("unique2word")).toBe(false);
|
|
120
|
+
expect(ctx.recentTopics.has("unique7word")).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
describe("session — clearSession", () => {
|
|
124
|
+
it("removes the file when present", () => {
|
|
125
|
+
appendTurn(tmpDir, sampleTurn(1));
|
|
126
|
+
expect(existsSync(join(tmpDir, ".mneme", "session.json"))).toBe(true);
|
|
127
|
+
clearSession(tmpDir);
|
|
128
|
+
expect(existsSync(join(tmpDir, ".mneme", "session.json"))).toBe(false);
|
|
129
|
+
expect(readSession(tmpDir)).toBeNull();
|
|
130
|
+
});
|
|
131
|
+
it("is a no-op when no session file exists", () => {
|
|
132
|
+
expect(() => clearSession(tmpDir)).not.toThrow();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("session — concurrent writes", () => {
|
|
136
|
+
it("never leaves the JSON file in a corrupt state", () => {
|
|
137
|
+
// Fire many appends in quick succession. Atomic-rename pattern guarantees
|
|
138
|
+
// any successful read sees a fully-written JSON document, never a partial one.
|
|
139
|
+
for (let i = 0; i < 30; i++) {
|
|
140
|
+
appendTurn(tmpDir, sampleTurn(i));
|
|
141
|
+
}
|
|
142
|
+
const path = join(tmpDir, ".mneme", "session.json");
|
|
143
|
+
const raw = readFileSync(path, "utf8");
|
|
144
|
+
// Must parse cleanly.
|
|
145
|
+
const parsed = JSON.parse(raw);
|
|
146
|
+
expect(Array.isArray(parsed.turns)).toBe(true);
|
|
147
|
+
expect(parsed.turns.length).toBeGreaterThan(0);
|
|
148
|
+
expect(parsed.turns.length).toBeLessThanOrEqual(20);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
describe("session — missing repo dir", () => {
|
|
152
|
+
it("readSession returns null for a non-existent path", () => {
|
|
153
|
+
expect(readSession(join(tmpDir, "does-not-exist"))).toBeNull();
|
|
154
|
+
});
|
|
155
|
+
it("appendTurn creates .mneme dir under an existing repoRoot", () => {
|
|
156
|
+
// The repo root exists (tmpDir) but .mneme does not yet.
|
|
157
|
+
appendTurn(tmpDir, sampleTurn(1));
|
|
158
|
+
expect(existsSync(join(tmpDir, ".mneme", "session.json"))).toBe(true);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
161
|
+
//# sourceMappingURL=session.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.test.js","sourceRoot":"","sources":["../../src/wisdom/session.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAClG,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EACL,UAAU,EACV,mBAAmB,EACnB,YAAY,EACZ,WAAW,EACX,eAAe,GAGhB,MAAM,cAAc,CAAC;AAEtB,IAAI,MAAc,CAAC;AAEnB,UAAU,CAAC,GAAG,EAAE;IACd,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,GAAG,EAAE;IACb,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,MAAM,UAAU,GAAG,CAAC,CAAS,EAAW,EAAE,CAAC,CAAC;IAC1C,EAAE,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE;IAC7C,QAAQ,EAAE,0CAA0C,CAAC,GAAG;IACxD,SAAS,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC;IACrC,QAAQ,EAAE,CAAC,sBAAsB,CAAC,KAAK,CAAC;IACxC,UAAU,EAAE,QAAQ;CACrB,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QACzB,MAAM,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACjC,MAAM,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACnD,MAAM,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,GAAG,WAAW,CAAC,MAAM,CAAE,CAAC;QAC/B,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,oEAAoE;QACpE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,GAAG,MAAM,CAAC;QACrD,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,GAAG,MAAM,CAAC;QACrD,MAAM,CAAC,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QACpD,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvD,aAAa,CAAC,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,CAAC;QAChD,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7B,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,CAAC,GAAG,WAAW,CAAC,MAAM,CAAE,CAAC;QAC/B,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QACjC,iDAAiD;QACjD,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,GAAG,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,UAAU,CAAC,MAAM,EAAE;YACjB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,QAAQ,EAAE,8BAA8B;YACxC,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC;YACzB,QAAQ,EAAE,CAAC,YAAY,CAAC;YACxB,UAAU,EAAE,MAAM;SACnB,CAAC,CAAC;QACH,UAAU,CAAC,MAAM,EAAE;YACjB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,QAAQ,EAAE,mDAAmD;YAC7D,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC;YACzB,QAAQ,EAAE,CAAC,aAAa,CAAC;YACzB,UAAU,EAAE,QAAQ;SACrB,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,GAAG,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;QACnC,kBAAkB;QAClB,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;QACpE,iBAAiB;QACjB,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC;QAC3E,qEAAqE;QACrE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAClE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAChE,2CAA2C;QAC3C,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,UAAU,CAAC,MAAM,EAAE;gBACjB,EAAE,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE;gBAC7C,QAAQ,EAAE,SAAS,CAAC,aAAa,CAAC,EAAE;gBACpC,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpB,QAAQ,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;gBACtB,UAAU,EAAE,KAAK;aAClB,CAAC,CAAC;QACL,CAAC;QACD,MAAM,GAAG,GAAG,mBAAmB,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;QACrD,6EAA6E;QAC7E,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtE,YAAY,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvE,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,6BAA6B,EAAE,GAAG,EAAE;IAC3C,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,0EAA0E;QAC1E,+EAA+E;QAC/E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACvC,sBAAsB;QACtB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAY,CAAC;QAC1C,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,yDAAyD;QACzD,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|