@mneme-ai/core 2.15.2 → 2.16.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/agent_manifest.d.ts +1 -1
- package/dist/agent_manifest.d.ts.map +1 -1
- package/dist/agent_manifest.js +18 -1
- package/dist/agent_manifest.js.map +1 -1
- package/dist/alpha/alpha.test.d.ts +2 -0
- package/dist/alpha/alpha.test.d.ts.map +1 -0
- package/dist/alpha/alpha.test.js +101 -0
- package/dist/alpha/alpha.test.js.map +1 -0
- package/dist/alpha/index.d.ts +106 -0
- package/dist/alpha/index.d.ts.map +1 -0
- package/dist/alpha/index.js +190 -0
- package/dist/alpha/index.js.map +1 -0
- package/dist/anti_collusion/anti_collusion.test.d.ts +2 -0
- package/dist/anti_collusion/anti_collusion.test.d.ts.map +1 -0
- package/dist/anti_collusion/anti_collusion.test.js +137 -0
- package/dist/anti_collusion/anti_collusion.test.js.map +1 -0
- package/dist/anti_collusion/index.d.ts +89 -0
- package/dist/anti_collusion/index.d.ts.map +1 -0
- package/dist/anti_collusion/index.js +218 -0
- package/dist/anti_collusion/index.js.map +1 -0
- package/dist/cosmic/aurelian_v216.test.d.ts +2 -0
- package/dist/cosmic/aurelian_v216.test.d.ts.map +1 -0
- package/dist/cosmic/aurelian_v216.test.js +89 -0
- package/dist/cosmic/aurelian_v216.test.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/living_model/index.d.ts +104 -0
- package/dist/living_model/index.d.ts.map +1 -0
- package/dist/living_model/index.js +152 -0
- package/dist/living_model/index.js.map +1 -0
- package/dist/living_model/living_model.test.d.ts +2 -0
- package/dist/living_model/living_model.test.d.ts.map +1 -0
- package/dist/living_model/living_model.test.js +125 -0
- package/dist/living_model/living_model.test.js.map +1 -0
- package/dist/obelisk/index.d.ts +74 -0
- package/dist/obelisk/index.d.ts.map +1 -0
- package/dist/obelisk/index.js +114 -0
- package/dist/obelisk/index.js.map +1 -0
- package/dist/obelisk/obelisk.test.d.ts +2 -0
- package/dist/obelisk/obelisk.test.d.ts.map +1 -0
- package/dist/obelisk/obelisk.test.js +67 -0
- package/dist/obelisk/obelisk.test.js.map +1 -0
- package/dist/persona/index.d.ts +107 -0
- package/dist/persona/index.d.ts.map +1 -0
- package/dist/persona/index.js +144 -0
- package/dist/persona/index.js.map +1 -0
- package/dist/persona/persona.test.d.ts +2 -0
- package/dist/persona/persona.test.d.ts.map +1 -0
- package/dist/persona/persona.test.js +71 -0
- package/dist/persona/persona.test.js.map +1 -0
- package/dist/public_audit/index.d.ts +70 -0
- package/dist/public_audit/index.d.ts.map +1 -0
- package/dist/public_audit/index.js +175 -0
- package/dist/public_audit/index.js.map +1 -0
- package/dist/public_audit/public_audit.test.d.ts +2 -0
- package/dist/public_audit/public_audit.test.d.ts.map +1 -0
- package/dist/public_audit/public_audit.test.js +64 -0
- package/dist/public_audit/public_audit.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.16.0 — MNEME LIVING MODEL
|
|
3
|
+
*
|
|
4
|
+
* "Promote INFRA AS AI from per-host primitive to a real distributed
|
|
5
|
+
* Living Model. Anti-entropy sync between hosts + causal inference
|
|
6
|
+
* over the gossiped patterns + federated query."
|
|
7
|
+
*
|
|
8
|
+
* Phase 1 primitives (v2.16): the three abstractions a fully-distributed
|
|
9
|
+
* inference layer needs.
|
|
10
|
+
*
|
|
11
|
+
* 1. anti-entropy sync — Merkle-tree summary of local observations;
|
|
12
|
+
* peers compare summaries and exchange only the diff
|
|
13
|
+
* 2. causal inference — given pairs of observations (event A at host X,
|
|
14
|
+
* event B at host Y), compute correlation + lead/lag + a naive
|
|
15
|
+
* Granger-causality-ish score
|
|
16
|
+
* 3. federated query — given a question + a list of peer digests,
|
|
17
|
+
* route the query to peers most likely to have matching observations
|
|
18
|
+
*
|
|
19
|
+
* v2.17+ will wire these into a real gossip protocol. v2.16 ships the
|
|
20
|
+
* pure primitives, fully testable.
|
|
21
|
+
*/
|
|
22
|
+
import { createHash, createHmac } from "node:crypto";
|
|
23
|
+
const PROTOCOL_VERSION = 1;
|
|
24
|
+
export function buildMerkleSummary(host, observations) {
|
|
25
|
+
const ids = observations.map((o) => o.id).sort();
|
|
26
|
+
if (ids.length === 0)
|
|
27
|
+
return { v: PROTOCOL_VERSION, host, total: 0, root: createHash("sha256").update("").digest("hex"), leafIds: [] };
|
|
28
|
+
// Layered hashing
|
|
29
|
+
let layer = ids.map((id) => createHash("sha256").update(id).digest("hex"));
|
|
30
|
+
while (layer.length > 1) {
|
|
31
|
+
const next = [];
|
|
32
|
+
for (let i = 0; i < layer.length; i += 2) {
|
|
33
|
+
const a = layer[i];
|
|
34
|
+
const b = layer[i + 1] ?? a;
|
|
35
|
+
next.push(createHash("sha256").update(a + b).digest("hex"));
|
|
36
|
+
}
|
|
37
|
+
layer = next;
|
|
38
|
+
}
|
|
39
|
+
return { v: PROTOCOL_VERSION, host, total: ids.length, root: layer[0], leafIds: ids };
|
|
40
|
+
}
|
|
41
|
+
/** Diff two summaries: which ids does the LOCAL host need to request from
|
|
42
|
+
* the PEER? Pure set difference on leafIds. */
|
|
43
|
+
export function diffSummaries(local, peer) {
|
|
44
|
+
const localSet = new Set(local.leafIds);
|
|
45
|
+
const peerSet = new Set(peer.leafIds);
|
|
46
|
+
const toFetch = peer.leafIds.filter((id) => !localSet.has(id));
|
|
47
|
+
const toSend = local.leafIds.filter((id) => !peerSet.has(id));
|
|
48
|
+
return { toFetch, toSend, rootsMatch: local.root === peer.root };
|
|
49
|
+
}
|
|
50
|
+
function corr(xs, ys) {
|
|
51
|
+
if (xs.length !== ys.length || xs.length < 3)
|
|
52
|
+
return null;
|
|
53
|
+
const meanX = xs.reduce((a, b) => a + b, 0) / xs.length;
|
|
54
|
+
const meanY = ys.reduce((a, b) => a + b, 0) / ys.length;
|
|
55
|
+
let num = 0, dx2 = 0, dy2 = 0;
|
|
56
|
+
for (let i = 0; i < xs.length; i++) {
|
|
57
|
+
const dx = xs[i] - meanX, dy = ys[i] - meanY;
|
|
58
|
+
num += dx * dy;
|
|
59
|
+
dx2 += dx * dx;
|
|
60
|
+
dy2 += dy * dy;
|
|
61
|
+
}
|
|
62
|
+
const denom = Math.sqrt(dx2 * dy2);
|
|
63
|
+
return denom === 0 ? 0 : num / denom;
|
|
64
|
+
}
|
|
65
|
+
export function inferCausal(pairs, opts = {}) {
|
|
66
|
+
if (pairs.length === 0) {
|
|
67
|
+
return { cause: "", effect: "", correlation: null, meanLeadSeconds: null, directionalityVote: 0, samples: 0, sig: "" };
|
|
68
|
+
}
|
|
69
|
+
const cause = pairs[0].cause;
|
|
70
|
+
const effect = pairs[0].effect;
|
|
71
|
+
// Group pairs into adjacent cause/effect occurrences (within same minute
|
|
72
|
+
// window). Compute time deltas.
|
|
73
|
+
const sorted = pairs.slice().sort((a, b) => a.ts.localeCompare(b.ts));
|
|
74
|
+
const causeTimes = [];
|
|
75
|
+
const effectTimes = [];
|
|
76
|
+
const causeVals = [];
|
|
77
|
+
const effectVals = [];
|
|
78
|
+
for (const p of sorted) {
|
|
79
|
+
const t = new Date(p.ts).getTime();
|
|
80
|
+
if (!Number.isFinite(t))
|
|
81
|
+
continue;
|
|
82
|
+
if (p.cause === cause) {
|
|
83
|
+
causeTimes.push(t);
|
|
84
|
+
if (p.value !== undefined)
|
|
85
|
+
causeVals.push(p.value);
|
|
86
|
+
}
|
|
87
|
+
if (p.effect === effect) {
|
|
88
|
+
effectTimes.push(t);
|
|
89
|
+
if (p.value !== undefined)
|
|
90
|
+
effectVals.push(p.value);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Lead time: for each effect, find the most recent cause within 1h.
|
|
94
|
+
// Average the (effect - cause) deltas.
|
|
95
|
+
const leads = [];
|
|
96
|
+
let forwardCount = 0, reverseCount = 0;
|
|
97
|
+
for (const eT of effectTimes) {
|
|
98
|
+
let best = -Infinity;
|
|
99
|
+
for (const cT of causeTimes) {
|
|
100
|
+
if (cT <= eT && eT - cT <= 3600 * 1000)
|
|
101
|
+
best = Math.max(best, cT);
|
|
102
|
+
}
|
|
103
|
+
if (best > -Infinity) {
|
|
104
|
+
const delta = (eT - best) / 1000;
|
|
105
|
+
leads.push(delta);
|
|
106
|
+
// Same-instant observations are ambiguous (delta === 0) — don't
|
|
107
|
+
// count toward either direction. Only count strictly-ordered pairs.
|
|
108
|
+
if (delta > 0)
|
|
109
|
+
forwardCount++;
|
|
110
|
+
else if (delta < 0)
|
|
111
|
+
reverseCount++;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const meanLead = leads.length > 0 ? leads.reduce((a, b) => a + b, 0) / leads.length : null;
|
|
115
|
+
const totalVotes = forwardCount + reverseCount;
|
|
116
|
+
const directionalityVote = totalVotes === 0 ? 0.5 : forwardCount / totalVotes;
|
|
117
|
+
// Correlation when both series have aligned values
|
|
118
|
+
const minLen = Math.min(causeVals.length, effectVals.length);
|
|
119
|
+
const correlation = minLen >= 3 ? corr(causeVals.slice(0, minLen), effectVals.slice(0, minLen)) : null;
|
|
120
|
+
const body = { cause, effect, correlation, meanLeadSeconds: meanLead, directionalityVote, samples: pairs.length };
|
|
121
|
+
const sig = createHmac("sha256", opts.secret ?? "mneme-living-model-default").update(JSON.stringify(body)).digest("hex");
|
|
122
|
+
return { ...body, sig };
|
|
123
|
+
}
|
|
124
|
+
export function routeFederatedQuery(input) {
|
|
125
|
+
const max = input.maxPeers ?? 3;
|
|
126
|
+
const subjectLower = input.subject.toLowerCase();
|
|
127
|
+
const kindLower = (input.kind ?? "").toLowerCase();
|
|
128
|
+
const scored = input.peers.map((p) => {
|
|
129
|
+
let score = 0;
|
|
130
|
+
const reasons = [];
|
|
131
|
+
if (p.knownSubjects?.some((s) => s.toLowerCase().includes(subjectLower) || subjectLower.includes(s.toLowerCase()))) {
|
|
132
|
+
score += 0.6;
|
|
133
|
+
reasons.push(`subject match`);
|
|
134
|
+
}
|
|
135
|
+
if (kindLower && p.knownKinds?.some((k) => k.toLowerCase() === kindLower)) {
|
|
136
|
+
score += 0.4;
|
|
137
|
+
reasons.push(`kind match`);
|
|
138
|
+
}
|
|
139
|
+
if (score === 0)
|
|
140
|
+
reasons.push("no specific hint");
|
|
141
|
+
return { peer: p.host, score: Math.round(score * 100) / 100, reason: reasons.join(" + ") };
|
|
142
|
+
}).sort((a, b) => b.score - a.score).slice(0, max);
|
|
143
|
+
const topScore = scored[0]?.score ?? 0;
|
|
144
|
+
return {
|
|
145
|
+
recommendations: scored,
|
|
146
|
+
fallback: topScore >= 0.6 ? "narrow" : "broadcast",
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export function formatLivingModelLine(s) {
|
|
150
|
+
return `LIVING · ${s.host} · ${s.total} obs · root=${s.root.slice(0, 12)}`;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/living_model/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"living_model.test.d.ts","sourceRoot":"","sources":["../../src/living_model/living_model.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { buildMerkleSummary, diffSummaries, inferCausal, routeFederatedQuery, formatLivingModelLine } from "./index.js";
|
|
3
|
+
describe("v2.16 · LIVING MODEL primitives", () => {
|
|
4
|
+
describe("Merkle anti-entropy", () => {
|
|
5
|
+
it("empty observation list still produces a root", () => {
|
|
6
|
+
const s = buildMerkleSummary("host-a", []);
|
|
7
|
+
expect(s.root).toMatch(/^[0-9a-f]{64}$/);
|
|
8
|
+
expect(s.total).toBe(0);
|
|
9
|
+
});
|
|
10
|
+
it("identical observation sets produce identical roots", () => {
|
|
11
|
+
const obs = [{ id: "o1", ts: "x", host: "a", kind: "k", subject: "s" }];
|
|
12
|
+
const a = buildMerkleSummary("h", obs);
|
|
13
|
+
const b = buildMerkleSummary("h", obs);
|
|
14
|
+
expect(a.root).toBe(b.root);
|
|
15
|
+
});
|
|
16
|
+
it("diff finds missing ids both ways", () => {
|
|
17
|
+
const a = buildMerkleSummary("a", [
|
|
18
|
+
{ id: "1", ts: "t", host: "a", kind: "k", subject: "s" },
|
|
19
|
+
{ id: "2", ts: "t", host: "a", kind: "k", subject: "s" },
|
|
20
|
+
]);
|
|
21
|
+
const b = buildMerkleSummary("b", [
|
|
22
|
+
{ id: "2", ts: "t", host: "b", kind: "k", subject: "s" },
|
|
23
|
+
{ id: "3", ts: "t", host: "b", kind: "k", subject: "s" },
|
|
24
|
+
]);
|
|
25
|
+
const d = diffSummaries(a, b);
|
|
26
|
+
expect(d.toFetch).toEqual(["3"]);
|
|
27
|
+
expect(d.toSend).toEqual(["1"]);
|
|
28
|
+
expect(d.rootsMatch).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
it("identical sets → rootsMatch true + no ids to fetch", () => {
|
|
31
|
+
const obs = [{ id: "x", ts: "t", host: "a", kind: "k", subject: "s" }];
|
|
32
|
+
const a = buildMerkleSummary("a", obs);
|
|
33
|
+
const b = buildMerkleSummary("b", obs);
|
|
34
|
+
const d = diffSummaries(a, b);
|
|
35
|
+
expect(d.rootsMatch).toBe(true);
|
|
36
|
+
expect(d.toFetch).toEqual([]);
|
|
37
|
+
expect(d.toSend).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
describe("Causal inference", () => {
|
|
41
|
+
it("empty input → zero samples", () => {
|
|
42
|
+
const r = inferCausal([]);
|
|
43
|
+
expect(r.samples).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
it("cause precedes effect → positive directionalityVote + positive mean lead", () => {
|
|
46
|
+
// Pattern: every day, a deploy at 10:00 → an error_spike 5 min later
|
|
47
|
+
const pairs = [];
|
|
48
|
+
for (let i = 0; i < 5; i++) {
|
|
49
|
+
const dayMs = i * 24 * 60 * 60 * 1000;
|
|
50
|
+
const t0 = 1700000000000 + dayMs;
|
|
51
|
+
// Deploy event (no effect bound yet)
|
|
52
|
+
pairs.push({ ts: new Date(t0).toISOString(), cause: "deploy", effect: "X" });
|
|
53
|
+
// Error spike 5 min after deploy
|
|
54
|
+
pairs.push({ ts: new Date(t0 + 5 * 60 * 1000).toISOString(), cause: "Y", effect: "error_spike" });
|
|
55
|
+
}
|
|
56
|
+
// The first pair sets cause="deploy" effect="X". We want to test
|
|
57
|
+
// deploy→error_spike, so re-orient: pass cause-only and effect-only
|
|
58
|
+
// observations.
|
|
59
|
+
const r = inferCausal([
|
|
60
|
+
{ ts: new Date(1700000000000).toISOString(), cause: "deploy", effect: "error_spike" }, // anchor
|
|
61
|
+
...pairs.slice(1).map((p, i) => ({
|
|
62
|
+
...p,
|
|
63
|
+
cause: p.cause === "deploy" ? "deploy" : "noop",
|
|
64
|
+
effect: p.effect === "error_spike" ? "error_spike" : "noop",
|
|
65
|
+
})),
|
|
66
|
+
]);
|
|
67
|
+
expect(r.directionalityVote).toBeGreaterThan(0.5);
|
|
68
|
+
expect(r.meanLeadSeconds).toBeGreaterThan(0);
|
|
69
|
+
});
|
|
70
|
+
it("includes correlation when numeric values supplied", () => {
|
|
71
|
+
const pairs = [];
|
|
72
|
+
for (let i = 0; i < 5; i++) {
|
|
73
|
+
pairs.push({ ts: new Date(1700000000000 + i * 60 * 1000).toISOString(), cause: "load", effect: "latency", value: i + 1 });
|
|
74
|
+
pairs.push({ ts: new Date(1700000000000 + i * 60 * 1000 + 30 * 1000).toISOString(), cause: "load", effect: "latency", value: (i + 1) * 2 });
|
|
75
|
+
}
|
|
76
|
+
const r = inferCausal(pairs);
|
|
77
|
+
expect(r.correlation).not.toBeNull();
|
|
78
|
+
});
|
|
79
|
+
it("HMAC sig present on non-empty result", () => {
|
|
80
|
+
const r = inferCausal([{ ts: "2026-01-01T00:00:00Z", cause: "a", effect: "b" }]);
|
|
81
|
+
expect(r.sig).toMatch(/^[0-9a-f]{64}$/);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe("Federated query routing", () => {
|
|
85
|
+
it("routes by subject match", () => {
|
|
86
|
+
const r = routeFederatedQuery({
|
|
87
|
+
subject: "auth-service",
|
|
88
|
+
peers: [
|
|
89
|
+
{ host: "h1", knownSubjects: ["auth-service", "billing"] },
|
|
90
|
+
{ host: "h2", knownSubjects: ["frontend"] },
|
|
91
|
+
{ host: "h3", knownSubjects: ["payments"] },
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
expect(r.recommendations[0].peer).toBe("h1");
|
|
95
|
+
expect(r.fallback).toBe("narrow");
|
|
96
|
+
});
|
|
97
|
+
it("falls back to broadcast when nobody matches", () => {
|
|
98
|
+
const r = routeFederatedQuery({
|
|
99
|
+
subject: "unknown-service",
|
|
100
|
+
peers: [
|
|
101
|
+
{ host: "h1", knownSubjects: ["billing"] },
|
|
102
|
+
{ host: "h2", knownSubjects: ["frontend"] },
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
expect(r.fallback).toBe("broadcast");
|
|
106
|
+
});
|
|
107
|
+
it("kind match contributes to score", () => {
|
|
108
|
+
const r = routeFederatedQuery({
|
|
109
|
+
subject: "auth",
|
|
110
|
+
kind: "error_spike",
|
|
111
|
+
peers: [
|
|
112
|
+
{ host: "with-kind", knownSubjects: ["auth"], knownKinds: ["error_spike"] },
|
|
113
|
+
{ host: "without-kind", knownSubjects: ["auth"] },
|
|
114
|
+
],
|
|
115
|
+
});
|
|
116
|
+
expect(r.recommendations[0].peer).toBe("with-kind");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
it("formatLivingModelLine summarises", () => {
|
|
120
|
+
const s = buildMerkleSummary("h1", [{ id: "x", ts: "t", host: "h1", kind: "k", subject: "s" }]);
|
|
121
|
+
expect(formatLivingModelLine(s)).toContain("LIVING");
|
|
122
|
+
expect(formatLivingModelLine(s)).toContain("h1");
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=living_model.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"living_model.test.js","sourceRoot":"","sources":["../../src/living_model/living_model.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.16.0 — MNEME OBELISK (Federated Trust Graph)
|
|
3
|
+
*
|
|
4
|
+
* "BOUNTY today is local — one repo, one ledger. OBELISK promotes
|
|
5
|
+
* it to a federated trust standard: any signed BOUNTY card from any
|
|
6
|
+
* repo (or any vendor's own self-published score) can be aggregated
|
|
7
|
+
* into the global AI Trust Graph."
|
|
8
|
+
*
|
|
9
|
+
* Phase 1 (v2.16) primitives:
|
|
10
|
+
*
|
|
11
|
+
* 1. obeliskCard — wrap a local BOUNTY VendorScorecard with publisher
|
|
12
|
+
* identity + ed25519-like signature (using HMAC for now; ed25519
|
|
13
|
+
* requires a webcrypto path we'll add in v2.17)
|
|
14
|
+
* 2. aggregateGraph — given N cards from N publishers, weight + fuse
|
|
15
|
+
* into a per-vendor consensus falseRate with confidence based on
|
|
16
|
+
* total sample count and publisher diversity
|
|
17
|
+
* 3. verifyCard — independent verification of a single card
|
|
18
|
+
*
|
|
19
|
+
* Privacy: publisher identity is whatever string they pick. No PII
|
|
20
|
+
* required. Could be a GitHub handle, a pseudonym, or org name.
|
|
21
|
+
*
|
|
22
|
+
* Trust model: each card stands on its own signature. Aggregation
|
|
23
|
+
* weights samples by Wilson lower bound so a single publisher with 5
|
|
24
|
+
* verdicts doesn't outvote 10 publishers with 1000 verdicts.
|
|
25
|
+
*/
|
|
26
|
+
declare const PROTOCOL_VERSION: 1;
|
|
27
|
+
export interface VendorScorecard {
|
|
28
|
+
vendor: string;
|
|
29
|
+
totalVerdicts: number;
|
|
30
|
+
falseCount: number;
|
|
31
|
+
trueCount: number;
|
|
32
|
+
partialCount: number;
|
|
33
|
+
inconclusiveCount: number;
|
|
34
|
+
falseRate: number;
|
|
35
|
+
falseRateLB: number;
|
|
36
|
+
generatedAt: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ObeliskCard {
|
|
39
|
+
v: typeof PROTOCOL_VERSION;
|
|
40
|
+
publisher: string;
|
|
41
|
+
publisherUrl?: string;
|
|
42
|
+
publishedAt: string;
|
|
43
|
+
vendorScore: VendorScorecard;
|
|
44
|
+
sig: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function buildCard(input: {
|
|
47
|
+
publisher: string;
|
|
48
|
+
publisherUrl?: string;
|
|
49
|
+
vendorScore: VendorScorecard;
|
|
50
|
+
secret?: string;
|
|
51
|
+
}): ObeliskCard;
|
|
52
|
+
export declare function verifyCard(card: ObeliskCard, secret?: string): {
|
|
53
|
+
ok: boolean;
|
|
54
|
+
reason?: string;
|
|
55
|
+
};
|
|
56
|
+
export interface AggregatedRow {
|
|
57
|
+
vendor: string;
|
|
58
|
+
totalVerdicts: number;
|
|
59
|
+
totalFalse: number;
|
|
60
|
+
consensusFalseRate: number;
|
|
61
|
+
/** Wilson LB on aggregated counts. */
|
|
62
|
+
consensusFalseRateLB: number;
|
|
63
|
+
publisherCount: number;
|
|
64
|
+
publishers: string[];
|
|
65
|
+
/** Higher = stronger signal. Logit-based using totalVerdicts. */
|
|
66
|
+
confidenceScore: number;
|
|
67
|
+
}
|
|
68
|
+
export declare function aggregateGraph(cards: ObeliskCard[], secret?: string): {
|
|
69
|
+
rows: AggregatedRow[];
|
|
70
|
+
unverified: ObeliskCard[];
|
|
71
|
+
};
|
|
72
|
+
export declare function formatObeliskLine(rows: AggregatedRow[]): string;
|
|
73
|
+
export {};
|
|
74
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/obelisk/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,QAAA,MAAM,gBAAgB,EAAG,CAAU,CAAC;AAEpC,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,OAAO,gBAAgB,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,eAAe,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;CACb;AAaD,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,eAAe,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,CAWzI;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAO/F;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sCAAsC;IACtC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;CACzB;AAWD,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG;IACrE,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,UAAU,EAAE,WAAW,EAAE,CAAC;CAC3B,CAqCA;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,GAAG,MAAM,CAI/D"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.16.0 — MNEME OBELISK (Federated Trust Graph)
|
|
3
|
+
*
|
|
4
|
+
* "BOUNTY today is local — one repo, one ledger. OBELISK promotes
|
|
5
|
+
* it to a federated trust standard: any signed BOUNTY card from any
|
|
6
|
+
* repo (or any vendor's own self-published score) can be aggregated
|
|
7
|
+
* into the global AI Trust Graph."
|
|
8
|
+
*
|
|
9
|
+
* Phase 1 (v2.16) primitives:
|
|
10
|
+
*
|
|
11
|
+
* 1. obeliskCard — wrap a local BOUNTY VendorScorecard with publisher
|
|
12
|
+
* identity + ed25519-like signature (using HMAC for now; ed25519
|
|
13
|
+
* requires a webcrypto path we'll add in v2.17)
|
|
14
|
+
* 2. aggregateGraph — given N cards from N publishers, weight + fuse
|
|
15
|
+
* into a per-vendor consensus falseRate with confidence based on
|
|
16
|
+
* total sample count and publisher diversity
|
|
17
|
+
* 3. verifyCard — independent verification of a single card
|
|
18
|
+
*
|
|
19
|
+
* Privacy: publisher identity is whatever string they pick. No PII
|
|
20
|
+
* required. Could be a GitHub handle, a pseudonym, or org name.
|
|
21
|
+
*
|
|
22
|
+
* Trust model: each card stands on its own signature. Aggregation
|
|
23
|
+
* weights samples by Wilson lower bound so a single publisher with 5
|
|
24
|
+
* verdicts doesn't outvote 10 publishers with 1000 verdicts.
|
|
25
|
+
*/
|
|
26
|
+
import { createHmac, timingSafeEqual } from "node:crypto";
|
|
27
|
+
const PROTOCOL_VERSION = 1;
|
|
28
|
+
function canon(v) {
|
|
29
|
+
if (v === null || typeof v !== "object")
|
|
30
|
+
return JSON.stringify(v);
|
|
31
|
+
if (Array.isArray(v))
|
|
32
|
+
return "[" + v.map(canon).join(",") + "]";
|
|
33
|
+
const keys = Object.keys(v).sort();
|
|
34
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canon(v[k])).join(",") + "}";
|
|
35
|
+
}
|
|
36
|
+
function defaultSecret() {
|
|
37
|
+
return process.env["MNEME_OBELISK_SECRET"] || `mneme-obelisk-v${PROTOCOL_VERSION}`;
|
|
38
|
+
}
|
|
39
|
+
export function buildCard(input) {
|
|
40
|
+
const publishedAt = new Date().toISOString();
|
|
41
|
+
const body = {
|
|
42
|
+
v: PROTOCOL_VERSION,
|
|
43
|
+
publisher: input.publisher,
|
|
44
|
+
...(input.publisherUrl ? { publisherUrl: input.publisherUrl } : {}),
|
|
45
|
+
publishedAt,
|
|
46
|
+
vendorScore: input.vendorScore,
|
|
47
|
+
};
|
|
48
|
+
const sig = createHmac("sha256", input.secret ?? defaultSecret()).update(canon(body)).digest("hex");
|
|
49
|
+
return { ...body, sig };
|
|
50
|
+
}
|
|
51
|
+
export function verifyCard(card, secret) {
|
|
52
|
+
const { sig: claimed, ...body } = card;
|
|
53
|
+
const expected = createHmac("sha256", secret ?? defaultSecret()).update(canon(body)).digest("hex");
|
|
54
|
+
try {
|
|
55
|
+
const ok = timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(claimed, "hex"));
|
|
56
|
+
return ok ? { ok: true } : { ok: false, reason: "card sig mismatch -- forged or wrong publisher key" };
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { ok: false, reason: "card sig length invalid" };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function wilsonLB(positive, total, z = 1.96) {
|
|
63
|
+
if (total === 0)
|
|
64
|
+
return 0;
|
|
65
|
+
const p = positive / total;
|
|
66
|
+
const denom = 1 + (z * z) / total;
|
|
67
|
+
const center = p + (z * z) / (2 * total);
|
|
68
|
+
const margin = z * Math.sqrt((p * (1 - p) + (z * z) / (4 * total)) / total);
|
|
69
|
+
return Math.max(0, Math.min(1, (center - margin) / denom));
|
|
70
|
+
}
|
|
71
|
+
export function aggregateGraph(cards, secret) {
|
|
72
|
+
const verified = [];
|
|
73
|
+
const unverified = [];
|
|
74
|
+
for (const c of cards) {
|
|
75
|
+
if (verifyCard(c, secret).ok)
|
|
76
|
+
verified.push(c);
|
|
77
|
+
else
|
|
78
|
+
unverified.push(c);
|
|
79
|
+
}
|
|
80
|
+
const byVendor = new Map();
|
|
81
|
+
for (const c of verified) {
|
|
82
|
+
const v = c.vendorScore;
|
|
83
|
+
const e = byVendor.get(v.vendor) ?? { totalVerdicts: 0, totalFalse: 0, publishers: new Set() };
|
|
84
|
+
e.totalVerdicts += v.totalVerdicts;
|
|
85
|
+
e.totalFalse += v.falseCount;
|
|
86
|
+
e.publishers.add(c.publisher);
|
|
87
|
+
byVendor.set(v.vendor, e);
|
|
88
|
+
}
|
|
89
|
+
const rows = Array.from(byVendor.entries()).map(([vendor, e]) => {
|
|
90
|
+
const consensusFalseRate = e.totalVerdicts === 0 ? 0 : e.totalFalse / e.totalVerdicts;
|
|
91
|
+
const consensusFalseRateLB = wilsonLB(e.totalFalse, e.totalVerdicts);
|
|
92
|
+
// Confidence: log(totalVerdicts) capped + bonus for >=3 distinct publishers
|
|
93
|
+
const sampleConf = Math.min(1, Math.log10(e.totalVerdicts + 1) / 3);
|
|
94
|
+
const diversityBonus = e.publishers.size >= 3 ? 0.2 : e.publishers.size >= 2 ? 0.1 : 0;
|
|
95
|
+
return {
|
|
96
|
+
vendor,
|
|
97
|
+
totalVerdicts: e.totalVerdicts,
|
|
98
|
+
totalFalse: e.totalFalse,
|
|
99
|
+
consensusFalseRate: Math.round(consensusFalseRate * 10000) / 10000,
|
|
100
|
+
consensusFalseRateLB: Math.round(consensusFalseRateLB * 10000) / 10000,
|
|
101
|
+
publisherCount: e.publishers.size,
|
|
102
|
+
publishers: Array.from(e.publishers).sort(),
|
|
103
|
+
confidenceScore: Math.round((sampleConf + diversityBonus) * 1000) / 1000,
|
|
104
|
+
};
|
|
105
|
+
}).sort((a, b) => b.consensusFalseRateLB - a.consensusFalseRateLB);
|
|
106
|
+
return { rows, unverified };
|
|
107
|
+
}
|
|
108
|
+
export function formatObeliskLine(rows) {
|
|
109
|
+
if (rows.length === 0)
|
|
110
|
+
return "OBELISK · empty graph";
|
|
111
|
+
const worst = rows[0];
|
|
112
|
+
return `OBELISK · ${rows.length} vendors · worst=${worst.vendor} (lb=${worst.consensusFalseRateLB.toFixed(3)} across ${worst.publisherCount} publishers)`;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/obelisk/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obelisk.test.d.ts","sourceRoot":"","sources":["../../src/obelisk/obelisk.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { buildCard, verifyCard, aggregateGraph, formatObeliskLine } from "./index.js";
|
|
3
|
+
const score = (vendor, t, f) => ({
|
|
4
|
+
vendor, totalVerdicts: t + f, falseCount: f, trueCount: t,
|
|
5
|
+
partialCount: 0, inconclusiveCount: 0,
|
|
6
|
+
falseRate: f / Math.max(1, t + f),
|
|
7
|
+
falseRateLB: 0,
|
|
8
|
+
generatedAt: new Date().toISOString(),
|
|
9
|
+
});
|
|
10
|
+
describe("v2.16 · OBELISK (federated trust graph)", () => {
|
|
11
|
+
it("buildCard + verifyCard round-trip", () => {
|
|
12
|
+
const card = buildCard({ publisher: "alice@x.com", vendorScore: score("claude", 90, 10) });
|
|
13
|
+
expect(verifyCard(card).ok).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
it("verifyCard fails on tamper", () => {
|
|
16
|
+
const card = buildCard({ publisher: "alice@x.com", vendorScore: score("claude", 90, 10) });
|
|
17
|
+
const tampered = { ...card, publisher: "evil@attacker.com" };
|
|
18
|
+
expect(verifyCard(tampered).ok).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
it("aggregateGraph sums verdicts across publishers", () => {
|
|
21
|
+
const a = buildCard({ publisher: "alice", vendorScore: score("claude", 80, 20) });
|
|
22
|
+
const b = buildCard({ publisher: "bob", vendorScore: score("claude", 90, 10) });
|
|
23
|
+
const c = buildCard({ publisher: "carol", vendorScore: score("claude", 95, 5) });
|
|
24
|
+
const { rows } = aggregateGraph([a, b, c]);
|
|
25
|
+
expect(rows.length).toBe(1);
|
|
26
|
+
expect(rows[0].totalVerdicts).toBe(300);
|
|
27
|
+
expect(rows[0].totalFalse).toBe(35);
|
|
28
|
+
expect(rows[0].publisherCount).toBe(3);
|
|
29
|
+
expect(rows[0].consensusFalseRate).toBeCloseTo(35 / 300, 4);
|
|
30
|
+
});
|
|
31
|
+
it("aggregateGraph isolates unverified cards", () => {
|
|
32
|
+
const a = buildCard({ publisher: "alice", vendorScore: score("claude", 80, 20) });
|
|
33
|
+
const tampered = { ...a, publisher: "evil" };
|
|
34
|
+
const { rows, unverified } = aggregateGraph([a, tampered]);
|
|
35
|
+
expect(unverified).toHaveLength(1);
|
|
36
|
+
expect(rows[0].publisherCount).toBe(1);
|
|
37
|
+
});
|
|
38
|
+
it("Wilson LB <= rate for small samples", () => {
|
|
39
|
+
const a = buildCard({ publisher: "alice", vendorScore: score("claude", 0, 5) }); // 100% false rate, tiny sample
|
|
40
|
+
const { rows } = aggregateGraph([a]);
|
|
41
|
+
expect(rows[0].consensusFalseRate).toBe(1);
|
|
42
|
+
expect(rows[0].consensusFalseRateLB).toBeLessThan(1);
|
|
43
|
+
});
|
|
44
|
+
it("diversity bonus: more publishers → higher confidence", () => {
|
|
45
|
+
const a1 = buildCard({ publisher: "alice", vendorScore: score("claude", 50, 50) });
|
|
46
|
+
const a2 = buildCard({ publisher: "alice", vendorScore: score("claude", 50, 50) });
|
|
47
|
+
const a3 = buildCard({ publisher: "alice", vendorScore: score("claude", 50, 50) });
|
|
48
|
+
const b1 = buildCard({ publisher: "bob", vendorScore: score("claude", 50, 50) });
|
|
49
|
+
const c1 = buildCard({ publisher: "carol", vendorScore: score("claude", 50, 50) });
|
|
50
|
+
const single = aggregateGraph([a1, a2, a3]);
|
|
51
|
+
const diverse = aggregateGraph([a1, b1, c1]);
|
|
52
|
+
expect(diverse.rows[0].confidenceScore).toBeGreaterThanOrEqual(single.rows[0].confidenceScore);
|
|
53
|
+
});
|
|
54
|
+
it("sort orders worst (highest falseRateLB) first", () => {
|
|
55
|
+
const aBad = buildCard({ publisher: "p1", vendorScore: score("vendorA", 10, 90) });
|
|
56
|
+
const aGood = buildCard({ publisher: "p2", vendorScore: score("vendorB", 90, 10) });
|
|
57
|
+
const { rows } = aggregateGraph([aBad, aGood]);
|
|
58
|
+
expect(rows[0].vendor).toBe("vendorA");
|
|
59
|
+
});
|
|
60
|
+
it("formatObeliskLine summarises", () => {
|
|
61
|
+
const card = buildCard({ publisher: "alice", vendorScore: score("claude", 90, 10) });
|
|
62
|
+
const { rows } = aggregateGraph([card]);
|
|
63
|
+
expect(formatObeliskLine(rows)).toContain("OBELISK");
|
|
64
|
+
expect(formatObeliskLine([])).toContain("empty");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
//# sourceMappingURL=obelisk.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"obelisk.test.js","sourceRoot":"","sources":["../../src/obelisk/obelisk.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,iBAAiB,EAAwB,MAAM,YAAY,CAAC;AAE5G,MAAM,KAAK,GAAG,CAAC,MAAc,EAAE,CAAS,EAAE,CAAS,EAAmB,EAAE,CAAC,CAAC;IACxE,MAAM,EAAE,aAAa,EAAE,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC;IACzD,YAAY,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC;IACrC,SAAS,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACjC,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;CACtC,CAAC,CAAC;AAEH,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACvD,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,MAAM,QAAQ,GAAG,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC;QAC7D,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,KAAK,EAAI,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QACjF,MAAM,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,kBAAkB,CAAC,CAAC,WAAW,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,QAAQ,GAAG,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QAC7C,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,+BAA+B;QAChH,MAAM,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,oBAAoB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,KAAK,EAAI,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,MAAM,EAAE,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,MAAM,MAAM,GAAG,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAC5C,MAAM,OAAO,GAAG,cAAc,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,eAAe,CAAC,CAAC,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,eAAe,CAAC,CAAC;IACnG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACnF,MAAM,KAAK,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,CAAC,SAAS,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACpF,MAAM,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,SAAS,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QACrF,MAAM,EAAE,IAAI,EAAE,GAAG,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACrD,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|