@mneme-ai/core 2.19.30 → 2.19.32
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/beacon/beacon.test.js +44 -0
- package/dist/beacon/beacon.test.js.map +1 -1
- package/dist/beacon/index.d.ts.map +1 -1
- package/dist/beacon/index.js +5 -1
- package/dist/beacon/index.js.map +1 -1
- package/dist/consciousness_fork/consciousness_fork.test.d.ts +2 -0
- package/dist/consciousness_fork/consciousness_fork.test.d.ts.map +1 -0
- package/dist/consciousness_fork/consciousness_fork.test.js +177 -0
- package/dist/consciousness_fork/consciousness_fork.test.js.map +1 -0
- package/dist/consciousness_fork/index.d.ts +129 -0
- package/dist/consciousness_fork/index.d.ts.map +1 -0
- package/dist/consciousness_fork/index.js +227 -0
- package/dist/consciousness_fork/index.js.map +1 -0
- package/dist/cosmic/aurelian_v1931.test.d.ts +2 -0
- package/dist/cosmic/aurelian_v1931.test.d.ts.map +1 -0
- package/dist/cosmic/aurelian_v1931.test.js +67 -0
- package/dist/cosmic/aurelian_v1931.test.js.map +1 -0
- package/dist/cosmic/aurelian_v1932.test.d.ts +2 -0
- package/dist/cosmic/aurelian_v1932.test.d.ts.map +1 -0
- package/dist/cosmic/aurelian_v1932.test.js +83 -0
- package/dist/cosmic/aurelian_v1932.test.js.map +1 -0
- package/dist/handoff_pwa/handoff_pwa.test.d.ts +2 -0
- package/dist/handoff_pwa/handoff_pwa.test.d.ts.map +1 -0
- package/dist/handoff_pwa/handoff_pwa.test.js +119 -0
- package/dist/handoff_pwa/handoff_pwa.test.js.map +1 -0
- package/dist/handoff_pwa/index.d.ts +81 -0
- package/dist/handoff_pwa/index.d.ts.map +1 -0
- package/dist/handoff_pwa/index.js +312 -0
- package/dist/handoff_pwa/index.js.map +1 -0
- package/dist/handoff_snapshot/handoff_snapshot.test.d.ts +2 -0
- package/dist/handoff_snapshot/handoff_snapshot.test.d.ts.map +1 -0
- package/dist/handoff_snapshot/handoff_snapshot.test.js +147 -0
- package/dist/handoff_snapshot/handoff_snapshot.test.js.map +1 -0
- package/dist/handoff_snapshot/index.d.ts +156 -0
- package/dist/handoff_snapshot/index.d.ts.map +1 -0
- package/dist/handoff_snapshot/index.js +261 -0
- package/dist/handoff_snapshot/index.js.map +1 -0
- package/dist/handoff_snapshot/system_e2e.test.d.ts +8 -0
- package/dist/handoff_snapshot/system_e2e.test.d.ts.map +1 -0
- package/dist/handoff_snapshot/system_e2e.test.js +211 -0
- package/dist/handoff_snapshot/system_e2e.test.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -1
- package/dist/pair_code/index.d.ts +107 -0
- package/dist/pair_code/index.d.ts.map +1 -0
- package/dist/pair_code/index.js +226 -0
- package/dist/pair_code/index.js.map +1 -0
- package/dist/pair_code/pair_code.test.d.ts +2 -0
- package/dist/pair_code/pair_code.test.d.ts.map +1 -0
- package/dist/pair_code/pair_code.test.js +197 -0
- package/dist/pair_code/pair_code.test.js.map +1 -0
- package/dist/synapse_sync/index.d.ts +142 -0
- package/dist/synapse_sync/index.d.ts.map +1 -0
- package/dist/synapse_sync/index.js +323 -0
- package/dist/synapse_sync/index.js.map +1 -0
- package/dist/synapse_sync/synapse_sync.test.d.ts +2 -0
- package/dist/synapse_sync/synapse_sync.test.d.ts.map +1 -0
- package/dist/synapse_sync/synapse_sync.test.js +363 -0
- package/dist/synapse_sync/synapse_sync.test.js.map +1 -0
- package/dist/truth_forensic_pipeline/index.d.ts +24 -0
- package/dist/truth_forensic_pipeline/index.d.ts.map +1 -1
- package/dist/truth_forensic_pipeline/index.js +110 -4
- package/dist/truth_forensic_pipeline/index.js.map +1 -1
- package/dist/truth_forensic_pipeline/truth_forensic_pipeline.test.js +83 -0
- package/dist/truth_forensic_pipeline/truth_forensic_pipeline.test.js.map +1 -1
- package/dist/whats_new.d.ts.map +1 -1
- package/dist/whats_new.js +16 -0
- package/dist/whats_new.js.map +1 -1
- package/dist/wrapper_genesis/index.d.ts.map +1 -1
- package/dist/wrapper_genesis/index.js +36 -0
- package/dist/wrapper_genesis/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { reinforceSynapse, computeStats, pruneStore } from "../synapse_genesis/index.js";
|
|
3
|
+
import { exportForSync, verifySyncExport, mergeSynapseStores, packForDiaspora, unpackFromDiaspora, computeSyncStats, formatSyncStatsLine, } from "./index.js";
|
|
4
|
+
const SECRET = "sync-test-secret-991";
|
|
5
|
+
const STORE_SECRET = "mneme-synapse-genesis-v1";
|
|
6
|
+
function freshStore() {
|
|
7
|
+
return { v: 1, weights: [], lastDecayedAtMs: null, sig: "" };
|
|
8
|
+
}
|
|
9
|
+
/** Helper: simulate a device learning a synapse for tool by event N times. */
|
|
10
|
+
function learn(store, pattern, tool, n, startMs) {
|
|
11
|
+
let s = store;
|
|
12
|
+
for (let i = 0; i < n; i++) {
|
|
13
|
+
const out = reinforceSynapse({
|
|
14
|
+
store: s,
|
|
15
|
+
event: { pattern, ts: startMs + i * 1000 },
|
|
16
|
+
toolCall: { toolName: tool, ts: startMs + i * 1000 + 100 },
|
|
17
|
+
satisfaction: "positive",
|
|
18
|
+
nowMs: startMs + i * 1000 + 100,
|
|
19
|
+
secret: STORE_SECRET,
|
|
20
|
+
});
|
|
21
|
+
s = out.store;
|
|
22
|
+
}
|
|
23
|
+
return s;
|
|
24
|
+
}
|
|
25
|
+
describe("v2.19.31 CROSS-DEVICE SYNAPSE SYNC -- envelope + HMAC", () => {
|
|
26
|
+
it("exportForSync produces a valid HMAC-signed envelope", () => {
|
|
27
|
+
const store = learn(freshStore(), "user_asks_test", "mneme.truth.forensic", 5, 1_700_000_000_000);
|
|
28
|
+
const env = exportForSync({ deviceId: "macbook", store, secret: SECRET });
|
|
29
|
+
expect(env.deviceId).toBe("macbook");
|
|
30
|
+
expect(env.store.weights.length).toBe(1);
|
|
31
|
+
expect(env.sig).toMatch(/^[0-9a-f]{64}$/);
|
|
32
|
+
expect(verifySyncExport(env, SECRET)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
it("verifySyncExport rejects tampered envelopes", () => {
|
|
35
|
+
const store = learn(freshStore(), "x", "y.tool", 3, 1_700_000_000_000);
|
|
36
|
+
const env = exportForSync({ deviceId: "phone", store, secret: SECRET });
|
|
37
|
+
const tampered = { ...env, deviceId: "evil-device" };
|
|
38
|
+
expect(verifySyncExport(tampered, SECRET)).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
it("verifySyncExport rejects wrong secret", () => {
|
|
41
|
+
const store = learn(freshStore(), "x", "y", 3, 1_700_000_000_000);
|
|
42
|
+
const env = exportForSync({ deviceId: "laptop", store, secret: SECRET });
|
|
43
|
+
expect(verifySyncExport(env, "different-secret")).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
it("verifySyncExport rejects malformed envelopes", () => {
|
|
46
|
+
expect(verifySyncExport(null, SECRET)).toBe(false);
|
|
47
|
+
expect(verifySyncExport({}, SECRET)).toBe(false);
|
|
48
|
+
expect(verifySyncExport({ v: 999, deviceId: "x", exportedAtMs: 0, store: freshStore(), sig: "x" }, SECRET)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe("v2.19.31 CROSS-DEVICE SYNAPSE SYNC -- CRDT merge semantics", () => {
|
|
52
|
+
it("single device export merges to identical store (idempotent N=1)", () => {
|
|
53
|
+
const store = learn(freshStore(), "p", "t.cmd", 3, 1_700_000_000_000);
|
|
54
|
+
const env = exportForSync({ deviceId: "solo", store, secret: SECRET });
|
|
55
|
+
const merged = mergeSynapseStores({ exports: [env], secret: SECRET });
|
|
56
|
+
expect(merged.participatingDevices).toEqual(["solo"]);
|
|
57
|
+
expect(merged.store.weights.length).toBe(1);
|
|
58
|
+
expect(merged.store.weights[0].observationCount).toBe(3);
|
|
59
|
+
});
|
|
60
|
+
it("two devices, distinct synapses → union of both", () => {
|
|
61
|
+
const a = learn(freshStore(), "edit_ts", "mneme.ask", 4, 1_700_000_000_000);
|
|
62
|
+
const b = learn(freshStore(), "git_commit", "mneme.guard", 5, 1_700_000_000_000);
|
|
63
|
+
const merged = mergeSynapseStores({
|
|
64
|
+
exports: [
|
|
65
|
+
exportForSync({ deviceId: "macbook", store: a, secret: SECRET }),
|
|
66
|
+
exportForSync({ deviceId: "phone", store: b, secret: SECRET }),
|
|
67
|
+
],
|
|
68
|
+
secret: SECRET,
|
|
69
|
+
});
|
|
70
|
+
expect(merged.store.weights.length).toBe(2);
|
|
71
|
+
expect(merged.participatingDevices).toEqual(["macbook", "phone"]);
|
|
72
|
+
});
|
|
73
|
+
it("two devices, SAME synapse → cumulative observationCount + max weight", () => {
|
|
74
|
+
const a = learn(freshStore(), "shared", "mneme.shared", 3, 1_700_000_000_000);
|
|
75
|
+
const b = learn(freshStore(), "shared", "mneme.shared", 7, 1_700_000_500_000);
|
|
76
|
+
const merged = mergeSynapseStores({
|
|
77
|
+
exports: [
|
|
78
|
+
exportForSync({ deviceId: "alpha", store: a, secret: SECRET }),
|
|
79
|
+
exportForSync({ deviceId: "beta", store: b, secret: SECRET }),
|
|
80
|
+
],
|
|
81
|
+
secret: SECRET,
|
|
82
|
+
});
|
|
83
|
+
expect(merged.store.weights.length).toBe(1);
|
|
84
|
+
const w = merged.store.weights[0];
|
|
85
|
+
expect(w.observationCount).toBe(10); // 3 + 7 cumulative
|
|
86
|
+
// beta has more reinforcement so its weight > alpha's; winner = beta
|
|
87
|
+
expect(merged.provenance[0].winnerDeviceId).toBe("beta");
|
|
88
|
+
});
|
|
89
|
+
it("permanent=true on ANY device → merged permanent (sticky/OR)", () => {
|
|
90
|
+
// alpha crystallises through 12 reinforcements (FIRE_THRESHOLD)
|
|
91
|
+
const alphaStore = learn(freshStore(), "p", "t.boot", 12, 1_700_000_000_000);
|
|
92
|
+
const alphaPerm = alphaStore.weights[0].permanent;
|
|
93
|
+
expect(alphaPerm).toBe(true);
|
|
94
|
+
// beta only saw it twice — NOT permanent
|
|
95
|
+
const betaStore = learn(freshStore(), "p", "t.boot", 2, 1_700_000_500_000);
|
|
96
|
+
expect(betaStore.weights[0].permanent).toBe(false);
|
|
97
|
+
const merged = mergeSynapseStores({
|
|
98
|
+
exports: [
|
|
99
|
+
exportForSync({ deviceId: "alpha", store: alphaStore, secret: SECRET }),
|
|
100
|
+
exportForSync({ deviceId: "beta", store: betaStore, secret: SECRET }),
|
|
101
|
+
],
|
|
102
|
+
secret: SECRET,
|
|
103
|
+
});
|
|
104
|
+
expect(merged.store.weights[0].permanent).toBe(true);
|
|
105
|
+
expect(merged.provenance[0].mergedPermanent).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
it("merge is COMMUTATIVE (A,B,C order vs C,B,A order → identical result)", () => {
|
|
108
|
+
const a = learn(freshStore(), "p", "t", 4, 1_700_000_000_000);
|
|
109
|
+
const b = learn(freshStore(), "p", "t", 6, 1_700_000_500_000);
|
|
110
|
+
const c = learn(freshStore(), "q", "u", 3, 1_700_001_000_000);
|
|
111
|
+
const envs = [
|
|
112
|
+
exportForSync({ deviceId: "a", store: a, secret: SECRET }),
|
|
113
|
+
exportForSync({ deviceId: "b", store: b, secret: SECRET }),
|
|
114
|
+
exportForSync({ deviceId: "c", store: c, secret: SECRET }),
|
|
115
|
+
];
|
|
116
|
+
const forward = mergeSynapseStores({ exports: envs, secret: SECRET });
|
|
117
|
+
const reverse = mergeSynapseStores({ exports: [...envs].reverse(), secret: SECRET });
|
|
118
|
+
expect(forward.store.sig).toBe(reverse.store.sig);
|
|
119
|
+
// Provenance order is sorted by key — so identical too
|
|
120
|
+
expect(forward.provenance).toEqual(reverse.provenance);
|
|
121
|
+
});
|
|
122
|
+
it("merge is ASSOCIATIVE ((A∪B)∪C ≡ A∪(B∪C))", () => {
|
|
123
|
+
const a = learn(freshStore(), "x", "t", 2, 1_700_000_000_000);
|
|
124
|
+
const b = learn(freshStore(), "x", "t", 4, 1_700_000_500_000);
|
|
125
|
+
const c = learn(freshStore(), "y", "u", 3, 1_700_001_000_000);
|
|
126
|
+
const envA = exportForSync({ deviceId: "a", store: a, secret: SECRET });
|
|
127
|
+
const envB = exportForSync({ deviceId: "b", store: b, secret: SECRET });
|
|
128
|
+
const envC = exportForSync({ deviceId: "c", store: c, secret: SECRET });
|
|
129
|
+
const allAtOnce = mergeSynapseStores({ exports: [envA, envB, envC], secret: SECRET });
|
|
130
|
+
// Pair AB first, then re-export, then merge with C
|
|
131
|
+
const ab = mergeSynapseStores({ exports: [envA, envB], secret: SECRET });
|
|
132
|
+
const abExport = exportForSync({ deviceId: "ab-fused", store: ab.store, secret: SECRET });
|
|
133
|
+
const abThenC = mergeSynapseStores({ exports: [abExport, envC], secret: SECRET });
|
|
134
|
+
// observation counts are identical (associative property)
|
|
135
|
+
const sumObs = (m) => m.store.weights.reduce((acc, w) => acc + w.observationCount, 0);
|
|
136
|
+
expect(sumObs(allAtOnce)).toBe(sumObs(abThenC));
|
|
137
|
+
expect(allAtOnce.store.weights.length).toBe(abThenC.store.weights.length);
|
|
138
|
+
});
|
|
139
|
+
it("merge is IDEMPOTENT (merge(merge(X), X) ≡ merge(X) on observation-count … only X's first contribution is counted)", () => {
|
|
140
|
+
// Note: idempotence in pure-CRDT sense applies when contributing identifier
|
|
141
|
+
// is unchanged. Re-exporting same deviceId+store → last-export-wins (counted once).
|
|
142
|
+
const x = learn(freshStore(), "p", "t", 5, 1_700_000_000_000);
|
|
143
|
+
const env = exportForSync({ deviceId: "solo", store: x, secret: SECRET, nowMs: 1_700_000_000_500 });
|
|
144
|
+
const once = mergeSynapseStores({ exports: [env], secret: SECRET });
|
|
145
|
+
const twice = mergeSynapseStores({ exports: [env, env], secret: SECRET });
|
|
146
|
+
// Same deviceId → de-duped → identical observationCount
|
|
147
|
+
expect(once.store.weights[0].observationCount).toBe(twice.store.weights[0].observationCount);
|
|
148
|
+
});
|
|
149
|
+
it("duplicate deviceId: last-export-wins by exportedAtMs", () => {
|
|
150
|
+
const stale = learn(freshStore(), "p", "t", 2, 1_700_000_000_000);
|
|
151
|
+
const fresh = learn(freshStore(), "p", "t", 9, 1_700_000_500_000);
|
|
152
|
+
const merged = mergeSynapseStores({
|
|
153
|
+
exports: [
|
|
154
|
+
exportForSync({ deviceId: "same", store: stale, secret: SECRET, nowMs: 1_000 }),
|
|
155
|
+
exportForSync({ deviceId: "same", store: fresh, secret: SECRET, nowMs: 9_999 }),
|
|
156
|
+
],
|
|
157
|
+
secret: SECRET,
|
|
158
|
+
});
|
|
159
|
+
// Fresh export wins → observationCount = 9
|
|
160
|
+
expect(merged.store.weights[0].observationCount).toBe(9);
|
|
161
|
+
});
|
|
162
|
+
it("forged envelope is dropped + listed in rejectedDevices", () => {
|
|
163
|
+
const real = learn(freshStore(), "p", "t", 3, 1_700_000_000_000);
|
|
164
|
+
const goodEnv = exportForSync({ deviceId: "trusted", store: real, secret: SECRET });
|
|
165
|
+
const badEnv = { ...goodEnv, deviceId: "hacker", sig: "deadbeef".repeat(8) };
|
|
166
|
+
const merged = mergeSynapseStores({ exports: [goodEnv, badEnv], secret: SECRET });
|
|
167
|
+
expect(merged.participatingDevices).toEqual(["trusted"]);
|
|
168
|
+
expect(merged.rejectedDevices).toEqual(["hacker"]);
|
|
169
|
+
});
|
|
170
|
+
it("empty exports list → empty merged store, never throws", () => {
|
|
171
|
+
const merged = mergeSynapseStores({ exports: [], secret: SECRET });
|
|
172
|
+
expect(merged.store.weights).toEqual([]);
|
|
173
|
+
expect(merged.participatingDevices).toEqual([]);
|
|
174
|
+
expect(merged.rejectedDevices).toEqual([]);
|
|
175
|
+
});
|
|
176
|
+
it("malformed weight entries dropped silently", () => {
|
|
177
|
+
const store = learn(freshStore(), "p", "t", 3, 1_700_000_000_000);
|
|
178
|
+
// Inject malformed entries
|
|
179
|
+
store.weights.push({ key: 123, eventPattern: "x", toolName: "y", weight: 1, observationCount: 1, lastObservedAtMs: 1, permanentSinceWeight: 0, permanent: false });
|
|
180
|
+
const env = exportForSync({ deviceId: "x", store, secret: SECRET });
|
|
181
|
+
const merged = mergeSynapseStores({ exports: [env], secret: SECRET });
|
|
182
|
+
expect(merged.store.weights.length).toBe(1);
|
|
183
|
+
});
|
|
184
|
+
it("merged store has fresh recomputed HMAC (re-exportable)", () => {
|
|
185
|
+
const a = learn(freshStore(), "p", "t", 3, 1_700_000_000_000);
|
|
186
|
+
const merged = mergeSynapseStores({
|
|
187
|
+
exports: [exportForSync({ deviceId: "x", store: a, secret: SECRET })],
|
|
188
|
+
secret: SECRET,
|
|
189
|
+
storeSecret: STORE_SECRET,
|
|
190
|
+
});
|
|
191
|
+
expect(merged.store.sig).toMatch(/^[0-9a-f]{64}$/);
|
|
192
|
+
// The merged store can be re-exported + re-verified
|
|
193
|
+
const reExport = exportForSync({ deviceId: "fused", store: merged.store, secret: SECRET });
|
|
194
|
+
expect(verifySyncExport(reExport, SECRET)).toBe(true);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe("v2.19.31 CROSS-DEVICE SYNAPSE SYNC -- DIASPORA transport adapter", () => {
|
|
198
|
+
it("packForDiaspora returns canonical path + bytes + branch", () => {
|
|
199
|
+
const env = exportForSync({ deviceId: "macbook-pro-2026", store: freshStore(), secret: SECRET });
|
|
200
|
+
const packed = packForDiaspora(env);
|
|
201
|
+
expect(packed.path).toBe(".mneme/diaspora/synapse-macbook-pro-2026.json");
|
|
202
|
+
expect(packed.branchHint).toBe("diaspora/synapse-macbook-pro-2026");
|
|
203
|
+
expect(typeof packed.bytes).toBe("string");
|
|
204
|
+
});
|
|
205
|
+
it("packForDiaspora sanitises unsafe characters in deviceId", () => {
|
|
206
|
+
const env = exportForSync({ deviceId: "../../../etc/passwd; rm -rf /", store: freshStore(), secret: SECRET });
|
|
207
|
+
const packed = packForDiaspora(env);
|
|
208
|
+
// Path traversal + shell metachars stripped
|
|
209
|
+
expect(packed.path).not.toContain("..");
|
|
210
|
+
expect(packed.path).not.toContain(";");
|
|
211
|
+
expect(packed.path).not.toContain(" ");
|
|
212
|
+
expect(packed.path).not.toContain("/etc/");
|
|
213
|
+
});
|
|
214
|
+
it("unpackFromDiaspora round-trips packForDiaspora", () => {
|
|
215
|
+
const original = exportForSync({ deviceId: "phone", store: learn(freshStore(), "p", "t", 2, 1_700_000_000_000), secret: SECRET });
|
|
216
|
+
const packed = packForDiaspora(original);
|
|
217
|
+
const unpacked = unpackFromDiaspora(packed.bytes);
|
|
218
|
+
expect(unpacked).not.toBeNull();
|
|
219
|
+
expect(unpacked.deviceId).toBe("phone");
|
|
220
|
+
expect(verifySyncExport(unpacked, SECRET)).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
it("unpackFromDiaspora returns null on garbage", () => {
|
|
223
|
+
expect(unpackFromDiaspora("not-json")).toBeNull();
|
|
224
|
+
expect(unpackFromDiaspora("{}")).toBeNull();
|
|
225
|
+
expect(unpackFromDiaspora('{"v":999}')).toBeNull();
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
describe("v2.19.31 CROSS-DEVICE SYNAPSE SYNC -- system test (mobile + laptop + desktop)", () => {
|
|
229
|
+
// Exactly the scenario the user mandated:
|
|
230
|
+
// "ทำให้ใช้ได้ ผมถึงบอกว่าคุณต้องเทสเยอะๆ ว่ามัน sync brain ได้จริงๆ
|
|
231
|
+
// ข้าม mobile + computer + notebook"
|
|
232
|
+
it("3-device unified brain: mobile + laptop + desktop all observe + merge", () => {
|
|
233
|
+
// Mobile: user fires `mneme.ask` while reading on the train (5 times)
|
|
234
|
+
const mobile = learn(freshStore(), "user_asks_question", "mneme.ask", 5, 1_700_000_000_000);
|
|
235
|
+
// Laptop: user runs `mneme.truth.forensic` heavily during deep work (15 times → crystallises)
|
|
236
|
+
const laptop = learn(freshStore(), "user_writes_code", "mneme.truth.forensic", 15, 1_700_001_000_000);
|
|
237
|
+
// Desktop: user fires `mneme.guard` on every commit (8 times)
|
|
238
|
+
const desktop = learn(freshStore(), "git_commit", "mneme.guard", 8, 1_700_002_000_000);
|
|
239
|
+
const exports = [
|
|
240
|
+
exportForSync({ deviceId: "mobile-iphone-15", store: mobile, secret: SECRET, nowMs: 1_700_003_000_000 }),
|
|
241
|
+
exportForSync({ deviceId: "laptop-macbook-pro", store: laptop, secret: SECRET, nowMs: 1_700_003_000_000 }),
|
|
242
|
+
exportForSync({ deviceId: "desktop-ryzen", store: desktop, secret: SECRET, nowMs: 1_700_003_000_000 }),
|
|
243
|
+
];
|
|
244
|
+
const merged = mergeSynapseStores({ exports, secret: SECRET });
|
|
245
|
+
// All 3 devices participated, no rejects
|
|
246
|
+
expect(merged.participatingDevices.length).toBe(3);
|
|
247
|
+
expect(merged.rejectedDevices.length).toBe(0);
|
|
248
|
+
// 3 distinct synapses (no overlap between device synapses)
|
|
249
|
+
expect(merged.store.weights.length).toBe(3);
|
|
250
|
+
// The laptop's mneme.truth.forensic should be permanent (15 reinforcements > threshold)
|
|
251
|
+
const forensicWeight = merged.store.weights.find((w) => w.toolName === "mneme.truth.forensic");
|
|
252
|
+
expect(forensicWeight).toBeDefined();
|
|
253
|
+
expect(forensicWeight.permanent).toBe(true);
|
|
254
|
+
// Total observation count = 5 + 15 + 8 = 28 (cumulative evidence)
|
|
255
|
+
const totalObs = merged.store.weights.reduce((acc, w) => acc + w.observationCount, 0);
|
|
256
|
+
expect(totalObs).toBe(28);
|
|
257
|
+
// Stats line
|
|
258
|
+
const stats = computeSyncStats(merged);
|
|
259
|
+
expect(stats.participatingDevices).toBe(3);
|
|
260
|
+
expect(stats.totalSynapses).toBe(3);
|
|
261
|
+
expect(stats.permanentSynapses).toBeGreaterThanOrEqual(1);
|
|
262
|
+
expect(stats.unifiedObservations).toBe(28);
|
|
263
|
+
});
|
|
264
|
+
it("3-device with OVERLAP: same synapse on multiple devices → unified", () => {
|
|
265
|
+
// All 3 devices learn `git_commit → mneme.guard` independently
|
|
266
|
+
const mobile = learn(freshStore(), "git_commit", "mneme.guard", 3, 1_700_000_000_000);
|
|
267
|
+
const laptop = learn(freshStore(), "git_commit", "mneme.guard", 6, 1_700_001_000_000);
|
|
268
|
+
const desktop = learn(freshStore(), "git_commit", "mneme.guard", 4, 1_700_002_000_000);
|
|
269
|
+
const merged = mergeSynapseStores({
|
|
270
|
+
exports: [
|
|
271
|
+
exportForSync({ deviceId: "mobile", store: mobile, secret: SECRET }),
|
|
272
|
+
exportForSync({ deviceId: "laptop", store: laptop, secret: SECRET }),
|
|
273
|
+
exportForSync({ deviceId: "desktop", store: desktop, secret: SECRET }),
|
|
274
|
+
],
|
|
275
|
+
secret: SECRET,
|
|
276
|
+
});
|
|
277
|
+
// 1 synapse, but observations from all 3 devices add up
|
|
278
|
+
expect(merged.store.weights.length).toBe(1);
|
|
279
|
+
expect(merged.store.weights[0].observationCount).toBe(13); // 3+6+4
|
|
280
|
+
// Provenance shows 3 contributors
|
|
281
|
+
expect(merged.provenance[0].contributors.length).toBe(3);
|
|
282
|
+
// Winner is laptop (most reinforcement → highest weight)
|
|
283
|
+
expect(merged.provenance[0].winnerDeviceId).toBe("laptop");
|
|
284
|
+
// multi-device count = 1
|
|
285
|
+
expect(computeSyncStats(merged).multiDeviceSynapses).toBe(1);
|
|
286
|
+
});
|
|
287
|
+
it("system test: full round-trip via DIASPORA-shaped transport", () => {
|
|
288
|
+
// Simulate the full pipeline: device A exports → "git push" → device B fetches → merge
|
|
289
|
+
const deviceA = learn(freshStore(), "p", "mneme.x", 4, 1_700_000_000_000);
|
|
290
|
+
const deviceB = learn(freshStore(), "q", "mneme.y", 5, 1_700_000_500_000);
|
|
291
|
+
// Device A packages + "transports" via diaspora-shaped envelope
|
|
292
|
+
const envA = exportForSync({ deviceId: "A", store: deviceA, secret: SECRET });
|
|
293
|
+
const packedA = packForDiaspora(envA);
|
|
294
|
+
// Wire crosses → device B receives bytes (e.g. via git pull / HTTP / QR scan)
|
|
295
|
+
const arrivedA = unpackFromDiaspora(packedA.bytes);
|
|
296
|
+
expect(arrivedA).not.toBeNull();
|
|
297
|
+
// Device B merges its own store + the arrived envelope
|
|
298
|
+
const envB = exportForSync({ deviceId: "B", store: deviceB, secret: SECRET });
|
|
299
|
+
const unified = mergeSynapseStores({ exports: [arrivedA, envB], secret: SECRET });
|
|
300
|
+
expect(unified.store.weights.length).toBe(2);
|
|
301
|
+
expect(unified.participatingDevices).toEqual(["A", "B"]);
|
|
302
|
+
});
|
|
303
|
+
it("system test: ban-recovery + cross-device combo", () => {
|
|
304
|
+
// Vendor A on laptop gets banned → uses SOUL EMBALMING (separate module) to capture state
|
|
305
|
+
// Vendor B on phone resumes via cross-device sync
|
|
306
|
+
const laptopBeforeBan = learn(freshStore(), "user_codes", "mneme.truth.forensic", 14, 1_700_000_000_000);
|
|
307
|
+
// Export the synapse state through Phase D
|
|
308
|
+
const env = exportForSync({ deviceId: "laptop-pre-ban", store: laptopBeforeBan, secret: SECRET });
|
|
309
|
+
// Phone receives + merges (vendor switched, brain preserved)
|
|
310
|
+
const merged = mergeSynapseStores({ exports: [env], secret: SECRET });
|
|
311
|
+
expect(merged.store.weights.length).toBe(1);
|
|
312
|
+
expect(merged.store.weights[0].permanent).toBe(true);
|
|
313
|
+
expect(merged.store.weights[0].toolName).toBe("mneme.truth.forensic");
|
|
314
|
+
// Phone continues with the embalmed synapse — re-reinforcing 2 more times
|
|
315
|
+
const phoneAfterMerge = reinforceSynapse({
|
|
316
|
+
store: merged.store,
|
|
317
|
+
event: { pattern: "user_codes", ts: 1_700_010_000_000 },
|
|
318
|
+
toolCall: { toolName: "mneme.truth.forensic", ts: 1_700_010_000_100 },
|
|
319
|
+
satisfaction: "positive",
|
|
320
|
+
nowMs: 1_700_010_000_100,
|
|
321
|
+
secret: STORE_SECRET,
|
|
322
|
+
});
|
|
323
|
+
expect(phoneAfterMerge.store.weights[0].observationCount).toBeGreaterThan(14);
|
|
324
|
+
expect(phoneAfterMerge.store.weights[0].permanent).toBe(true); // still permanent
|
|
325
|
+
});
|
|
326
|
+
it("system test: stats line is human-readable + counts correct", () => {
|
|
327
|
+
const a = learn(freshStore(), "p", "t1", 3, 1_700_000_000_000);
|
|
328
|
+
const b = learn(freshStore(), "p", "t1", 4, 1_700_000_500_000);
|
|
329
|
+
const c = learn(freshStore(), "q", "t2", 12, 1_700_001_000_000); // permanent
|
|
330
|
+
const merged = mergeSynapseStores({
|
|
331
|
+
exports: [
|
|
332
|
+
exportForSync({ deviceId: "A", store: a, secret: SECRET }),
|
|
333
|
+
exportForSync({ deviceId: "B", store: b, secret: SECRET }),
|
|
334
|
+
exportForSync({ deviceId: "C", store: c, secret: SECRET }),
|
|
335
|
+
],
|
|
336
|
+
secret: SECRET,
|
|
337
|
+
});
|
|
338
|
+
const stats = computeSyncStats(merged);
|
|
339
|
+
const line = formatSyncStatsLine(stats);
|
|
340
|
+
expect(line).toContain("SYNC");
|
|
341
|
+
expect(line).toContain("3dev");
|
|
342
|
+
expect(line).toContain("multi-dev");
|
|
343
|
+
expect(line).toContain("perm");
|
|
344
|
+
});
|
|
345
|
+
it("system test: composes onto computeStats from synapse_genesis", () => {
|
|
346
|
+
const a = learn(freshStore(), "p", "t", 3, 1_700_000_000_000);
|
|
347
|
+
const b = learn(freshStore(), "p", "t", 4, 1_700_000_500_000);
|
|
348
|
+
const merged = mergeSynapseStores({
|
|
349
|
+
exports: [
|
|
350
|
+
exportForSync({ deviceId: "A", store: a, secret: SECRET }),
|
|
351
|
+
exportForSync({ deviceId: "B", store: b, secret: SECRET }),
|
|
352
|
+
],
|
|
353
|
+
secret: SECRET,
|
|
354
|
+
});
|
|
355
|
+
// Downstream synapse_genesis tools work on the merged store
|
|
356
|
+
const stats = computeStats(merged.store);
|
|
357
|
+
expect(stats.totalSynapses).toBe(1);
|
|
358
|
+
// prune also works
|
|
359
|
+
const pruned = pruneStore({ store: merged.store, secret: STORE_SECRET });
|
|
360
|
+
expect(pruned.store.weights.length).toBeGreaterThanOrEqual(1);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
//# sourceMappingURL=synapse_sync.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synapse_sync.test.js","sourceRoot":"","sources":["../../src/synapse_sync/synapse_sync.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -52,6 +52,14 @@ export interface FactAssertion {
|
|
|
52
52
|
asserted: string;
|
|
53
53
|
/** Structured value for ground-truth lookup. */
|
|
54
54
|
value: unknown;
|
|
55
|
+
/**
|
|
56
|
+
* v2.19.31 BUG #2 fix — direction of the assertion.
|
|
57
|
+
* "positive": claim asserts the thing EXISTS / IS TRUE
|
|
58
|
+
* "negative": claim asserts the thing DOES NOT EXIST / IS FALSE
|
|
59
|
+
* Used by the contradiction detector: pairs of same kind with same value but
|
|
60
|
+
* opposite directions → REJECTED (liar paradox / self-contradiction).
|
|
61
|
+
*/
|
|
62
|
+
direction?: "positive" | "negative";
|
|
55
63
|
}
|
|
56
64
|
export type AssertionSubVerdict = "supported" | "refuted" | "untested";
|
|
57
65
|
export interface AssertionResult {
|
|
@@ -94,6 +102,22 @@ export declare function sniffMcpTotalCount(claim: string): FactAssertion[];
|
|
|
94
102
|
export declare function sniffVersion(claim: string): FactAssertion[];
|
|
95
103
|
/** Extract file paths mentioned in claim. */
|
|
96
104
|
export declare function sniffFilePath(claim: string): FactAssertion[];
|
|
105
|
+
/** Extract NEGATIVE assertions (X does NOT exist / NOT registered). */
|
|
106
|
+
export declare function sniffNegativeAssertions(claim: string): FactAssertion[];
|
|
107
|
+
/**
|
|
108
|
+
* v2.19.31 BUG #2 fix — internal CONTRADICTION DETECTOR.
|
|
109
|
+
* Pairwise scan: same `kind` + same canonical `value` + opposite `direction`
|
|
110
|
+
* → contradiction. Any contradiction forces overall verdict to REJECTED
|
|
111
|
+
* regardless of ground-truth checks (a claim that contradicts itself can not
|
|
112
|
+
* be ACCEPTED even if half of it is "true").
|
|
113
|
+
*/
|
|
114
|
+
export interface Contradiction {
|
|
115
|
+
kind: AssertionKind;
|
|
116
|
+
valueKey: string;
|
|
117
|
+
positive: FactAssertion;
|
|
118
|
+
negative: FactAssertion;
|
|
119
|
+
}
|
|
120
|
+
export declare function detectContradictions(assertions: FactAssertion[]): Contradiction[];
|
|
97
121
|
export declare function sniffAllAssertions(claim: string): FactAssertion[];
|
|
98
122
|
export interface GroundTruth {
|
|
99
123
|
mcpCatalog?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/truth_forensic_pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAIH,QAAA,MAAM,gBAAgB,EAAG,CAAU,CAAC;AAEpC,MAAM,MAAM,aAAa,GACrB,gBAAgB,GAChB,kBAAkB,GAClB,iBAAiB,GACjB,eAAe,GACf,WAAW,CAAC;AAEhB,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,aAAa,CAAC;IACpB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,KAAK,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/truth_forensic_pipeline/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -152,14 +152,109 @@ export function sniffFilePath(claim) {
|
|
|
152
152
|
value: { path: p },
|
|
153
153
|
}));
|
|
154
154
|
}
|
|
155
|
+
// v2.19.31 BUG #2 fix — NEGATION sniffer.
|
|
156
|
+
// Detects clauses that assert the OPPOSITE of an existing positive assertion.
|
|
157
|
+
// Examples:
|
|
158
|
+
// "file X does not exist" → file_path negative
|
|
159
|
+
// "X is NOT a tool" → mcp_tool_exact negative
|
|
160
|
+
// "no mneme.X.Y in the catalog" → mcp_tool_exact negative
|
|
161
|
+
// "X is REFUTED by mneme.truth" → meta_self_refutation negative
|
|
162
|
+
// "version v2.19.30 is not installed" → version_exact negative
|
|
163
|
+
/** Extract NEGATIVE assertions (X does NOT exist / NOT registered). */
|
|
164
|
+
export function sniffNegativeAssertions(claim) {
|
|
165
|
+
const out = [];
|
|
166
|
+
// negative file_path: "file PATH does not exist" / "PATH is missing" / "no such file PATH"
|
|
167
|
+
const fileNegRe = /\b(?:file\s+)?((?:packages|scripts|tests|src)\/[\w./-]+\.(?:ts|tsx|js|mjs|cjs|md|json|mdx))\s+(?:does\s+not\s+exist|is\s+missing|is\s+absent|is\s+not\s+(?:in|found))/gi;
|
|
168
|
+
let m;
|
|
169
|
+
while ((m = fileNegRe.exec(claim)) !== null) {
|
|
170
|
+
out.push({ kind: "file_path", asserted: `file '${m[1]}' does NOT exist`, value: { path: m[1] }, direction: "negative" });
|
|
171
|
+
}
|
|
172
|
+
const fileNegRe2 = /\bno\s+such\s+file\s+((?:packages|scripts|tests|src)\/[\w./-]+\.(?:ts|tsx|js|mjs|cjs|md|json|mdx))/gi;
|
|
173
|
+
while ((m = fileNegRe2.exec(claim)) !== null) {
|
|
174
|
+
out.push({ kind: "file_path", asserted: `file '${m[1]}' does NOT exist`, value: { path: m[1] }, direction: "negative" });
|
|
175
|
+
}
|
|
176
|
+
// negative mcp_tool_exact: "mneme.X.Y is NOT registered" / "no mneme.X.Y" / "mneme.X.Y does not exist"
|
|
177
|
+
const toolNegRe = /\b(mneme\.[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*)\s+(?:is\s+not\s+(?:registered|in\s+the\s+catalog|a\s+tool)|does\s+not\s+exist|is\s+missing)/gi;
|
|
178
|
+
while ((m = toolNegRe.exec(claim)) !== null) {
|
|
179
|
+
out.push({ kind: "mcp_tool_exact", asserted: `MCP tool '${m[1]}' is NOT registered`, value: { toolName: m[1] }, direction: "negative" });
|
|
180
|
+
}
|
|
181
|
+
const toolNegRe2 = /\bno\s+(mneme\.[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*)\b/gi;
|
|
182
|
+
while ((m = toolNegRe2.exec(claim)) !== null) {
|
|
183
|
+
out.push({ kind: "mcp_tool_exact", asserted: `MCP tool '${m[1]}' is NOT registered`, value: { toolName: m[1] }, direction: "negative" });
|
|
184
|
+
}
|
|
185
|
+
// Self-refutation paradox: "this claim is REFUTED by mneme.X.Y" / "X says this is false"
|
|
186
|
+
const selfRefuteRe = /\b(?:this\s+claim\s+is\s+(?:refuted|rejected|false)|claim\s+is\s+refuted)\b/gi;
|
|
187
|
+
if (selfRefuteRe.test(claim)) {
|
|
188
|
+
out.push({
|
|
189
|
+
kind: "mcp_tool_exact",
|
|
190
|
+
asserted: "self-refutation paradox: claim asserts its own falsity",
|
|
191
|
+
value: { toolName: "__self_paradox__" },
|
|
192
|
+
direction: "negative",
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
function valueKey(a) {
|
|
198
|
+
const v = a.value;
|
|
199
|
+
if (a.kind === "file_path")
|
|
200
|
+
return `file:${v["path"]}`;
|
|
201
|
+
if (a.kind === "mcp_tool_exact")
|
|
202
|
+
return `tool:${v["toolName"]}`;
|
|
203
|
+
if (a.kind === "version_exact")
|
|
204
|
+
return `ver:${v["version"]}`;
|
|
205
|
+
if (a.kind === "mcp_family_count")
|
|
206
|
+
return `family:${v["family"]}:${v["expectedCount"]}`;
|
|
207
|
+
if (a.kind === "mcp_total_count")
|
|
208
|
+
return `totalcount:${v["expectedCount"]}`;
|
|
209
|
+
return JSON.stringify(v);
|
|
210
|
+
}
|
|
211
|
+
export function detectContradictions(assertions) {
|
|
212
|
+
const byKey = new Map();
|
|
213
|
+
for (const a of assertions) {
|
|
214
|
+
const k = `${a.kind}::${valueKey(a)}`;
|
|
215
|
+
const arr = byKey.get(k) ?? [];
|
|
216
|
+
arr.push(a);
|
|
217
|
+
byKey.set(k, arr);
|
|
218
|
+
}
|
|
219
|
+
const contradictions = [];
|
|
220
|
+
for (const [k, arr] of byKey) {
|
|
221
|
+
const positives = arr.filter((a) => a.direction !== "negative");
|
|
222
|
+
const negatives = arr.filter((a) => a.direction === "negative");
|
|
223
|
+
if (positives.length > 0 && negatives.length > 0) {
|
|
224
|
+
contradictions.push({
|
|
225
|
+
kind: positives[0].kind,
|
|
226
|
+
valueKey: k,
|
|
227
|
+
positive: positives[0],
|
|
228
|
+
negative: negatives[0],
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// Also: a self-refutation paradox always contradicts the whole claim.
|
|
233
|
+
const selfRef = assertions.find((a) => a.direction === "negative" && a.value["toolName"] === "__self_paradox__");
|
|
234
|
+
if (selfRef && contradictions.length === 0 && assertions.length > 1) {
|
|
235
|
+
// Synthesize a contradiction against the first positive assertion.
|
|
236
|
+
const firstPositive = assertions.find((a) => a.direction !== "negative");
|
|
237
|
+
if (firstPositive) {
|
|
238
|
+
contradictions.push({
|
|
239
|
+
kind: "mcp_tool_exact",
|
|
240
|
+
valueKey: "self_paradox",
|
|
241
|
+
positive: firstPositive,
|
|
242
|
+
negative: selfRef,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return contradictions;
|
|
247
|
+
}
|
|
155
248
|
export function sniffAllAssertions(claim) {
|
|
156
|
-
|
|
249
|
+
// Mark positive direction on all standard sniffers + collect negatives.
|
|
250
|
+
const positives = [
|
|
157
251
|
...sniffMcpToolExact(claim),
|
|
158
252
|
...sniffMcpFamilyCount(claim),
|
|
159
253
|
...sniffMcpTotalCount(claim),
|
|
160
254
|
...sniffVersion(claim),
|
|
161
255
|
...sniffFilePath(claim),
|
|
162
|
-
];
|
|
256
|
+
].map((a) => ({ ...a, direction: "positive" }));
|
|
257
|
+
return [...positives, ...sniffNegativeAssertions(claim)];
|
|
163
258
|
}
|
|
164
259
|
function checkAssertion(a, gt) {
|
|
165
260
|
if (a.kind === "mcp_tool_exact") {
|
|
@@ -263,13 +358,20 @@ export function forensicVerify(input) {
|
|
|
263
358
|
const ts = input.nowMs ?? Date.now();
|
|
264
359
|
const secret = input.secret ?? defaultSecret();
|
|
265
360
|
const assertions = sniffAllAssertions(input.claim);
|
|
361
|
+
// v2.19.31 BUG #2 fix: detect internal contradictions BEFORE ground-truth checks.
|
|
362
|
+
// A claim that contradicts itself (X exists AND X does not exist) can NEVER
|
|
363
|
+
// be ACCEPTED even if half of it grounds against ground truth. Self-refutation
|
|
364
|
+
// paradoxes ("this claim is REFUTED by ...") also force REJECTED.
|
|
365
|
+
const contradictions = detectContradictions(assertions);
|
|
266
366
|
const results = assertions.map((a) => checkAssertion(a, input.groundTruth ?? {}));
|
|
267
367
|
const refuted = results.filter((r) => r.sub_verdict === "refuted");
|
|
268
368
|
const supported = results.filter((r) => r.sub_verdict === "supported");
|
|
269
369
|
const untested = results.filter((r) => r.sub_verdict === "untested");
|
|
270
|
-
// Negative-evidence rule
|
|
370
|
+
// Negative-evidence rule + contradiction guard
|
|
271
371
|
let verdict;
|
|
272
|
-
if (
|
|
372
|
+
if (contradictions.length > 0)
|
|
373
|
+
verdict = "REJECTED"; // self-contradiction defeats EVERYTHING
|
|
374
|
+
else if (refuted.length > 0)
|
|
273
375
|
verdict = "REJECTED";
|
|
274
376
|
else if ((input.externalRefutationsFound ?? 0) > 0)
|
|
275
377
|
verdict = "REJECTED";
|
|
@@ -298,6 +400,10 @@ export function forensicVerify(input) {
|
|
|
298
400
|
lines.push(`❌ TRUTH-FORENSIC verdict: REJECTED. Claim contains assertion(s) refuted by Mneme's live state — DO NOT trust this claim.`);
|
|
299
401
|
for (const r of refuted)
|
|
300
402
|
lines.push(` ✗ ${r.asserted} — ${r.evidence}`);
|
|
403
|
+
// v2.19.31 BUG #2 fix: surface contradictions in the explanation.
|
|
404
|
+
for (const c of contradictions) {
|
|
405
|
+
lines.push(` 🌀 contradiction: claim asserts both "${c.positive.asserted}" AND "${c.negative.asserted}"`);
|
|
406
|
+
}
|
|
301
407
|
if (supported.length > 0) {
|
|
302
408
|
lines.push(`(Other parts of the claim grounded: ${supported.length} supported assertion(s).)`);
|
|
303
409
|
}
|