@mneme-ai/mcp 1.17.6 → 1.19.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/README.md +64 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +135 -8
- package/dist/index.js.map +1 -1
- package/dist/mcp_primitives/completion.d.ts +19 -0
- package/dist/mcp_primitives/completion.d.ts.map +1 -0
- package/dist/mcp_primitives/completion.js +55 -0
- package/dist/mcp_primitives/completion.js.map +1 -0
- package/dist/mcp_primitives/prompts.d.ts +36 -0
- package/dist/mcp_primitives/prompts.d.ts.map +1 -0
- package/dist/mcp_primitives/prompts.js +140 -0
- package/dist/mcp_primitives/prompts.js.map +1 -0
- package/dist/mcp_primitives/resources.d.ts +30 -0
- package/dist/mcp_primitives/resources.d.ts.map +1 -0
- package/dist/mcp_primitives/resources.js +127 -0
- package/dist/mcp_primitives/resources.js.map +1 -0
- package/dist/tools/_aletheia.d.ts +82 -0
- package/dist/tools/_aletheia.d.ts.map +1 -0
- package/dist/tools/_aletheia.js +800 -0
- package/dist/tools/_aletheia.js.map +1 -0
- package/dist/tools/_confess.d.ts +67 -0
- package/dist/tools/_confess.d.ts.map +1 -0
- package/dist/tools/_confess.js +260 -0
- package/dist/tools/_confess.js.map +1 -0
- package/dist/tools/_confess.test.d.ts +6 -0
- package/dist/tools/_confess.test.d.ts.map +1 -0
- package/dist/tools/_confess.test.js +96 -0
- package/dist/tools/_confess.test.js.map +1 -0
- package/dist/tools/_contract.test.d.ts +19 -0
- package/dist/tools/_contract.test.d.ts.map +1 -0
- package/dist/tools/_contract.test.js +117 -0
- package/dist/tools/_contract.test.js.map +1 -0
- package/dist/tools/_court.d.ts +57 -0
- package/dist/tools/_court.d.ts.map +1 -0
- package/dist/tools/_court.js +261 -0
- package/dist/tools/_court.js.map +1 -0
- package/dist/tools/_court.test.d.ts +8 -0
- package/dist/tools/_court.test.d.ts.map +1 -0
- package/dist/tools/_court.test.js +111 -0
- package/dist/tools/_court.test.js.map +1 -0
- package/dist/tools/_genome_marketplace.d.ts +83 -0
- package/dist/tools/_genome_marketplace.d.ts.map +1 -0
- package/dist/tools/_genome_marketplace.js +410 -0
- package/dist/tools/_genome_marketplace.js.map +1 -0
- package/dist/tools/_genome_marketplace.test.d.ts +5 -0
- package/dist/tools/_genome_marketplace.test.d.ts.map +1 -0
- package/dist/tools/_genome_marketplace.test.js +157 -0
- package/dist/tools/_genome_marketplace.test.js.map +1 -0
- package/dist/tools/_lineage.d.ts +35 -0
- package/dist/tools/_lineage.d.ts.map +1 -0
- package/dist/tools/_lineage.js +782 -0
- package/dist/tools/_lineage.js.map +1 -0
- package/dist/tools/_mesh.d.ts +51 -0
- package/dist/tools/_mesh.d.ts.map +1 -0
- package/dist/tools/_mesh.js +182 -0
- package/dist/tools/_mesh.js.map +1 -0
- package/dist/tools/_registry.d.ts.map +1 -1
- package/dist/tools/_registry.js +19 -0
- package/dist/tools/_registry.js.map +1 -1
- package/dist/tools/_replay.d.ts +52 -0
- package/dist/tools/_replay.d.ts.map +1 -0
- package/dist/tools/_replay.js +253 -0
- package/dist/tools/_replay.js.map +1 -0
- package/dist/tools/_replay.test.d.ts +5 -0
- package/dist/tools/_replay.test.d.ts.map +1 -0
- package/dist/tools/_replay.test.js +90 -0
- package/dist/tools/_replay.test.js.map +1 -0
- package/dist/tools/_timetravel.d.ts +46 -0
- package/dist/tools/_timetravel.d.ts.map +1 -0
- package/dist/tools/_timetravel.js +243 -0
- package/dist/tools/_timetravel.js.map +1 -0
- package/dist/tools/_timetravel.test.d.ts +7 -0
- package/dist/tools/_timetravel.test.d.ts.map +1 -0
- package/dist/tools/_timetravel.test.js +31 -0
- package/dist/tools/_timetravel.test.js.map +1 -0
- package/dist/tools/_tool_meta.d.ts +30 -0
- package/dist/tools/_tool_meta.d.ts.map +1 -0
- package/dist/tools/_tool_meta.js +530 -0
- package/dist/tools/_tool_meta.js.map +1 -0
- package/dist/tools/_types.d.ts +46 -5
- package/dist/tools/_types.d.ts.map +1 -1
- package/dist/tools/_types.js.map +1 -1
- package/dist/tools/_verify_claims_tool.d.ts.map +1 -1
- package/dist/tools/_verify_claims_tool.js +23 -0
- package/dist/tools/_verify_claims_tool.js.map +1 -1
- package/dist/tools/audit.d.ts.map +1 -1
- package/dist/tools/audit.js +37 -0
- package/dist/tools/audit.js.map +1 -1
- package/dist/tools/memory.d.ts.map +1 -1
- package/dist/tools/memory.js +23 -0
- package/dist/tools/memory.js.map +1 -1
- package/dist/tools/quant.d.ts +10 -2
- package/dist/tools/quant.d.ts.map +1 -1
- package/dist/tools/quant.js +311 -18
- package/dist/tools/quant.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay Traces (v1.18.0 — black sheep #7)
|
|
3
|
+
*
|
|
4
|
+
* Every MCP tool call is recorded as one HMAC-chained line in
|
|
5
|
+
* `.mneme/replay.jsonl`. Each entry's `prevHash` references the previous
|
|
6
|
+
* line, so any tampering breaks the chain at exactly one point — Merkle
|
|
7
|
+
* trail without a tree.
|
|
8
|
+
*
|
|
9
|
+
* • mneme.replay.dump — return the whole session trace as JSON
|
|
10
|
+
* • mneme.replay.fingerprint — return the Merkle root + chain integrity
|
|
11
|
+
*
|
|
12
|
+
* Use cases: SOC2 / EU AI Act audit evidence, deterministic-session proofs,
|
|
13
|
+
* post-mortem reconstruction. The fingerprint is what you publish; the
|
|
14
|
+
* dump is what you'd hand an auditor.
|
|
15
|
+
*
|
|
16
|
+
* The recorder lives in this file but is invoked from index.ts on every
|
|
17
|
+
* call. Recording is best-effort — any I/O error is swallowed so a
|
|
18
|
+
* corrupted disk can never block tool dispatch.
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } from "node:fs";
|
|
21
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
const REPLAY_DIR = ".mneme";
|
|
24
|
+
const REPLAY_FILE = "replay.jsonl";
|
|
25
|
+
const SECRET_FILE = "replay-secret.bin";
|
|
26
|
+
function ensureSecret(repoRoot) {
|
|
27
|
+
const dir = join(repoRoot, REPLAY_DIR);
|
|
28
|
+
if (!existsSync(dir))
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
const path = join(dir, SECRET_FILE);
|
|
31
|
+
if (existsSync(path))
|
|
32
|
+
return readFileSync(path);
|
|
33
|
+
const buf = randomBytes(32);
|
|
34
|
+
writeFileSync(path, buf, { mode: 0o600 });
|
|
35
|
+
return buf;
|
|
36
|
+
}
|
|
37
|
+
function shortHash(s) {
|
|
38
|
+
return createHash("sha256").update(s).digest("hex").slice(0, 16);
|
|
39
|
+
}
|
|
40
|
+
function hmacSha(secret, payload) {
|
|
41
|
+
// HMAC-SHA-256, returned as 32-char hex prefix.
|
|
42
|
+
const h = createHash("sha256");
|
|
43
|
+
h.update(secret);
|
|
44
|
+
h.update("|");
|
|
45
|
+
h.update(payload);
|
|
46
|
+
return h.digest("hex").slice(0, 32);
|
|
47
|
+
}
|
|
48
|
+
function readLastHash(repoRoot) {
|
|
49
|
+
const path = join(repoRoot, REPLAY_DIR, REPLAY_FILE);
|
|
50
|
+
if (!existsSync(path))
|
|
51
|
+
return "GENESIS";
|
|
52
|
+
const txt = readFileSync(path, "utf8").trimEnd();
|
|
53
|
+
if (!txt)
|
|
54
|
+
return "GENESIS";
|
|
55
|
+
const lines = txt.split("\n");
|
|
56
|
+
const last = lines[lines.length - 1];
|
|
57
|
+
if (!last)
|
|
58
|
+
return "GENESIS";
|
|
59
|
+
try {
|
|
60
|
+
const e = JSON.parse(last);
|
|
61
|
+
return e.hash;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return "GENESIS";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Append a single tool-call record. Best-effort — never throws. */
|
|
68
|
+
export function recordReplay(repoRoot, tool, args, response) {
|
|
69
|
+
try {
|
|
70
|
+
const secret = ensureSecret(repoRoot);
|
|
71
|
+
const argHash = shortHash(JSON.stringify(args ?? {}));
|
|
72
|
+
const responseHash = shortHash(JSON.stringify(response ?? {}));
|
|
73
|
+
const verdict = extractVerdict(response);
|
|
74
|
+
const prevHash = readLastHash(repoRoot);
|
|
75
|
+
const ts = new Date().toISOString();
|
|
76
|
+
const payload = `${ts}|${tool}|${argHash}|${responseHash}|${verdict ?? ""}|${prevHash}`;
|
|
77
|
+
const hash = hmacSha(secret, payload);
|
|
78
|
+
const entry = { ts, tool, argHash, responseHash, prevHash, hash };
|
|
79
|
+
if (verdict)
|
|
80
|
+
entry.verdict = verdict;
|
|
81
|
+
appendFileSync(join(repoRoot, REPLAY_DIR, REPLAY_FILE), JSON.stringify(entry) + "\n", "utf8");
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// best-effort — never block dispatch
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function extractVerdict(response) {
|
|
88
|
+
if (!response || typeof response !== "object")
|
|
89
|
+
return undefined;
|
|
90
|
+
const r = response;
|
|
91
|
+
if (r.data && typeof r.data === "object") {
|
|
92
|
+
const d = r.data;
|
|
93
|
+
if (typeof d.verdict === "string")
|
|
94
|
+
return d.verdict;
|
|
95
|
+
}
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
export function verifyChain(repoRoot) {
|
|
99
|
+
const path = join(repoRoot, REPLAY_DIR, REPLAY_FILE);
|
|
100
|
+
if (!existsSync(path))
|
|
101
|
+
return { total: 0, intact: true, root: "EMPTY" };
|
|
102
|
+
const txt = readFileSync(path, "utf8").trimEnd();
|
|
103
|
+
if (!txt)
|
|
104
|
+
return { total: 0, intact: true, root: "EMPTY" };
|
|
105
|
+
const secret = ensureSecret(repoRoot);
|
|
106
|
+
const lines = txt.split("\n").filter(Boolean);
|
|
107
|
+
let prev = "GENESIS";
|
|
108
|
+
for (let i = 0; i < lines.length; i++) {
|
|
109
|
+
let entry;
|
|
110
|
+
try {
|
|
111
|
+
entry = JSON.parse(lines[i]);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return { total: lines.length, intact: false, brokenAt: i, root: "INVALID" };
|
|
115
|
+
}
|
|
116
|
+
if (entry.prevHash !== prev) {
|
|
117
|
+
return { total: lines.length, intact: false, brokenAt: i, root: "BROKEN" };
|
|
118
|
+
}
|
|
119
|
+
const expected = hmacSha(secret, `${entry.ts}|${entry.tool}|${entry.argHash}|${entry.responseHash}|${entry.verdict ?? ""}|${entry.prevHash}`);
|
|
120
|
+
if (expected !== entry.hash) {
|
|
121
|
+
return { total: lines.length, intact: false, brokenAt: i, root: "TAMPERED" };
|
|
122
|
+
}
|
|
123
|
+
prev = entry.hash;
|
|
124
|
+
}
|
|
125
|
+
// Merkle root = HMAC of the final hash (so root depends on all entries).
|
|
126
|
+
const root = hmacSha(secret, prev);
|
|
127
|
+
return { total: lines.length, intact: true, root };
|
|
128
|
+
}
|
|
129
|
+
export function readReplay(repoRoot, limit = 1000) {
|
|
130
|
+
const path = join(repoRoot, REPLAY_DIR, REPLAY_FILE);
|
|
131
|
+
if (!existsSync(path))
|
|
132
|
+
return [];
|
|
133
|
+
const txt = readFileSync(path, "utf8").trimEnd();
|
|
134
|
+
if (!txt)
|
|
135
|
+
return [];
|
|
136
|
+
const lines = txt.split("\n").filter(Boolean);
|
|
137
|
+
const slice = limit > 0 && lines.length > limit ? lines.slice(-limit) : lines;
|
|
138
|
+
return slice
|
|
139
|
+
.map((l) => {
|
|
140
|
+
try {
|
|
141
|
+
return JSON.parse(l);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
.filter((e) => e !== null);
|
|
148
|
+
}
|
|
149
|
+
export const replayDumpTool = {
|
|
150
|
+
name: "mneme.replay.dump",
|
|
151
|
+
category: "meta",
|
|
152
|
+
description: "Return the HMAC-chained replay log of every MCP tool call this session " +
|
|
153
|
+
"(and earlier sessions in the same repo). Each entry: timestamp, tool name, " +
|
|
154
|
+
"argument-hash, response-hash, verdict (if present), and the chain link. " +
|
|
155
|
+
"Use WHEN you need a complete audit trail of what the AI did — for SOC2 / " +
|
|
156
|
+
"EU AI Act compliance, postmortem reconstruction, or deterministic-session " +
|
|
157
|
+
"proofs. Pair with mneme.replay.fingerprint for a tamper-evident root hash.",
|
|
158
|
+
whenToUse: "You need the complete audit trail of every tool call in this repo (or session) — for compliance / postmortem / reproducibility.",
|
|
159
|
+
triggers: ["dump replay log", "audit trail", "session trace", "SOC2 evidence"],
|
|
160
|
+
inputSchema: {
|
|
161
|
+
type: "object",
|
|
162
|
+
properties: {
|
|
163
|
+
limit: {
|
|
164
|
+
type: "number",
|
|
165
|
+
description: "Max entries to return (most-recent N). Default 1000. 0 = no cap.",
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
outputSchema: {
|
|
170
|
+
type: "object",
|
|
171
|
+
properties: {
|
|
172
|
+
total: { type: "number" },
|
|
173
|
+
returned: { type: "number" },
|
|
174
|
+
entries: { type: "array", items: { type: "object" } },
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
examples: [
|
|
178
|
+
{
|
|
179
|
+
userQuery: "Give me the audit trail of every Mneme call from this AI session",
|
|
180
|
+
args: { limit: 1000 },
|
|
181
|
+
expectedOutput: "Returns up to 1000 most-recent ReplayEntry objects. Each has ts/tool/argHash/responseHash/prevHash/hash and optional verdict.",
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
pitfalls: [
|
|
185
|
+
"The log is INDEFINITE — it grows across sessions. Rotate manually if you don't want cross-session traces.",
|
|
186
|
+
"Hashes are short (16-char prefix) for readability — a determined attacker COULD find collisions; this is audit-grade, not crypto-grade integrity.",
|
|
187
|
+
"Recording is best-effort: a disk-full error swallows the entry silently rather than blocking dispatch.",
|
|
188
|
+
],
|
|
189
|
+
composeWith: ["mneme.replay.fingerprint", "mneme.audit.ledger", "mneme.audit.report"],
|
|
190
|
+
handler: async (rt, args) => {
|
|
191
|
+
const limit = typeof args["limit"] === "number" ? args["limit"] : 1000;
|
|
192
|
+
const entries = readReplay(rt.meta.rootPath, limit);
|
|
193
|
+
return {
|
|
194
|
+
data: {
|
|
195
|
+
total: entries.length,
|
|
196
|
+
returned: entries.length,
|
|
197
|
+
entries,
|
|
198
|
+
},
|
|
199
|
+
wisdom: entries.length === 0
|
|
200
|
+
? "Replay log is empty — either no tool calls have happened in this repo yet, or the log was never created."
|
|
201
|
+
: `Returned ${entries.length} chain-linked entries. Verify integrity via mneme.replay.fingerprint.`,
|
|
202
|
+
followUp: ["mneme.replay.fingerprint"],
|
|
203
|
+
confidence: { level: "high" },
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
export const replayFingerprintTool = {
|
|
208
|
+
name: "mneme.replay.fingerprint",
|
|
209
|
+
category: "meta",
|
|
210
|
+
description: "Return the tamper-evident root hash of the replay log + chain integrity " +
|
|
211
|
+
"status. Each entry in the log links to the previous via HMAC, so any " +
|
|
212
|
+
"tampering breaks the chain at exactly one point. The root is a stable " +
|
|
213
|
+
"identifier you can publish to prove this AI session was deterministic + " +
|
|
214
|
+
"untouched. Use WHEN you want to attest to session integrity (e.g., embed " +
|
|
215
|
+
"the root in a release note, or compare two replay logs to prove they ran " +
|
|
216
|
+
"the same sequence).",
|
|
217
|
+
whenToUse: "You need a single tamper-evident hash that summarizes the entire MCP-call history of this repo — publishable proof of session integrity.",
|
|
218
|
+
triggers: ["replay fingerprint", "session merkle root", "integrity check"],
|
|
219
|
+
inputSchema: { type: "object", properties: {} },
|
|
220
|
+
outputSchema: {
|
|
221
|
+
type: "object",
|
|
222
|
+
properties: {
|
|
223
|
+
total: { type: "number" },
|
|
224
|
+
intact: { type: "boolean" },
|
|
225
|
+
brokenAt: { type: "number", description: "Line index where chain broke (only set if intact=false)." },
|
|
226
|
+
root: { type: "string", description: "Merkle root — stable identifier for this trace." },
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
examples: [
|
|
230
|
+
{
|
|
231
|
+
userQuery: "Is the audit trail intact?",
|
|
232
|
+
expectedOutput: "Returns { total, intact: true, root: '<32-hex>' } when chain verifies. If tampered: intact=false, brokenAt set, root = 'TAMPERED' / 'BROKEN' / 'INVALID'.",
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
pitfalls: [
|
|
236
|
+
"Verifies the LOCAL log only — there's no global anchor (yet). For external attestation, post the root to git via a tagged commit.",
|
|
237
|
+
"If .mneme/replay-secret.bin is regenerated, ALL prior chain links become unverifiable. Treat the secret like a key.",
|
|
238
|
+
],
|
|
239
|
+
composeWith: ["mneme.replay.dump", "mneme.audit.ledger"],
|
|
240
|
+
handler: async (rt) => {
|
|
241
|
+
const status = verifyChain(rt.meta.rootPath);
|
|
242
|
+
return {
|
|
243
|
+
data: status,
|
|
244
|
+
wisdom: status.intact
|
|
245
|
+
? `Chain intact — ${status.total} entr${status.total === 1 ? "y" : "ies"}. Root: ${status.root}.`
|
|
246
|
+
: `CHAIN BROKEN at entry ${status.brokenAt} — replay log has been tampered with or corrupted. Root: ${status.root}.`,
|
|
247
|
+
confidence: { level: "high" },
|
|
248
|
+
followUp: ["mneme.replay.dump"],
|
|
249
|
+
};
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
export const replayTools = [replayDumpTool, replayFingerprintTool];
|
|
253
|
+
//# sourceMappingURL=_replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_replay.js","sourceRoot":"","sources":["../../src/tools/_replay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,MAAM,UAAU,GAAG,QAAQ,CAAC;AAC5B,MAAM,WAAW,GAAG,cAAc,CAAC;AACnC,MAAM,WAAW,GAAG,mBAAmB,CAAC;AAmBxC,SAAS,YAAY,CAAC,QAAgB;IACpC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IACvC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACpC,IAAI,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC;IAC5B,aAAa,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1C,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,SAAS,CAAC,CAAS;IAC1B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,OAAO,CAAC,MAAc,EAAE,OAAe;IAC9C,gDAAgD;IAChD,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/B,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACjB,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACd,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAClB,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAgB,CAAC;QAC1C,OAAO,CAAC,CAAC,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,YAAY,CAC1B,QAAgB,EAChB,IAAY,EACZ,IAAa,EACb,QAAiB;IAEjB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;QACtD,MAAM,YAAY,GAAG,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,GAAG,EAAE,IAAI,IAAI,IAAI,OAAO,IAAI,YAAY,IAAI,OAAO,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;QACxF,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACtC,MAAM,KAAK,GAAgB,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC/E,IAAI,OAAO;YAAE,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;QACrC,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;IAChG,CAAC;IAAC,MAAM,CAAC;QACP,qCAAqC;IACvC,CAAC;AACH,CAAC;AAED,SAAS,cAAc,CAAC,QAAiB;IACvC,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChE,MAAM,CAAC,GAAG,QAA8B,CAAC;IACzC,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,CAAC,IAA4B,CAAC;QACzC,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ;YAAE,OAAO,CAAC,CAAC,OAAO,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAUD,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACxE,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAC3D,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,IAAI,IAAI,GAAG,SAAS,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,KAAkB,CAAC;QACvB,IAAI,CAAC;YACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAE,CAAgB,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC9E,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC5B,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;QAC7E,CAAC;QACD,MAAM,QAAQ,GAAG,OAAO,CACtB,MAAM,EACN,GAAG,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,OAAO,IAAI,EAAE,IAAI,KAAK,CAAC,QAAQ,EAAE,CAC5G,CAAC;QACF,IAAI,QAAQ,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;YAC5B,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC/E,CAAC;QACD,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACpB,CAAC;IACD,yEAAyE;IACzE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACnC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,KAAK,GAAG,IAAI;IACvD,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9E,OAAO,KAAK;SACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAgB,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,CAAC;SACD,MAAM,CAAC,CAAC,CAAC,EAAoB,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAc;IACvC,IAAI,EAAE,mBAAmB;IACzB,QAAQ,EAAE,MAAM;IAChB,WAAW,EACT,yEAAyE;QACzE,6EAA6E;QAC7E,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,4EAA4E;IAC9E,SAAS,EACP,iIAAiI;IACnI,QAAQ,EAAE,CAAC,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,CAAC;IAC9E,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,KAAK,EAAE;gBACL,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,kEAAkE;aAChF;SACF;KACF;IACD,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YAC5B,OAAO,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;SACtD;KACF;IACD,QAAQ,EAAE;QACR;YACE,SAAS,EAAE,kEAAkE;YAC7E,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;YACrB,cAAc,EAAE,+HAA+H;SAChJ;KACF;IACD,QAAQ,EAAE;QACR,2GAA2G;QAC3G,mJAAmJ;QACnJ,wGAAwG;KACzG;IACD,WAAW,EAAE,CAAC,0BAA0B,EAAE,oBAAoB,EAAE,oBAAoB,CAAC;IACrF,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE;QAC1B,MAAM,KAAK,GAAG,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAI,CAAC,OAAO,CAAY,CAAC,CAAC,CAAC,IAAI,CAAC;QACnF,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACpD,OAAO;YACL,IAAI,EAAE;gBACJ,KAAK,EAAE,OAAO,CAAC,MAAM;gBACrB,QAAQ,EAAE,OAAO,CAAC,MAAM;gBACxB,OAAO;aACR;YACD,MAAM,EACJ,OAAO,CAAC,MAAM,KAAK,CAAC;gBAClB,CAAC,CAAC,0GAA0G;gBAC5G,CAAC,CAAC,YAAY,OAAO,CAAC,MAAM,uEAAuE;YACvG,QAAQ,EAAE,CAAC,0BAA0B,CAAC;YACtC,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;SAC9B,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAc;IAC9C,IAAI,EAAE,0BAA0B;IAChC,QAAQ,EAAE,MAAM;IAChB,WAAW,EACT,0EAA0E;QAC1E,uEAAuE;QACvE,wEAAwE;QACxE,0EAA0E;QAC1E,2EAA2E;QAC3E,2EAA2E;QAC3E,qBAAqB;IACvB,SAAS,EACP,0IAA0I;IAC5I,QAAQ,EAAE,CAAC,oBAAoB,EAAE,qBAAqB,EAAE,iBAAiB,CAAC;IAC1E,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE;IAC/C,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;YACzB,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;YAC3B,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0DAA0D,EAAE;YACrG,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iDAAiD,EAAE;SACzF;KACF;IACD,QAAQ,EAAE;QACR;YACE,SAAS,EAAE,4BAA4B;YACvC,cAAc,EACZ,2JAA2J;SAC9J;KACF;IACD,QAAQ,EAAE;QACR,mIAAmI;QACnI,qHAAqH;KACtH;IACD,WAAW,EAAE,CAAC,mBAAmB,EAAE,oBAAoB,CAAC;IACxD,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE;QACpB,MAAM,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC7C,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,MAAM,EAAE,MAAM,CAAC,MAAM;gBACnB,CAAC,CAAC,kBAAkB,MAAM,CAAC,KAAK,QAAQ,MAAM,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,WAAW,MAAM,CAAC,IAAI,GAAG;gBACjG,CAAC,CAAC,yBAAyB,MAAM,CAAC,QAAQ,4DAA4D,MAAM,CAAC,IAAI,GAAG;YACtH,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;YAC7B,QAAQ,EAAE,CAAC,mBAAmB,CAAC;SAChC,CAAC;IACJ,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,WAAW,GAAgB,CAAC,cAAc,EAAE,qBAAqB,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_replay.test.d.ts","sourceRoot":"","sources":["../../src/tools/_replay.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replay Traces — unit tests for the HMAC-chained log + verifier.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, expect, it, beforeEach, afterEach } from "vitest";
|
|
5
|
+
import { mkdtempSync, rmSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { recordReplay, verifyChain, readReplay } from "./_replay.js";
|
|
9
|
+
describe("recordReplay + verifyChain", () => {
|
|
10
|
+
let repo;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
repo = mkdtempSync(join(tmpdir(), "mneme-replay-"));
|
|
13
|
+
});
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
try {
|
|
16
|
+
rmSync(repo, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
catch { /* ignore */ }
|
|
19
|
+
});
|
|
20
|
+
it("verifyChain on an empty/missing log returns intact=true with EMPTY root", () => {
|
|
21
|
+
const s = verifyChain(repo);
|
|
22
|
+
expect(s.intact).toBe(true);
|
|
23
|
+
expect(s.total).toBe(0);
|
|
24
|
+
expect(s.root).toBe("EMPTY");
|
|
25
|
+
});
|
|
26
|
+
it("records entries with chain-linked hashes", () => {
|
|
27
|
+
recordReplay(repo, "mneme.memory.ask", { question: "why?" }, { data: { ok: true } });
|
|
28
|
+
recordReplay(repo, "mneme.audit.certify", { strict: true }, { data: { verdict: "PASS" } });
|
|
29
|
+
const entries = readReplay(repo);
|
|
30
|
+
expect(entries).toHaveLength(2);
|
|
31
|
+
expect(entries[0].prevHash).toBe("GENESIS");
|
|
32
|
+
expect(entries[1].prevHash).toBe(entries[0].hash);
|
|
33
|
+
});
|
|
34
|
+
it("verifyChain returns intact=true after legitimate appends", () => {
|
|
35
|
+
recordReplay(repo, "tool.a", {}, { data: {} });
|
|
36
|
+
recordReplay(repo, "tool.b", {}, { data: {} });
|
|
37
|
+
recordReplay(repo, "tool.c", {}, { data: {} });
|
|
38
|
+
const s = verifyChain(repo);
|
|
39
|
+
expect(s.intact).toBe(true);
|
|
40
|
+
expect(s.total).toBe(3);
|
|
41
|
+
expect(s.root).not.toBe("EMPTY");
|
|
42
|
+
expect(s.root).toMatch(/^[0-9a-f]+$/);
|
|
43
|
+
});
|
|
44
|
+
it("verifyChain detects tampering in the middle of the log", () => {
|
|
45
|
+
recordReplay(repo, "tool.a", {}, { data: {} });
|
|
46
|
+
recordReplay(repo, "tool.b", {}, { data: {} });
|
|
47
|
+
recordReplay(repo, "tool.c", {}, { data: {} });
|
|
48
|
+
// Tamper: rewrite the second line with a different tool name.
|
|
49
|
+
const path = join(repo, ".mneme", "replay.jsonl");
|
|
50
|
+
const lines = readFileSync(path, "utf8").trimEnd().split("\n");
|
|
51
|
+
const tampered = lines.map((l, i) => {
|
|
52
|
+
if (i === 1) {
|
|
53
|
+
const e = JSON.parse(l);
|
|
54
|
+
e.tool = "tool.evil";
|
|
55
|
+
return JSON.stringify(e);
|
|
56
|
+
}
|
|
57
|
+
return l;
|
|
58
|
+
});
|
|
59
|
+
writeFileSync(path, tampered.join("\n") + "\n", "utf8");
|
|
60
|
+
const s = verifyChain(repo);
|
|
61
|
+
expect(s.intact).toBe(false);
|
|
62
|
+
expect(s.brokenAt).toBe(1);
|
|
63
|
+
expect(["TAMPERED", "BROKEN", "INVALID"]).toContain(s.root);
|
|
64
|
+
});
|
|
65
|
+
it("verdict is captured when present in response.data.verdict", () => {
|
|
66
|
+
recordReplay(repo, "mneme.audit.certify", {}, { data: { verdict: "WARN" } });
|
|
67
|
+
const entries = readReplay(repo);
|
|
68
|
+
expect(entries[0].verdict).toBe("WARN");
|
|
69
|
+
});
|
|
70
|
+
it("recordReplay never throws on bad input", () => {
|
|
71
|
+
// Pass a circular reference — JSON.stringify throws, recorder should swallow.
|
|
72
|
+
const circ = {};
|
|
73
|
+
circ["self"] = circ;
|
|
74
|
+
expect(() => recordReplay(repo, "tool.bad", circ, { data: {} })).not.toThrow();
|
|
75
|
+
});
|
|
76
|
+
it("readReplay respects the limit parameter (most-recent N)", () => {
|
|
77
|
+
for (let i = 0; i < 10; i++)
|
|
78
|
+
recordReplay(repo, `tool.${i}`, {}, { data: {} });
|
|
79
|
+
const last3 = readReplay(repo, 3);
|
|
80
|
+
expect(last3).toHaveLength(3);
|
|
81
|
+
expect(last3[0].tool).toBe("tool.7");
|
|
82
|
+
expect(last3[2].tool).toBe("tool.9");
|
|
83
|
+
});
|
|
84
|
+
it("creates the .mneme dir + secret on first record", () => {
|
|
85
|
+
recordReplay(repo, "tool.a", {}, {});
|
|
86
|
+
expect(existsSync(join(repo, ".mneme", "replay-secret.bin"))).toBe(true);
|
|
87
|
+
expect(existsSync(join(repo, ".mneme", "replay.jsonl"))).toBe(true);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
//# sourceMappingURL=_replay.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_replay.test.js","sourceRoot":"","sources":["../../src/tools/_replay.test.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACvF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAErE,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,IAAI,IAAY,CAAC;IACjB,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IACH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC;YAAC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,YAAY,CAAC,IAAI,EAAE,kBAAkB,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QACrF,YAAY,CAAC,IAAI,EAAE,qBAAqB,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3F,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7C,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/C,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/C,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/C,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxB,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/C,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/C,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/C,8DAA8D;QAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAClC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBACZ,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;gBACxB,CAAC,CAAC,IAAI,GAAG,WAAW,CAAC;gBACrB,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;YAC3B,CAAC;YACD,OAAO,CAAC,CAAC;QACX,CAAC,CAAC,CAAC;QACH,aAAa,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC;QACxD,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC3B,MAAM,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,YAAY,CAAC,IAAI,EAAE,qBAAqB,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAC7E,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QACjC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,8EAA8E;QAC9E,MAAM,IAAI,GAA4B,EAAE,CAAC;QACzC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;QACpB,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;YAAE,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAC/E,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAC9B,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QACrC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Time-travel MCP (v1.18.0 — black sheep #2)
|
|
3
|
+
*
|
|
4
|
+
* Snap the AI agent's view of the repo to a specific git ref. Every
|
|
5
|
+
* subsequent tool call (within the same MCP server process) operates
|
|
6
|
+
* AS IF today were that ref. State is per-server-process, so two AI
|
|
7
|
+
* sessions connecting to the same repo via separate MCP processes
|
|
8
|
+
* don't collide.
|
|
9
|
+
*
|
|
10
|
+
* • mneme.timetravel.activate(ref) — freeze the view at this ref
|
|
11
|
+
* • mneme.timetravel.status — query current state
|
|
12
|
+
* • mneme.timetravel.deactivate — return to live HEAD
|
|
13
|
+
*
|
|
14
|
+
* v1.18.0 ships the SCAFFOLDING — a per-process state holder + the
|
|
15
|
+
* three tools above. Existing tools that want to honor time-travel
|
|
16
|
+
* read `getTimeTravelState()` from this module and clamp their git
|
|
17
|
+
* commands to `--until <commitDate>` or `<ref>` accordingly. The
|
|
18
|
+
* gradual-rollout pattern: tools opt in over time without a big-bang
|
|
19
|
+
* refactor.
|
|
20
|
+
*
|
|
21
|
+
* Use cases:
|
|
22
|
+
* • Counterfactual analysis: "If I were on duty Sept 2024, what
|
|
23
|
+
* would I have seen?" — recreate incident-response state.
|
|
24
|
+
* • Hindsight-bias audit: "Did the AI's recommendation REALLY hold
|
|
25
|
+
* up at the time?" — replay decisions against frozen context.
|
|
26
|
+
* • Onboarding rehearsal: walk a new engineer through the repo at
|
|
27
|
+
* the moment they would have joined.
|
|
28
|
+
*/
|
|
29
|
+
import type { MnemeTool } from "./_types.js";
|
|
30
|
+
interface TimeTravelState {
|
|
31
|
+
active: boolean;
|
|
32
|
+
ref: string | null;
|
|
33
|
+
resolvedHash: string | null;
|
|
34
|
+
resolvedDate: string | null;
|
|
35
|
+
activatedAt: string | null;
|
|
36
|
+
}
|
|
37
|
+
/** Read current time-travel state — exported for tools that opt in. */
|
|
38
|
+
export declare function getTimeTravelState(): Readonly<TimeTravelState>;
|
|
39
|
+
/** Reset to live HEAD. Exported for tests. */
|
|
40
|
+
export declare function resetTimeTravel(): void;
|
|
41
|
+
export declare const timeTravelActivateTool: MnemeTool;
|
|
42
|
+
export declare const timeTravelStatusTool: MnemeTool;
|
|
43
|
+
export declare const timeTravelDeactivateTool: MnemeTool;
|
|
44
|
+
export declare const timeTravelTools: MnemeTool[];
|
|
45
|
+
export {};
|
|
46
|
+
//# sourceMappingURL=_timetravel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_timetravel.d.ts","sourceRoot":"","sources":["../../src/tools/_timetravel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,UAAU,eAAe;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAYD,uEAAuE;AACvE,wBAAgB,kBAAkB,IAAI,QAAQ,CAAC,eAAe,CAAC,CAE9D;AAED,8CAA8C;AAC9C,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AA4BD,eAAO,MAAM,sBAAsB,EAAE,SA+FpC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,SAwClC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,SAsCtC,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,SAAS,EAItC,CAAC"}
|