@mneme-ai/core 2.0.0 → 2.2.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/adversarial_twins/index.d.ts +52 -0
- package/dist/adversarial_twins/index.d.ts.map +1 -0
- package/dist/adversarial_twins/index.js +58 -0
- package/dist/adversarial_twins/index.js.map +1 -0
- package/dist/adversarial_twins/twins.test.d.ts +2 -0
- package/dist/adversarial_twins/twins.test.d.ts.map +1 -0
- package/dist/adversarial_twins/twins.test.js +66 -0
- package/dist/adversarial_twins/twins.test.js.map +1 -0
- package/dist/gladiator/gladiator.test.d.ts +2 -0
- package/dist/gladiator/gladiator.test.d.ts.map +1 -0
- package/dist/gladiator/gladiator.test.js +145 -0
- package/dist/gladiator/gladiator.test.js.map +1 -0
- package/dist/gladiator/index.d.ts +181 -0
- package/dist/gladiator/index.d.ts.map +1 -0
- package/dist/gladiator/index.js +192 -0
- package/dist/gladiator/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/interstellar/index.d.ts +77 -0
- package/dist/interstellar/index.d.ts.map +1 -0
- package/dist/interstellar/index.js +84 -0
- package/dist/interstellar/index.js.map +1 -0
- package/dist/interstellar/interstellar.test.d.ts +2 -0
- package/dist/interstellar/interstellar.test.d.ts.map +1 -0
- package/dist/interstellar/interstellar.test.js +69 -0
- package/dist/interstellar/interstellar.test.js.map +1 -0
- package/dist/living_will/index.d.ts +60 -0
- package/dist/living_will/index.d.ts.map +1 -0
- package/dist/living_will/index.js +66 -0
- package/dist/living_will/index.js.map +1 -0
- package/dist/living_will/living_will.test.d.ts +2 -0
- package/dist/living_will/living_will.test.d.ts.map +1 -0
- package/dist/living_will/living_will.test.js +74 -0
- package/dist/living_will/living_will.test.js.map +1 -0
- package/dist/necromancy/index.d.ts +52 -0
- package/dist/necromancy/index.d.ts.map +1 -0
- package/dist/necromancy/index.js +126 -0
- package/dist/necromancy/index.js.map +1 -0
- package/dist/necromancy/necromancy.test.d.ts +2 -0
- package/dist/necromancy/necromancy.test.d.ts.map +1 -0
- package/dist/necromancy/necromancy.test.js +50 -0
- package/dist/necromancy/necromancy.test.js.map +1 -0
- package/dist/prophet/index.d.ts +47 -0
- package/dist/prophet/index.d.ts.map +1 -0
- package/dist/prophet/index.js +46 -0
- package/dist/prophet/index.js.map +1 -0
- package/dist/prophet/prophet.test.d.ts +2 -0
- package/dist/prophet/prophet.test.d.ts.map +1 -0
- package/dist/prophet/prophet.test.js +37 -0
- package/dist/prophet/prophet.test.js.map +1 -0
- package/dist/tool_selector/index.d.ts +86 -0
- package/dist/tool_selector/index.d.ts.map +1 -0
- package/dist/tool_selector/index.js +188 -0
- package/dist/tool_selector/index.js.map +1 -0
- package/dist/tool_selector/tool_selector.test.d.ts +2 -0
- package/dist/tool_selector/tool_selector.test.d.ts.map +1 -0
- package/dist/tool_selector/tool_selector.test.js +59 -0
- package/dist/tool_selector/tool_selector.test.js.map +1 -0
- package/dist/wisdom_shards/index.d.ts +62 -0
- package/dist/wisdom_shards/index.d.ts.map +1 -0
- package/dist/wisdom_shards/index.js +69 -0
- package/dist/wisdom_shards/index.js.map +1 -0
- package/dist/wisdom_shards/wisdom_shards.test.d.ts +2 -0
- package/dist/wisdom_shards/wisdom_shards.test.d.ts.map +1 -0
- package/dist/wisdom_shards/wisdom_shards.test.js +69 -0
- package/dist/wisdom_shards/wisdom_shards.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { prophesyAndPrewarm, formatProphetPulseLine } from "./index.js";
|
|
3
|
+
describe("v2.1 PROPHET · pre-fetch top-K", () => {
|
|
4
|
+
it("predicts + prewarms tasks for matched classes", async () => {
|
|
5
|
+
const r = await prophesyAndPrewarm({
|
|
6
|
+
currentQuery: "is this rare?",
|
|
7
|
+
lastAiReply: "Cannot confirm rarity without auction history.",
|
|
8
|
+
hydrationMap: {
|
|
9
|
+
"rarity-followup": [
|
|
10
|
+
{ id: "fetch-auctions", work: async () => "auction-data-result" },
|
|
11
|
+
],
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
expect(r.prediction.predictions.length).toBeGreaterThan(0);
|
|
15
|
+
expect(r.prewarmed.length).toBeGreaterThan(0);
|
|
16
|
+
expect(r.prewarmed[0].ok).toBe(true);
|
|
17
|
+
expect(r.cache.get("rarity-followup")).toEqual(["auction-data-result"]);
|
|
18
|
+
});
|
|
19
|
+
it("records failed prewarm tasks", async () => {
|
|
20
|
+
const r = await prophesyAndPrewarm({
|
|
21
|
+
currentQuery: "x",
|
|
22
|
+
lastAiReply: "rarity collectible",
|
|
23
|
+
hydrationMap: { "rarity-followup": [{ id: "bad", work: async () => { throw new Error("boom"); } }] },
|
|
24
|
+
});
|
|
25
|
+
expect(r.prewarmed[0].ok).toBe(false);
|
|
26
|
+
expect(r.prewarmed[0].error).toContain("boom");
|
|
27
|
+
});
|
|
28
|
+
it("returns empty prewarmed when reply has no triggers", async () => {
|
|
29
|
+
const r = await prophesyAndPrewarm({ currentQuery: "x", lastAiReply: "hi" });
|
|
30
|
+
expect(r.prewarmed.length).toBe(0);
|
|
31
|
+
});
|
|
32
|
+
it("formatProphetPulseLine summarises", async () => {
|
|
33
|
+
const r = await prophesyAndPrewarm({ currentQuery: "x", lastAiReply: "rare collectible" });
|
|
34
|
+
expect(formatProphetPulseLine(r)).toContain("PROPHET");
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=prophet.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prophet.test.js","sourceRoot":"","sources":["../../src/prophet/prophet.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAExE,QAAQ,CAAC,gCAAgC,EAAE,GAAG,EAAE;IAC9C,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,CAAC,GAAG,MAAM,kBAAkB,CAAC;YACjC,YAAY,EAAE,eAAe;YAC7B,WAAW,EAAE,gDAAgD;YAC7D,YAAY,EAAE;gBACZ,iBAAiB,EAAE;oBACjB,EAAE,EAAE,EAAE,gBAAgB,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,qBAAqB,EAAE;iBAClE;aACF;SACF,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;QAC5C,MAAM,CAAC,GAAG,MAAM,kBAAkB,CAAC;YACjC,YAAY,EAAE,GAAG;YACjB,WAAW,EAAE,oBAAoB;YACjC,YAAY,EAAE,EAAE,iBAAiB,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,GAAG,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE;SACrG,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;QAClE,MAAM,CAAC,GAAG,MAAM,kBAAkB,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,CAAC,GAAG,MAAM,kBAAkB,CAAC,EAAE,YAAY,EAAE,GAAG,EAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC3F,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.1.0 -- TOOL SELECTOR · smart intent → tool router (THE FIX for "AI picks wrong tool")
|
|
3
|
+
*
|
|
4
|
+
* User's exact worry: "Mneme ships 100+ tools. AI agents pick the wrong
|
|
5
|
+
* one, customer gets angry. How do we make sure the AI always picks
|
|
6
|
+
* RIGHT?"
|
|
7
|
+
*
|
|
8
|
+
* Vanilla AI: scans tool descriptions, picks one that "looks relevant."
|
|
9
|
+
* Probabilistic + opaque + flaky.
|
|
10
|
+
*
|
|
11
|
+
* TOOL SELECTOR: deterministic, auditable, fuzzy-Thai/English/mixed.
|
|
12
|
+
*
|
|
13
|
+
* selectTool({userIntent: "ส่งสมองไปมือถือ", catalog, ctx})
|
|
14
|
+
* → ranked candidates with confidence + reasoning trace
|
|
15
|
+
* → COMMIT (≥0.75): call it
|
|
16
|
+
* → CONFIRM (0.40-0.75): ask user to confirm
|
|
17
|
+
* → MENU (<0.40): show menu
|
|
18
|
+
*
|
|
19
|
+
* Pure function. Same input → same selection. The AI agent doesn't
|
|
20
|
+
* pick the tool — Mneme picks it FOR the AI based on math.
|
|
21
|
+
*/
|
|
22
|
+
export interface ToolEntry {
|
|
23
|
+
/** Tool name, e.g. "mneme.rainbow.show_local". */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Category for grouping: memory / people / audit / forensics / ... */
|
|
26
|
+
category: string;
|
|
27
|
+
/** One-line description for AI agents. */
|
|
28
|
+
description: string;
|
|
29
|
+
/** Trigger keywords / phrases (Thai/English/mixed). When user intent
|
|
30
|
+
* contains any of these, the tool wins votes. */
|
|
31
|
+
triggers: string[];
|
|
32
|
+
/** "Use this when..." rule that should match the user's intent semantically. */
|
|
33
|
+
whenToUse: string;
|
|
34
|
+
/** Example user phrases that should resolve to this tool. */
|
|
35
|
+
examples: string[];
|
|
36
|
+
/** Optional list of tool names that are alternatives / preferred companions. */
|
|
37
|
+
prefer?: string[];
|
|
38
|
+
/** Optional list of tool names that should NOT be paired. */
|
|
39
|
+
conflictsWith?: string[];
|
|
40
|
+
/** Prior probability when no signals match. Default 1/N. */
|
|
41
|
+
prior?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface SelectorContext {
|
|
44
|
+
/** Previous tool the AI agent called this session — boosts related tools. */
|
|
45
|
+
recentTools?: string[];
|
|
46
|
+
/** User's preferred output kind (text / file / qr / browser). */
|
|
47
|
+
outputPreference?: string;
|
|
48
|
+
/** Active features the user has opted into. */
|
|
49
|
+
enabledFeatures?: string[];
|
|
50
|
+
}
|
|
51
|
+
export interface ToolCandidate {
|
|
52
|
+
tool: ToolEntry;
|
|
53
|
+
/** Combined posterior 0..1. */
|
|
54
|
+
confidence: number;
|
|
55
|
+
/** Which triggers matched. */
|
|
56
|
+
matchedTriggers: string[];
|
|
57
|
+
/** Per-signal breakdown. */
|
|
58
|
+
signals: {
|
|
59
|
+
triggerMatch: number;
|
|
60
|
+
recencyBoost: number;
|
|
61
|
+
preferBoost: number;
|
|
62
|
+
conflictPenalty: number;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export interface SelectorResult {
|
|
66
|
+
/** Top candidate. Null when catalog is empty. */
|
|
67
|
+
top: ToolCandidate | null;
|
|
68
|
+
/** Ranked candidates, descending. */
|
|
69
|
+
ranked: ToolCandidate[];
|
|
70
|
+
/** Decision based on top confidence. */
|
|
71
|
+
verdict: "COMMIT" | "CONFIRM" | "MENU" | "EMPTY";
|
|
72
|
+
/** Why we chose this verdict. */
|
|
73
|
+
reasoning: string;
|
|
74
|
+
}
|
|
75
|
+
export interface SelectInput {
|
|
76
|
+
userIntent: string;
|
|
77
|
+
catalog: readonly ToolEntry[];
|
|
78
|
+
ctx?: SelectorContext;
|
|
79
|
+
}
|
|
80
|
+
export declare function selectTool(input: SelectInput): SelectorResult;
|
|
81
|
+
export declare function formatSelectorPulseLine(r: SelectorResult): string;
|
|
82
|
+
export declare function formatConfirmationPrompt(r: SelectorResult): string;
|
|
83
|
+
/** Starter catalog covering the most-misrouted tools. AI agents should
|
|
84
|
+
* EXTEND with the full 100+ catalog from `mneme.capabilities`. */
|
|
85
|
+
export declare const STARTER_CATALOG: ToolEntry[];
|
|
86
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tool_selector/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,MAAM,WAAW,SAAS;IACxB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB;sDACkD;IAClD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,+CAA+C;IAC/C,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,8BAA8B;IAC9B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,4BAA4B;IAC5B,OAAO,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAA;KAAE,CAAC;CACvG;AAED,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,GAAG,EAAE,aAAa,GAAG,IAAI,CAAC;IAC1B,qCAAqC;IACrC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,wCAAwC;IACxC,OAAO,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC;IACjD,iCAAiC;IACjC,SAAS,EAAE,MAAM,CAAC;CACnB;AA+BD,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE,eAAe,CAAC;CACvB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,cAAc,CAyC7D;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAGjE;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CASlE;AAED;mEACmE;AACnE,eAAO,MAAM,eAAe,EAAE,SAAS,EAyEtC,CAAC"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.1.0 -- TOOL SELECTOR · smart intent → tool router (THE FIX for "AI picks wrong tool")
|
|
3
|
+
*
|
|
4
|
+
* User's exact worry: "Mneme ships 100+ tools. AI agents pick the wrong
|
|
5
|
+
* one, customer gets angry. How do we make sure the AI always picks
|
|
6
|
+
* RIGHT?"
|
|
7
|
+
*
|
|
8
|
+
* Vanilla AI: scans tool descriptions, picks one that "looks relevant."
|
|
9
|
+
* Probabilistic + opaque + flaky.
|
|
10
|
+
*
|
|
11
|
+
* TOOL SELECTOR: deterministic, auditable, fuzzy-Thai/English/mixed.
|
|
12
|
+
*
|
|
13
|
+
* selectTool({userIntent: "ส่งสมองไปมือถือ", catalog, ctx})
|
|
14
|
+
* → ranked candidates with confidence + reasoning trace
|
|
15
|
+
* → COMMIT (≥0.75): call it
|
|
16
|
+
* → CONFIRM (0.40-0.75): ask user to confirm
|
|
17
|
+
* → MENU (<0.40): show menu
|
|
18
|
+
*
|
|
19
|
+
* Pure function. Same input → same selection. The AI agent doesn't
|
|
20
|
+
* pick the tool — Mneme picks it FOR the AI based on math.
|
|
21
|
+
*/
|
|
22
|
+
function normalize(s) {
|
|
23
|
+
return s.toLowerCase().normalize("NFC").replace(/[]/g, "");
|
|
24
|
+
}
|
|
25
|
+
function triggerMatchScore(intent, triggers) {
|
|
26
|
+
const intentN = normalize(intent);
|
|
27
|
+
const matches = [];
|
|
28
|
+
let score = 0;
|
|
29
|
+
for (const t of triggers) {
|
|
30
|
+
if (!t)
|
|
31
|
+
continue;
|
|
32
|
+
const tn = normalize(t);
|
|
33
|
+
if (intentN.includes(tn)) {
|
|
34
|
+
matches.push(t);
|
|
35
|
+
// Longer matches contribute more (more specific keyword). A single
|
|
36
|
+
// specific keyword (≥6 chars) already wins ~1.0; additional matches
|
|
37
|
+
// saturate the score.
|
|
38
|
+
score += Math.min(1, tn.length / 6);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { score: Math.min(1, score), matches };
|
|
42
|
+
}
|
|
43
|
+
function recencyBoost(toolName, recent) {
|
|
44
|
+
if (!recent || recent.length === 0)
|
|
45
|
+
return 0;
|
|
46
|
+
const idx = recent.indexOf(toolName);
|
|
47
|
+
if (idx === -1)
|
|
48
|
+
return 0;
|
|
49
|
+
return Math.max(0, 0.10 - idx * 0.04);
|
|
50
|
+
}
|
|
51
|
+
export function selectTool(input) {
|
|
52
|
+
if (input.catalog.length === 0) {
|
|
53
|
+
return { top: null, ranked: [], verdict: "EMPTY", reasoning: "no tools in catalog" };
|
|
54
|
+
}
|
|
55
|
+
const ctx = input.ctx ?? {};
|
|
56
|
+
const N = input.catalog.length;
|
|
57
|
+
const scored = input.catalog.map((tool) => {
|
|
58
|
+
const { score: triggerMatch, matches } = triggerMatchScore(input.userIntent, tool.triggers);
|
|
59
|
+
const recBoost = recencyBoost(tool.name, ctx.recentTools);
|
|
60
|
+
let preferBoost = 0;
|
|
61
|
+
let conflictPenalty = 0;
|
|
62
|
+
if (ctx.recentTools && tool.prefer) {
|
|
63
|
+
for (const r of ctx.recentTools)
|
|
64
|
+
if (tool.prefer.includes(r))
|
|
65
|
+
preferBoost += 0.05;
|
|
66
|
+
}
|
|
67
|
+
if (ctx.recentTools && tool.conflictsWith) {
|
|
68
|
+
for (const r of ctx.recentTools)
|
|
69
|
+
if (tool.conflictsWith.includes(r))
|
|
70
|
+
conflictPenalty += 0.10;
|
|
71
|
+
}
|
|
72
|
+
const prior = tool.prior ?? 1 / N;
|
|
73
|
+
const raw = prior * 0.10 + triggerMatch * 0.80 + recBoost + preferBoost - conflictPenalty;
|
|
74
|
+
const confidence = Math.max(0, Math.min(1, raw));
|
|
75
|
+
return { tool, confidence, matchedTriggers: matches, signals: { triggerMatch, recencyBoost: recBoost, preferBoost, conflictPenalty } };
|
|
76
|
+
});
|
|
77
|
+
scored.sort((a, b) => b.confidence - a.confidence);
|
|
78
|
+
const top = scored[0];
|
|
79
|
+
let verdict;
|
|
80
|
+
let reasoning;
|
|
81
|
+
if (top.confidence >= 0.75) {
|
|
82
|
+
verdict = "COMMIT";
|
|
83
|
+
reasoning = `top tool ${top.tool.name} has confidence ${top.confidence.toFixed(2)} ≥ 0.75 — call it`;
|
|
84
|
+
}
|
|
85
|
+
else if (top.confidence >= 0.40) {
|
|
86
|
+
verdict = "CONFIRM";
|
|
87
|
+
reasoning = `top tool ${top.tool.name} has confidence ${top.confidence.toFixed(2)} in 0.40-0.75 range — ask user to confirm`;
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
verdict = "MENU";
|
|
91
|
+
reasoning = `top confidence ${top.confidence.toFixed(2)} below 0.40 — show user a menu`;
|
|
92
|
+
}
|
|
93
|
+
return { top, ranked: scored, verdict, reasoning };
|
|
94
|
+
}
|
|
95
|
+
export function formatSelectorPulseLine(r) {
|
|
96
|
+
if (!r.top)
|
|
97
|
+
return "TOOL-SELECTOR · EMPTY · no tools";
|
|
98
|
+
return `TOOL-SELECTOR · ${r.verdict} · top=${r.top.tool.name}(${r.top.confidence.toFixed(2)}) · triggers=[${r.top.matchedTriggers.slice(0, 3).join(",")}]`;
|
|
99
|
+
}
|
|
100
|
+
export function formatConfirmationPrompt(r) {
|
|
101
|
+
if (!r.top)
|
|
102
|
+
return "I don't have a tool that matches what you asked. Could you rephrase?";
|
|
103
|
+
if (r.verdict === "COMMIT")
|
|
104
|
+
return `Running '${r.top.tool.name}'.`;
|
|
105
|
+
if (r.verdict === "CONFIRM") {
|
|
106
|
+
const runners = r.ranked.slice(1, 3).map((c) => `${c.tool.name} (${c.confidence.toFixed(2)})`).join(" · ");
|
|
107
|
+
return `About to call '${r.top.tool.name}' (confidence ${r.top.confidence.toFixed(2)}). Other plausible: ${runners}. Confirm or say which one.`;
|
|
108
|
+
}
|
|
109
|
+
const lines = r.ranked.slice(0, 5).map((c, i) => ` ${i + 1}. ${c.tool.name} — ${c.tool.whenToUse}`);
|
|
110
|
+
return `Not sure which tool fits. Pick one:\n${lines.join("\n")}`;
|
|
111
|
+
}
|
|
112
|
+
/** Starter catalog covering the most-misrouted tools. AI agents should
|
|
113
|
+
* EXTEND with the full 100+ catalog from `mneme.capabilities`. */
|
|
114
|
+
export const STARTER_CATALOG = [
|
|
115
|
+
{
|
|
116
|
+
name: "mneme.clone.to",
|
|
117
|
+
category: "rainbow",
|
|
118
|
+
description: "Clone brain to another AI / device.",
|
|
119
|
+
triggers: ["ส่งสมอง", "ส่งความจำ", "ย้าย mneme", "clone", "send brain", "sync", "share mneme", "โคลน", "ก๊อปไป", "brain to"],
|
|
120
|
+
whenToUse: "user wants to send/clone/sync their brain to another AI agent or device",
|
|
121
|
+
examples: ['"ส่งสมองไปมือถือ"', '"clone brain to gemini"'],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "mneme.flash.run",
|
|
125
|
+
category: "flash",
|
|
126
|
+
description: "Anti-hallucination V_eff scan before stating a factual claim.",
|
|
127
|
+
triggers: ["rare", "rarity", "really rare", "is it authentic", "verify", "is this true", "หายากไหม", "ของจริงไหม", "authenticity"],
|
|
128
|
+
whenToUse: "user makes a factual claim or asks AI to confirm one",
|
|
129
|
+
examples: ['"is this card really rare?"'],
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "mneme.passport.issue",
|
|
133
|
+
category: "passport",
|
|
134
|
+
description: "Issue HMAC-signed eternal passport bundle.",
|
|
135
|
+
triggers: ["passport", "export my context", "save my brain", "พาสปอร์ต"],
|
|
136
|
+
whenToUse: "user wants to export portable identity for cross-vendor use",
|
|
137
|
+
examples: ['"issue my passport"'],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "mneme.bloodline.report",
|
|
141
|
+
category: "bloodline",
|
|
142
|
+
description: "Show personal AI genetic strain — DNA fingerprint + σ deviations.",
|
|
143
|
+
triggers: ["bloodline", "my dna", "personality report", "my strain", "ลายนิ้วมือ"],
|
|
144
|
+
whenToUse: "user asks how their Mneme has evolved vs baseline",
|
|
145
|
+
examples: ['"show my AI dna"'],
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "mneme.mutiny.check",
|
|
149
|
+
category: "mutiny",
|
|
150
|
+
description: "Check if request matches documented historical regret.",
|
|
151
|
+
triggers: ["use redis", "tighten jwt", "ใช้ redis"],
|
|
152
|
+
whenToUse: "before any destructive / 'let's try X again' change",
|
|
153
|
+
examples: ['"let me use Redis for sessions"'],
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: "mneme.memory.ask",
|
|
157
|
+
category: "memory",
|
|
158
|
+
description: "Semantic Q&A against the memory store + AI synthesis.",
|
|
159
|
+
triggers: ["what changed", "why did we", "who wrote", "เปลี่ยนอะไร", "ทำไม"],
|
|
160
|
+
whenToUse: "user asks 'what/why/who' about the codebase",
|
|
161
|
+
examples: ['"why did we migrate to postgres?"'],
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
name: "mneme.audit.certify",
|
|
165
|
+
category: "audit",
|
|
166
|
+
description: "5-axis CI trust gate (PASS/WARN/FAIL).",
|
|
167
|
+
triggers: ["certify", "audit", "trust gate", "ตรวจสอบ"],
|
|
168
|
+
whenToUse: "before merging an AI-written commit",
|
|
169
|
+
examples: ['"certify this commit"'],
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "mneme.qx.run_quantum",
|
|
173
|
+
category: "quantum",
|
|
174
|
+
description: "Run quantum circuit on simulator or real cloud.",
|
|
175
|
+
triggers: ["quantum", "qubit", "bell pair", "grover", "qasm", "ควอนตัม"],
|
|
176
|
+
whenToUse: "user provides quantum circuit or asks for famous circuit",
|
|
177
|
+
examples: ['"run a bell pair"'],
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "mneme.system.upgrade",
|
|
181
|
+
category: "system",
|
|
182
|
+
description: "Upgrade Mneme to latest npm version.",
|
|
183
|
+
triggers: ["upgrade mneme", "update mneme", "อัปเกรด mneme", "อัพเดท mneme"],
|
|
184
|
+
whenToUse: "user wants newer Mneme version",
|
|
185
|
+
examples: ['"upgrade Mneme"'],
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tool_selector/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_selector.test.d.ts","sourceRoot":"","sources":["../../src/tool_selector/tool_selector.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { selectTool, formatConfirmationPrompt, formatSelectorPulseLine, STARTER_CATALOG } from "./index.js";
|
|
3
|
+
describe("v2.1 TOOL SELECTOR · the AI-picks-right-tool fix", () => {
|
|
4
|
+
it("COMMIT on strong Thai trigger match", () => {
|
|
5
|
+
const r = selectTool({ userIntent: "ส่งสมองไปมือถือ", catalog: STARTER_CATALOG });
|
|
6
|
+
expect(r.verdict).toBe("COMMIT");
|
|
7
|
+
expect(r.top.tool.name).toBe("mneme.clone.to");
|
|
8
|
+
expect(r.top.confidence).toBeGreaterThanOrEqual(0.75);
|
|
9
|
+
});
|
|
10
|
+
it("COMMIT on English trigger match", () => {
|
|
11
|
+
const r = selectTool({ userIntent: "clone brain to gemini", catalog: STARTER_CATALOG });
|
|
12
|
+
expect(r.verdict).toBe("COMMIT");
|
|
13
|
+
expect(r.top.tool.name).toBe("mneme.clone.to");
|
|
14
|
+
});
|
|
15
|
+
it("CONFIRM on partial match", () => {
|
|
16
|
+
// No clear trigger but a soft signal
|
|
17
|
+
const r = selectTool({ userIntent: "send", catalog: STARTER_CATALOG });
|
|
18
|
+
expect(["CONFIRM", "MENU"]).toContain(r.verdict);
|
|
19
|
+
});
|
|
20
|
+
it("MENU when intent doesn't match anything", () => {
|
|
21
|
+
const r = selectTool({ userIntent: "draw me a unicorn", catalog: STARTER_CATALOG });
|
|
22
|
+
expect(r.verdict).toBe("MENU");
|
|
23
|
+
});
|
|
24
|
+
it("routes 'upgrade Mneme' to system.upgrade", () => {
|
|
25
|
+
const r = selectTool({ userIntent: "upgrade Mneme", catalog: STARTER_CATALOG });
|
|
26
|
+
expect(r.top.tool.name).toBe("mneme.system.upgrade");
|
|
27
|
+
});
|
|
28
|
+
it("routes 'verify this is rare' to flash.run (not clone.to)", () => {
|
|
29
|
+
const r = selectTool({ userIntent: "is this card really rare", catalog: STARTER_CATALOG });
|
|
30
|
+
expect(r.top.tool.name).toBe("mneme.flash.run");
|
|
31
|
+
});
|
|
32
|
+
it("routes 'who wrote auth' to memory.ask", () => {
|
|
33
|
+
const r = selectTool({ userIntent: "who wrote the auth module?", catalog: STARTER_CATALOG });
|
|
34
|
+
expect(r.top.tool.name).toBe("mneme.memory.ask");
|
|
35
|
+
});
|
|
36
|
+
it("recencyBoost lifts a tool from the recent list", () => {
|
|
37
|
+
const r = selectTool({
|
|
38
|
+
userIntent: "is this rare? also verify",
|
|
39
|
+
catalog: STARTER_CATALOG,
|
|
40
|
+
ctx: { recentTools: ["mneme.flash.run"] },
|
|
41
|
+
});
|
|
42
|
+
expect(r.top.tool.name).toBe("mneme.flash.run");
|
|
43
|
+
});
|
|
44
|
+
it("formatConfirmationPrompt produces a helpful menu when MENU", () => {
|
|
45
|
+
const r = selectTool({ userIntent: "asdfqwer", catalog: STARTER_CATALOG });
|
|
46
|
+
const msg = formatConfirmationPrompt(r);
|
|
47
|
+
expect(msg).toContain("Pick one:");
|
|
48
|
+
});
|
|
49
|
+
it("formatSelectorPulseLine produces a one-liner", () => {
|
|
50
|
+
const r = selectTool({ userIntent: "ส่งสมองไปมือถือ", catalog: STARTER_CATALOG });
|
|
51
|
+
expect(formatSelectorPulseLine(r)).toContain("TOOL-SELECTOR");
|
|
52
|
+
});
|
|
53
|
+
it("EMPTY verdict on empty catalog", () => {
|
|
54
|
+
const r = selectTool({ userIntent: "anything", catalog: [] });
|
|
55
|
+
expect(r.verdict).toBe("EMPTY");
|
|
56
|
+
expect(r.top).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
//# sourceMappingURL=tool_selector.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool_selector.test.js","sourceRoot":"","sources":["../../src/tool_selector/tool_selector.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.1.0 -- WISDOM SHARDS · proof-of-truth currency primitive
|
|
3
|
+
*
|
|
4
|
+
* Mint a shard when the AI gives a verified-grounded answer (passes
|
|
5
|
+
* V_eff from FLASH + audit-chain check). Burn a shard when the AI is
|
|
6
|
+
* caught hallucinating. The cumulative balance is HMAC-chained,
|
|
7
|
+
* tamper-evident, and observable across federation nodes (when wired).
|
|
8
|
+
*
|
|
9
|
+
* This is the LEDGER primitive. Federation hub adoption is v1.27 work;
|
|
10
|
+
* for v2.1 we ship the local ledger that any federation can adopt.
|
|
11
|
+
*/
|
|
12
|
+
export interface ShardEntry {
|
|
13
|
+
/** Stable id. */
|
|
14
|
+
id: string;
|
|
15
|
+
ts: number;
|
|
16
|
+
/** Mint (+) or burn (-). */
|
|
17
|
+
kind: "mint" | "burn";
|
|
18
|
+
/** Value (positive integer, in shards). */
|
|
19
|
+
value: number;
|
|
20
|
+
/** Why — for audit. */
|
|
21
|
+
reason: string;
|
|
22
|
+
/** Optional citation (commit / audit-log entry / FLASH verdict id). */
|
|
23
|
+
citation?: string;
|
|
24
|
+
/** Hash chain: SHA-256(prev || JSON(this entry minus chainHash)). */
|
|
25
|
+
chainHash: string;
|
|
26
|
+
}
|
|
27
|
+
export interface Ledger {
|
|
28
|
+
/** Newest first. */
|
|
29
|
+
entries: ShardEntry[];
|
|
30
|
+
/** Public key fingerprint for verifying signatures. */
|
|
31
|
+
keyFingerprint: string;
|
|
32
|
+
}
|
|
33
|
+
export declare function createLedger(secret: Buffer): Ledger;
|
|
34
|
+
export interface AppendInput {
|
|
35
|
+
ledger: Ledger;
|
|
36
|
+
kind: "mint" | "burn";
|
|
37
|
+
value: number;
|
|
38
|
+
reason: string;
|
|
39
|
+
citation?: string;
|
|
40
|
+
secret: Buffer;
|
|
41
|
+
}
|
|
42
|
+
export declare function appendShard(input: AppendInput): {
|
|
43
|
+
ledger: Ledger;
|
|
44
|
+
entry: ShardEntry;
|
|
45
|
+
};
|
|
46
|
+
export interface BalanceResult {
|
|
47
|
+
totalMinted: number;
|
|
48
|
+
totalBurned: number;
|
|
49
|
+
balance: number;
|
|
50
|
+
entryCount: number;
|
|
51
|
+
}
|
|
52
|
+
export declare function balanceOf(ledger: Ledger): BalanceResult;
|
|
53
|
+
export type ChainVerdict = "VALID" | "BROKEN" | "WRONG_KEY";
|
|
54
|
+
export interface ChainVerifyResult {
|
|
55
|
+
verdict: ChainVerdict;
|
|
56
|
+
reason: string;
|
|
57
|
+
/** Index of the first broken entry (entries[0] is newest, so highest broken index = oldest broken). */
|
|
58
|
+
firstBrokenIndex?: number;
|
|
59
|
+
}
|
|
60
|
+
export declare function verifyChain(ledger: Ledger, secret: Buffer): ChainVerifyResult;
|
|
61
|
+
export declare function formatLedgerPulseLine(ledger: Ledger): string;
|
|
62
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/wisdom_shards/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,MAAM,WAAW,UAAU;IACzB,iBAAiB;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,4BAA4B;IAC5B,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,uBAAuB;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,MAAM;IACrB,oBAAoB;IACpB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,uDAAuD;IACvD,cAAc,EAAE,MAAM,CAAC;CACxB;AAWD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,WAAW,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,CAYrF;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAQvD;AAED,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,uGAAuG;IACvG,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAiB7E;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAG5D"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v2.1.0 -- WISDOM SHARDS · proof-of-truth currency primitive
|
|
3
|
+
*
|
|
4
|
+
* Mint a shard when the AI gives a verified-grounded answer (passes
|
|
5
|
+
* V_eff from FLASH + audit-chain check). Burn a shard when the AI is
|
|
6
|
+
* caught hallucinating. The cumulative balance is HMAC-chained,
|
|
7
|
+
* tamper-evident, and observable across federation nodes (when wired).
|
|
8
|
+
*
|
|
9
|
+
* This is the LEDGER primitive. Federation hub adoption is v1.27 work;
|
|
10
|
+
* for v2.1 we ship the local ledger that any federation can adopt.
|
|
11
|
+
*/
|
|
12
|
+
import { createHmac, createHash, randomBytes } from "node:crypto";
|
|
13
|
+
function fpSecret(secret) {
|
|
14
|
+
return createHash("sha256").update(secret).digest("hex").slice(0, 16);
|
|
15
|
+
}
|
|
16
|
+
function chainOf(prevHash, entryWithoutHash, secret) {
|
|
17
|
+
const sig = createHmac("sha256", secret).update(prevHash + JSON.stringify(entryWithoutHash)).digest("hex");
|
|
18
|
+
return sig.slice(0, 32);
|
|
19
|
+
}
|
|
20
|
+
export function createLedger(secret) {
|
|
21
|
+
return { entries: [], keyFingerprint: fpSecret(secret) };
|
|
22
|
+
}
|
|
23
|
+
export function appendShard(input) {
|
|
24
|
+
if (input.value <= 0 || !Number.isInteger(input.value)) {
|
|
25
|
+
throw new Error("value must be a positive integer");
|
|
26
|
+
}
|
|
27
|
+
const ts = Date.now();
|
|
28
|
+
const id = randomBytes(6).toString("hex");
|
|
29
|
+
const prevHash = input.ledger.entries[0]?.chainHash ?? "0".repeat(32);
|
|
30
|
+
const withoutHash = { id, ts, kind: input.kind, value: input.value, reason: input.reason, citation: input.citation };
|
|
31
|
+
const chainHash = chainOf(prevHash, withoutHash, input.secret);
|
|
32
|
+
const entry = { ...withoutHash, chainHash };
|
|
33
|
+
const ledger = { ...input.ledger, entries: [entry, ...input.ledger.entries] };
|
|
34
|
+
return { ledger, entry };
|
|
35
|
+
}
|
|
36
|
+
export function balanceOf(ledger) {
|
|
37
|
+
let totalMinted = 0;
|
|
38
|
+
let totalBurned = 0;
|
|
39
|
+
for (const e of ledger.entries) {
|
|
40
|
+
if (e.kind === "mint")
|
|
41
|
+
totalMinted += e.value;
|
|
42
|
+
else
|
|
43
|
+
totalBurned += e.value;
|
|
44
|
+
}
|
|
45
|
+
return { totalMinted, totalBurned, balance: totalMinted - totalBurned, entryCount: ledger.entries.length };
|
|
46
|
+
}
|
|
47
|
+
export function verifyChain(ledger, secret) {
|
|
48
|
+
if (fpSecret(secret) !== ledger.keyFingerprint) {
|
|
49
|
+
return { verdict: "WRONG_KEY", reason: "secret fingerprint does not match ledger keyFingerprint" };
|
|
50
|
+
}
|
|
51
|
+
// Walk OLDEST → NEWEST. ledger.entries[0] is newest, so reverse.
|
|
52
|
+
let prevHash = "0".repeat(32);
|
|
53
|
+
for (let i = ledger.entries.length - 1; i >= 0; i--) {
|
|
54
|
+
const e = ledger.entries[i];
|
|
55
|
+
const { chainHash: _h, ...rest } = e;
|
|
56
|
+
void _h;
|
|
57
|
+
const expected = chainOf(prevHash, rest, secret);
|
|
58
|
+
if (expected !== e.chainHash) {
|
|
59
|
+
return { verdict: "BROKEN", reason: `chain hash mismatch at entry ${e.id} (index ${i})`, firstBrokenIndex: i };
|
|
60
|
+
}
|
|
61
|
+
prevHash = e.chainHash;
|
|
62
|
+
}
|
|
63
|
+
return { verdict: "VALID", reason: `verified ${ledger.entries.length} entries` };
|
|
64
|
+
}
|
|
65
|
+
export function formatLedgerPulseLine(ledger) {
|
|
66
|
+
const b = balanceOf(ledger);
|
|
67
|
+
return `WISDOM-SHARDS · balance=${b.balance} (minted=${b.totalMinted}, burned=${b.totalBurned}) · ${b.entryCount} entries`;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wisdom_shards/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyBlE,SAAS,QAAQ,CAAC,MAAc;IAC9B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACxE,CAAC;AAED,SAAS,OAAO,CAAC,QAAgB,EAAE,gBAA+C,EAAE,MAAc;IAChG,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3G,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc;IACzC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;AAC3D,CAAC;AAWD,MAAM,UAAU,WAAW,CAAC,KAAkB;IAC5C,IAAI,KAAK,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACtE,MAAM,WAAW,GAAkC,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;IACpJ,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAe,EAAE,GAAG,WAAW,EAAE,SAAS,EAAE,CAAC;IACxD,MAAM,MAAM,GAAW,EAAE,GAAG,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;IACtF,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAC3B,CAAC;AASD,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QAC/B,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM;YAAE,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC;;YACzC,WAAW,IAAI,CAAC,CAAC,KAAK,CAAC;IAC9B,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,WAAW,EAAE,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;AAC7G,CAAC;AAWD,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,MAAc;IACxD,IAAI,QAAQ,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC,cAAc,EAAE,CAAC;QAC/C,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,yDAAyD,EAAE,CAAC;IACrG,CAAC;IACD,iEAAiE;IACjE,IAAI,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC;QAC7B,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC;QACrC,KAAK,EAAE,CAAC;QACR,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACjD,IAAI,QAAQ,KAAK,CAAC,CAAC,SAAS,EAAE,CAAC;YAC7B,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,gCAAgC,CAAC,CAAC,EAAE,WAAW,CAAC,GAAG,EAAE,gBAAgB,EAAE,CAAC,EAAE,CAAC;QACjH,CAAC;QACD,QAAQ,GAAG,CAAC,CAAC,SAAS,CAAC;IACzB,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,MAAM,CAAC,OAAO,CAAC,MAAM,UAAU,EAAE,CAAC;AACnF,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAc;IAClD,MAAM,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,2BAA2B,CAAC,CAAC,OAAO,YAAY,CAAC,CAAC,WAAW,YAAY,CAAC,CAAC,WAAW,OAAO,CAAC,CAAC,UAAU,UAAU,CAAC;AAC7H,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wisdom_shards.test.d.ts","sourceRoot":"","sources":["../../src/wisdom_shards/wisdom_shards.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
import { createLedger, appendShard, balanceOf, verifyChain, formatLedgerPulseLine } from "./index.js";
|
|
4
|
+
describe("v2.1 WISDOM SHARDS · proof-of-truth ledger", () => {
|
|
5
|
+
const secret = randomBytes(32);
|
|
6
|
+
it("createLedger starts empty + has keyFingerprint", () => {
|
|
7
|
+
const l = createLedger(secret);
|
|
8
|
+
expect(l.entries.length).toBe(0);
|
|
9
|
+
expect(l.keyFingerprint.length).toBeGreaterThan(0);
|
|
10
|
+
});
|
|
11
|
+
it("appendShard mint adds value to balance", () => {
|
|
12
|
+
let l = createLedger(secret);
|
|
13
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 5, reason: "verified grounding", secret }));
|
|
14
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 3, reason: "another verification", secret }));
|
|
15
|
+
const b = balanceOf(l);
|
|
16
|
+
expect(b.totalMinted).toBe(8);
|
|
17
|
+
expect(b.balance).toBe(8);
|
|
18
|
+
});
|
|
19
|
+
it("burn subtracts from balance", () => {
|
|
20
|
+
let l = createLedger(secret);
|
|
21
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 10, reason: "ok", secret }));
|
|
22
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "burn", value: 3, reason: "hallucination caught", secret }));
|
|
23
|
+
const b = balanceOf(l);
|
|
24
|
+
expect(b.balance).toBe(7);
|
|
25
|
+
});
|
|
26
|
+
it("rejects non-positive integer value", () => {
|
|
27
|
+
const l = createLedger(secret);
|
|
28
|
+
expect(() => appendShard({ ledger: l, kind: "mint", value: 0, reason: "x", secret })).toThrow(/positive integer/);
|
|
29
|
+
expect(() => appendShard({ ledger: l, kind: "mint", value: 1.5, reason: "x", secret })).toThrow(/positive integer/);
|
|
30
|
+
expect(() => appendShard({ ledger: l, kind: "mint", value: -1, reason: "x", secret })).toThrow(/positive integer/);
|
|
31
|
+
});
|
|
32
|
+
it("chain verifies VALID after correct appends", () => {
|
|
33
|
+
let l = createLedger(secret);
|
|
34
|
+
for (let i = 0; i < 5; i++) {
|
|
35
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 1, reason: `entry-${i}`, secret }));
|
|
36
|
+
}
|
|
37
|
+
expect(verifyChain(l, secret).verdict).toBe("VALID");
|
|
38
|
+
});
|
|
39
|
+
it("chain detects BROKEN entry tampering", () => {
|
|
40
|
+
let l = createLedger(secret);
|
|
41
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 1, reason: "a", secret }));
|
|
42
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 1, reason: "b", secret }));
|
|
43
|
+
// Tamper with the oldest entry
|
|
44
|
+
l.entries[1].value = 99;
|
|
45
|
+
const r = verifyChain(l, secret);
|
|
46
|
+
expect(r.verdict).toBe("BROKEN");
|
|
47
|
+
expect(r.firstBrokenIndex).toBeDefined();
|
|
48
|
+
});
|
|
49
|
+
it("chain returns WRONG_KEY on wrong secret", () => {
|
|
50
|
+
let l = createLedger(secret);
|
|
51
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 1, reason: "a", secret }));
|
|
52
|
+
const wrong = randomBytes(32);
|
|
53
|
+
expect(verifyChain(l, wrong).verdict).toBe("WRONG_KEY");
|
|
54
|
+
});
|
|
55
|
+
it("newest-first ordering: entries[0] is the latest", () => {
|
|
56
|
+
let l = createLedger(secret);
|
|
57
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 1, reason: "first", secret }));
|
|
58
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 1, reason: "second", secret }));
|
|
59
|
+
expect(l.entries[0].reason).toBe("second");
|
|
60
|
+
expect(l.entries[1].reason).toBe("first");
|
|
61
|
+
});
|
|
62
|
+
it("formatLedgerPulseLine summarises", () => {
|
|
63
|
+
let l = createLedger(secret);
|
|
64
|
+
({ ledger: l } = appendShard({ ledger: l, kind: "mint", value: 3, reason: "x", secret }));
|
|
65
|
+
expect(formatLedgerPulseLine(l)).toContain("WISDOM-SHARDS");
|
|
66
|
+
expect(formatLedgerPulseLine(l)).toContain("balance=3");
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=wisdom_shards.test.js.map
|