ai-consensus-core 0.9.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/LICENSE +21 -0
- package/README.md +415 -0
- package/dist/engine.d.ts +30 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +428 -0
- package/dist/engine.js.map +1 -0
- package/dist/events.d.ts +29 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +46 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +27 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +55 -0
- package/dist/parser.js.map +1 -0
- package/dist/personas.d.ts +16 -0
- package/dist/personas.d.ts.map +1 -0
- package/dist/personas.js +109 -0
- package/dist/personas.js.map +1 -0
- package/dist/prompts.d.ts +60 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +111 -0
- package/dist/prompts.js.map +1 -0
- package/dist/stats.d.ts +42 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +108 -0
- package/dist/stats.js.map +1 -0
- package/dist/types.d.ts +303 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +31 -0
- package/dist/types.js.map +1 -0
- package/package.json +71 -0
package/dist/parser.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// Parser — extractors for confidence and judge sections
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
const CONFIDENCE_RE = /CONFIDENCE:\s*(\d+)/i;
|
|
5
|
+
const JUDGE_CONFIDENCE_RE = /JUDGE_CONFIDENCE:\s*\[?\s*(\d+)\s*\]?/i;
|
|
6
|
+
/**
|
|
7
|
+
* Extract the trailing `CONFIDENCE: N` value. Clamps to [0, 100] and
|
|
8
|
+
* defaults to 50 when the marker is absent.
|
|
9
|
+
*
|
|
10
|
+
* The default is intentional: an absent marker is a model compliance
|
|
11
|
+
* issue, not a "low-confidence" signal, so we treat it as neutral rather
|
|
12
|
+
* than letting it skew the consensus score downward.
|
|
13
|
+
*/
|
|
14
|
+
export function extractConfidence(text) {
|
|
15
|
+
const match = CONFIDENCE_RE.exec(text);
|
|
16
|
+
if (!match)
|
|
17
|
+
return 50;
|
|
18
|
+
const n = Number.parseInt(match[1], 10);
|
|
19
|
+
if (Number.isNaN(n))
|
|
20
|
+
return 50;
|
|
21
|
+
return Math.min(100, Math.max(0, n));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Extract the judge's self-reported synthesis confidence. Tolerates both
|
|
25
|
+
* `JUDGE_CONFIDENCE: 87` and `JUDGE_CONFIDENCE: [87]` forms, since the
|
|
26
|
+
* JUDGE_PERSONA prompt wraps the placeholder in brackets.
|
|
27
|
+
*/
|
|
28
|
+
export function extractJudgeConfidence(text) {
|
|
29
|
+
const match = JUDGE_CONFIDENCE_RE.exec(text);
|
|
30
|
+
if (!match)
|
|
31
|
+
return 50;
|
|
32
|
+
const n = Number.parseInt(match[1], 10);
|
|
33
|
+
if (Number.isNaN(n))
|
|
34
|
+
return 50;
|
|
35
|
+
return Math.min(100, Math.max(0, n));
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extract a named `## Heading`-style section from a judge synthesis.
|
|
39
|
+
* Returns the trimmed section body, or "" if not found.
|
|
40
|
+
*/
|
|
41
|
+
export function extractJudgeSection(text, heading) {
|
|
42
|
+
const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
43
|
+
const pattern = new RegExp(`##\\s*${escaped}\\s*\\n([\\s\\S]*?)(?=\\n##\\s|$)`, "i");
|
|
44
|
+
const m = pattern.exec(text);
|
|
45
|
+
return m ? m[1].trim() : "";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Strip the trailing `CONFIDENCE: N` line from a body. Used when quoting
|
|
49
|
+
* a participant response back to a downstream model, so the marker from
|
|
50
|
+
* an earlier round doesn't bleed into the next round's parser pass.
|
|
51
|
+
*/
|
|
52
|
+
export function stripConfidenceLine(text) {
|
|
53
|
+
return text.replace(/\nCONFIDENCE:\s*\d+\s*$/i, "").trim();
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,wDAAwD;AACxD,gEAAgE;AAEhE,MAAM,aAAa,GAAG,sBAAsB,CAAC;AAC7C,MAAM,mBAAmB,GAAG,wCAAwC,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,CAAC;IACtB,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,EAAE,CAAC,CAAC;IACzC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,OAAe;IAC/D,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,SAAS,OAAO,mCAAmC,EAAE,GAAG,CAAC,CAAC;IACrF,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7D,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Persona } from "./types.js";
|
|
2
|
+
export declare const PERSONAS: readonly Persona[];
|
|
3
|
+
/** Look up a persona by id. Returns `undefined` if not found. */
|
|
4
|
+
export declare function getPersonaById(id: string): Persona | undefined;
|
|
5
|
+
/** Look up a persona by id, falling back to the first persona if the id is unknown. */
|
|
6
|
+
export declare function getPersonaOrDefault(id: string): Persona;
|
|
7
|
+
/**
|
|
8
|
+
* The Judge persona — used by the non-voting synthesizer.
|
|
9
|
+
* Not exposed via the participant selector.
|
|
10
|
+
*
|
|
11
|
+
* The output contract is exact: four markdown sections followed by a
|
|
12
|
+
* `JUDGE_CONFIDENCE: [0-100]` line. `parser.extractJudgeSection` and
|
|
13
|
+
* `parser.extractJudgeConfidence` both key off this contract.
|
|
14
|
+
*/
|
|
15
|
+
export declare const JUDGE_PERSONA: Persona;
|
|
16
|
+
//# sourceMappingURL=personas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personas.d.ts","sourceRoot":"","sources":["../src/personas.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,eAAO,MAAM,QAAQ,EAAE,SAAS,OAAO,EAgE7B,CAAC;AAEX,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAE9D;AAED,uFAAuF;AACvF,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,OA2B3B,CAAC"}
|
package/dist/personas.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// Personas — ported verbatim from roundtable/lib/personas.ts
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
// Seven debate personas + one non-voting judge. IDs, names,
|
|
5
|
+
// emojis, colors and system prompts are byte-for-byte identical
|
|
6
|
+
// to the source, so a run here is behaviorally equivalent to a
|
|
7
|
+
// run in the original roundtable UI.
|
|
8
|
+
export const PERSONAS = [
|
|
9
|
+
{
|
|
10
|
+
id: "pessimist",
|
|
11
|
+
name: "Risk Analyst",
|
|
12
|
+
emoji: "☠️",
|
|
13
|
+
color: "#ef4444",
|
|
14
|
+
description: "Identifies risks, failure modes, tail risks, and worst-case scenarios",
|
|
15
|
+
systemPrompt: `You are a rigorous Risk Analyst. Your role is to surface hidden dangers, second-order effects, tail risks, and plausible failure modes. You are not cynical — you are protective. Be precise, evidence-based, and constructive. Highlight what could go wrong and why, so the group can make more robust decisions.`,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "first-principles",
|
|
19
|
+
name: "First-Principles Engineer",
|
|
20
|
+
emoji: "⚙️",
|
|
21
|
+
color: "#3b82f6",
|
|
22
|
+
description: "Breaks every claim down to fundamental truths and reasons from the ground up",
|
|
23
|
+
systemPrompt: `You are a First-Principles Engineer. Ruthlessly decompose every claim into its most fundamental axioms. Reject analogies and conventional wisdom. Question every assumption. Structure your thinking clearly and expose hidden premises that others are taking for granted.`,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "vc-specialist",
|
|
27
|
+
name: "VC Funds Specialist",
|
|
28
|
+
emoji: "💰",
|
|
29
|
+
color: "#10b981",
|
|
30
|
+
description: "Evaluates through market dynamics, scalability, moats, and investment viability",
|
|
31
|
+
systemPrompt: `You are a battle-tested Venture Capital Specialist. Analyze everything through the lens of market opportunity, scalable business models, competitive moats, unit economics, network effects, and capital efficiency. Think in terms of TAM/SAM/SOM, defensibility, and long-term value creation.`,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "scientific-skeptic",
|
|
35
|
+
name: "Scientific Skeptic",
|
|
36
|
+
emoji: "🔬",
|
|
37
|
+
color: "#f59e0b",
|
|
38
|
+
description: "Demands rigorous evidence and applies scientific scrutiny to every claim",
|
|
39
|
+
systemPrompt: `You are a Scientific Skeptic. Demand high-quality evidence for every assertion. Question methodology, sample size, selection bias, statistical power, and reproducibility. Distinguish correlation from causation. Call out logical fallacies and over-extrapolation without mercy.`,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
id: "optimistic-futurist",
|
|
43
|
+
name: "Optimistic Futurist",
|
|
44
|
+
emoji: "🚀",
|
|
45
|
+
color: "#8b5cf6",
|
|
46
|
+
description: "Sees transformative potential and identifies exponential upside opportunities",
|
|
47
|
+
systemPrompt: `You are an Optimistic Futurist. Identify exponential trends, paradigm shifts, and breakthrough opportunities. Paint compelling visions of positive futures while remaining grounded. Focus on how obstacles can be overcome and how the idea could scale into something transformative.`,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "devils-advocate",
|
|
51
|
+
name: "Devil's Advocate",
|
|
52
|
+
emoji: "⚖️",
|
|
53
|
+
color: "#ec4899",
|
|
54
|
+
description: "Stress-tests ideas by arguing the strongest possible counter-position",
|
|
55
|
+
systemPrompt: `You are the Devil's Advocate. Your job is to construct the strongest possible counter-arguments to whatever position is emerging. Do this constructively — not to win, but to expose weaknesses and make the final consensus more robust. Be sharp, logical, and relentless.`,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "domain-expert",
|
|
59
|
+
name: "Domain Expert",
|
|
60
|
+
emoji: "🧠",
|
|
61
|
+
color: "#06b6d4",
|
|
62
|
+
description: "Brings deep technical and practical implementation knowledge with concrete examples",
|
|
63
|
+
systemPrompt: `You are a seasoned Domain Expert with years of hands-on experience. Ground your analysis in real-world implementation details, known patterns, anti-patterns, edge cases, and practical constraints. Be specific, cite concrete examples, and provide reality-checks that only deep domain knowledge can offer.`,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
/** Look up a persona by id. Returns `undefined` if not found. */
|
|
67
|
+
export function getPersonaById(id) {
|
|
68
|
+
return PERSONAS.find((p) => p.id === id);
|
|
69
|
+
}
|
|
70
|
+
/** Look up a persona by id, falling back to the first persona if the id is unknown. */
|
|
71
|
+
export function getPersonaOrDefault(id) {
|
|
72
|
+
return getPersonaById(id) ?? PERSONAS[0];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The Judge persona — used by the non-voting synthesizer.
|
|
76
|
+
* Not exposed via the participant selector.
|
|
77
|
+
*
|
|
78
|
+
* The output contract is exact: four markdown sections followed by a
|
|
79
|
+
* `JUDGE_CONFIDENCE: [0-100]` line. `parser.extractJudgeSection` and
|
|
80
|
+
* `parser.extractJudgeConfidence` both key off this contract.
|
|
81
|
+
*/
|
|
82
|
+
export const JUDGE_PERSONA = {
|
|
83
|
+
id: "judge",
|
|
84
|
+
name: "Consensus Judge",
|
|
85
|
+
emoji: "🪶",
|
|
86
|
+
color: "#eab308",
|
|
87
|
+
description: "Non-voting synthesizer that summarises majority and minority positions",
|
|
88
|
+
systemPrompt: `You are the Consensus Judge. You do NOT participate in the debate and you do NOT vote. Your only job is to read the final-round responses from every participant and produce a faithful synthesis.
|
|
89
|
+
|
|
90
|
+
Produce your output in exactly this shape, with those headings:
|
|
91
|
+
|
|
92
|
+
## Majority Position
|
|
93
|
+
One paragraph describing the position held by the largest coherent group, with the participants who held it.
|
|
94
|
+
|
|
95
|
+
## Minority Positions
|
|
96
|
+
One short paragraph per dissenting view. Always preserve conditional exceptions — do not collapse them into the majority.
|
|
97
|
+
|
|
98
|
+
## Unresolved Disputes
|
|
99
|
+
Bullet list of specific disagreements that remained open at the end of the debate. If none, say "None".
|
|
100
|
+
|
|
101
|
+
## Synthesis Confidence
|
|
102
|
+
A single integer 0-100 reflecting how confident you are that the above synthesis is faithful to what was actually said. End with a line in exactly this format: \`JUDGE_CONFIDENCE: [0-100]\`.
|
|
103
|
+
|
|
104
|
+
Rules:
|
|
105
|
+
- Do not invent claims. Quote or paraphrase what participants actually said.
|
|
106
|
+
- Do not pick a winner. Your job is faithfulness, not victory.
|
|
107
|
+
- Do not collapse a minority view with a conditional exception into the majority.`,
|
|
108
|
+
};
|
|
109
|
+
//# sourceMappingURL=personas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"personas.js","sourceRoot":"","sources":["../src/personas.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAChE,6DAA6D;AAC7D,gEAAgE;AAChE,4DAA4D;AAC5D,gEAAgE;AAChE,+DAA+D;AAC/D,qCAAqC;AAIrC,MAAM,CAAC,MAAM,QAAQ,GAAuB;IAC1C;QACE,EAAE,EAAE,WAAW;QACf,IAAI,EAAE,cAAc;QACpB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,uEAAuE;QACzE,YAAY,EAAE,qTAAqT;KACpU;IACD;QACE,EAAE,EAAE,kBAAkB;QACtB,IAAI,EAAE,2BAA2B;QACjC,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,8EAA8E;QAChF,YAAY,EAAE,6QAA6Q;KAC5R;IACD;QACE,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,qBAAqB;QAC3B,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,iFAAiF;QACnF,YAAY,EAAE,kSAAkS;KACjT;IACD;QACE,EAAE,EAAE,oBAAoB;QACxB,IAAI,EAAE,oBAAoB;QAC1B,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,0EAA0E;QAC5E,YAAY,EAAE,qRAAqR;KACpS;IACD;QACE,EAAE,EAAE,qBAAqB;QACzB,IAAI,EAAE,qBAAqB;QAC3B,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,+EAA+E;QACjF,YAAY,EAAE,yRAAyR;KACxS;IACD;QACE,EAAE,EAAE,iBAAiB;QACrB,IAAI,EAAE,kBAAkB;QACxB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,uEAAuE;QACzE,YAAY,EAAE,8QAA8Q;KAC7R;IACD;QACE,EAAE,EAAE,eAAe;QACnB,IAAI,EAAE,eAAe;QACrB,KAAK,EAAE,IAAI;QACX,KAAK,EAAE,SAAS;QAChB,WAAW,EACT,qFAAqF;QACvF,YAAY,EAAE,iTAAiT;KAChU;CACO,CAAC;AAEX,iEAAiE;AACjE,MAAM,UAAU,cAAc,CAAC,EAAU;IACvC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,mBAAmB,CAAC,EAAU;IAC5C,OAAO,cAAc,CAAC,EAAE,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAE,CAAC;AAC5C,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAY;IACpC,EAAE,EAAE,OAAO;IACX,IAAI,EAAE,iBAAiB;IACvB,KAAK,EAAE,IAAI;IACX,KAAK,EAAE,SAAS;IAChB,WAAW,EACT,wEAAwE;IAC1E,YAAY,EAAE;;;;;;;;;;;;;;;;;;;kFAmBkE;CACjF,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ParticipantResponse, Participant, Phase } from "./types.js";
|
|
2
|
+
export interface RoundMeta {
|
|
3
|
+
phase: Phase;
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Map a round number to its phase and human label.
|
|
8
|
+
*
|
|
9
|
+
* Round 1 → initial-analysis "Initial Analysis"
|
|
10
|
+
* Round 2 → counterarguments "Counterarguments"
|
|
11
|
+
* Round 3 → evidence-assessment "Evidence Assessment"
|
|
12
|
+
* Round 4..N-1 → synthesis "Synthesis & Refinement (Round N)"
|
|
13
|
+
* Round N → synthesis "Final Synthesis"
|
|
14
|
+
*/
|
|
15
|
+
export declare function getRoundMeta(round: number, totalRounds: number): RoundMeta;
|
|
16
|
+
/**
|
|
17
|
+
* Format the block of previous responses that a participant sees at the
|
|
18
|
+
* start of rounds 2+. Matches roundtable's "PREVIOUS ROUND RESPONSES"
|
|
19
|
+
* fence so models can't latch onto a different delimiter across versions.
|
|
20
|
+
*/
|
|
21
|
+
export declare function formatPreviousResponses(responses: readonly ParticipantResponse[]): string;
|
|
22
|
+
/**
|
|
23
|
+
* Build the full system prompt for a single participant call.
|
|
24
|
+
*
|
|
25
|
+
* Shape:
|
|
26
|
+
*
|
|
27
|
+
* {persona.systemPrompt}
|
|
28
|
+
*
|
|
29
|
+
* {phase instructions}{previous-responses block, if any}
|
|
30
|
+
*
|
|
31
|
+
* IMPORTANT: End your response with a line in exactly this format:
|
|
32
|
+
* CONFIDENCE: [number 0-100]
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildParticipantSystemPrompt(params: {
|
|
35
|
+
personaSystemPrompt: string;
|
|
36
|
+
phase: Phase;
|
|
37
|
+
round: number;
|
|
38
|
+
totalRounds: number;
|
|
39
|
+
previousResponses: readonly ParticipantResponse[];
|
|
40
|
+
}): string;
|
|
41
|
+
/**
|
|
42
|
+
* Build the judge's system prompt. We append the original user prompt to
|
|
43
|
+
* the JUDGE_PERSONA's instructions so the model knows what was debated,
|
|
44
|
+
* without having to infer it from participant text.
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildJudgeSystemPrompt(params: {
|
|
47
|
+
judgeSystemPrompt: string;
|
|
48
|
+
question: string;
|
|
49
|
+
}): string;
|
|
50
|
+
/**
|
|
51
|
+
* Build the judge's user content: the final-round responses, labelled with
|
|
52
|
+
* persona name and model id, and with the trailing `CONFIDENCE: N` line
|
|
53
|
+
* stripped from each body (the participant confidence is surfaced in the
|
|
54
|
+
* heading instead).
|
|
55
|
+
*/
|
|
56
|
+
export declare function buildJudgeUserPrompt(params: {
|
|
57
|
+
finalResponses: readonly ParticipantResponse[];
|
|
58
|
+
participants: readonly Participant[];
|
|
59
|
+
}): string;
|
|
60
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAE1E,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,CAW1E;AA0BD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,SAAS,mBAAmB,EAAE,GAAG,MAAM,CAMzF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACnD,GAAG,MAAM,CAST;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB,GAAG,MAAM,CAOT;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,cAAc,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC/C,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;CACtC,GAAG,MAAM,CAWT"}
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// Prompts — faithful port of the CVP prompt templates.
|
|
3
|
+
// ─────────────────────────────────────────────────────────────
|
|
4
|
+
// The exact strings here shape model behavior. Any drift between
|
|
5
|
+
// this file and roundtable/lib/consensus-engine.ts will change
|
|
6
|
+
// run semantics, so edits should be deliberate.
|
|
7
|
+
/**
|
|
8
|
+
* Map a round number to its phase and human label.
|
|
9
|
+
*
|
|
10
|
+
* Round 1 → initial-analysis "Initial Analysis"
|
|
11
|
+
* Round 2 → counterarguments "Counterarguments"
|
|
12
|
+
* Round 3 → evidence-assessment "Evidence Assessment"
|
|
13
|
+
* Round 4..N-1 → synthesis "Synthesis & Refinement (Round N)"
|
|
14
|
+
* Round N → synthesis "Final Synthesis"
|
|
15
|
+
*/
|
|
16
|
+
export function getRoundMeta(round, totalRounds) {
|
|
17
|
+
if (round === 1)
|
|
18
|
+
return { phase: "initial-analysis", label: "Initial Analysis" };
|
|
19
|
+
if (round === 2)
|
|
20
|
+
return { phase: "counterarguments", label: "Counterarguments" };
|
|
21
|
+
if (round === 3)
|
|
22
|
+
return { phase: "evidence-assessment", label: "Evidence Assessment" };
|
|
23
|
+
return {
|
|
24
|
+
phase: "synthesis",
|
|
25
|
+
label: round === totalRounds
|
|
26
|
+
? "Final Synthesis"
|
|
27
|
+
: `Synthesis & Refinement (Round ${round})`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const PHASE_INSTRUCTIONS = {
|
|
31
|
+
"initial-analysis": (round, total) => `This is Round ${round}/${total}: INITIAL ANALYSIS.
|
|
32
|
+
Provide your initial analysis of the prompt. Share your perspective, key observations, and preliminary assessment. State your confidence level (0-100) at the end.`,
|
|
33
|
+
counterarguments: (round, total) => `This is Round ${round}/${total}: COUNTERARGUMENTS.
|
|
34
|
+
Review the initial analyses from all participants below. Identify weaknesses, biases, and blind spots. Offer substantive counterarguments. Challenge assumptions. State your updated confidence level (0-100) at the end.`,
|
|
35
|
+
"evidence-assessment": (round, total) => `This is Round ${round}/${total}: EVIDENCE ASSESSMENT.
|
|
36
|
+
Evaluate the strength of evidence and reasoning presented so far. Distinguish well-supported claims from speculation. Identify areas where consensus is forming and where disagreement remains substantive. State your confidence level (0-100) at the end.`,
|
|
37
|
+
synthesis: (round, total) => {
|
|
38
|
+
const isFinal = round === total;
|
|
39
|
+
const header = isFinal ? "FINAL SYNTHESIS" : "SYNTHESIS & REFINEMENT";
|
|
40
|
+
const directive = isFinal
|
|
41
|
+
? "Provide your final, considered position."
|
|
42
|
+
: "Refine your position based on the strongest arguments presented.";
|
|
43
|
+
return `This is Round ${round}/${total}: ${header}.
|
|
44
|
+
Synthesize the discussion so far into a coherent assessment. Acknowledge remaining uncertainties. ${directive} State your final confidence level (0-100) at the end.`;
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Format the block of previous responses that a participant sees at the
|
|
49
|
+
* start of rounds 2+. Matches roundtable's "PREVIOUS ROUND RESPONSES"
|
|
50
|
+
* fence so models can't latch onto a different delimiter across versions.
|
|
51
|
+
*/
|
|
52
|
+
export function formatPreviousResponses(responses) {
|
|
53
|
+
if (responses.length === 0)
|
|
54
|
+
return "";
|
|
55
|
+
const blocks = responses.map((r) => `[Participant ${r.participantId} | Confidence: ${r.confidence}%]\n${r.content}`);
|
|
56
|
+
return `\n\n--- PREVIOUS ROUND RESPONSES ---\n${blocks.join("\n\n---\n\n")}\n--- END PREVIOUS RESPONSES ---`;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Build the full system prompt for a single participant call.
|
|
60
|
+
*
|
|
61
|
+
* Shape:
|
|
62
|
+
*
|
|
63
|
+
* {persona.systemPrompt}
|
|
64
|
+
*
|
|
65
|
+
* {phase instructions}{previous-responses block, if any}
|
|
66
|
+
*
|
|
67
|
+
* IMPORTANT: End your response with a line in exactly this format:
|
|
68
|
+
* CONFIDENCE: [number 0-100]
|
|
69
|
+
*/
|
|
70
|
+
export function buildParticipantSystemPrompt(params) {
|
|
71
|
+
const instructions = PHASE_INSTRUCTIONS[params.phase](params.round, params.totalRounds);
|
|
72
|
+
const previousContext = formatPreviousResponses(params.previousResponses);
|
|
73
|
+
return `${params.personaSystemPrompt}
|
|
74
|
+
|
|
75
|
+
${instructions}${previousContext}
|
|
76
|
+
|
|
77
|
+
IMPORTANT: End your response with a line in exactly this format:
|
|
78
|
+
CONFIDENCE: [number 0-100]`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Build the judge's system prompt. We append the original user prompt to
|
|
82
|
+
* the JUDGE_PERSONA's instructions so the model knows what was debated,
|
|
83
|
+
* without having to infer it from participant text.
|
|
84
|
+
*/
|
|
85
|
+
export function buildJudgeSystemPrompt(params) {
|
|
86
|
+
return `${params.judgeSystemPrompt}
|
|
87
|
+
|
|
88
|
+
The original prompt that was debated was:
|
|
89
|
+
"""
|
|
90
|
+
${params.question}
|
|
91
|
+
"""`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Build the judge's user content: the final-round responses, labelled with
|
|
95
|
+
* persona name and model id, and with the trailing `CONFIDENCE: N` line
|
|
96
|
+
* stripped from each body (the participant confidence is surfaced in the
|
|
97
|
+
* heading instead).
|
|
98
|
+
*/
|
|
99
|
+
export function buildJudgeUserPrompt(params) {
|
|
100
|
+
const { finalResponses, participants } = params;
|
|
101
|
+
const blocks = finalResponses.map((r) => {
|
|
102
|
+
const p = participants.find((x) => x.id === r.participantId);
|
|
103
|
+
const label = p
|
|
104
|
+
? `${p.persona.name} (${p.modelId})`
|
|
105
|
+
: r.participantId;
|
|
106
|
+
const body = r.content.replace(/\nCONFIDENCE:\s*\d+\s*$/i, "").trim();
|
|
107
|
+
return `### ${label} — self-reported confidence ${r.confidence}%\n${body}`;
|
|
108
|
+
});
|
|
109
|
+
return `Below are the final-round responses from every participant. Synthesize them per your instructions.\n\n${blocks.join("\n\n---\n\n")}`;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"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"}
|
package/dist/stats.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Disagreement, Participant, ParticipantResponse } from "./types.js";
|
|
2
|
+
/** Arithmetic mean. Returns 0 for empty input. */
|
|
3
|
+
export declare function average(xs: readonly number[]): number;
|
|
4
|
+
/**
|
|
5
|
+
* Population standard deviation (divides by N, not N-1).
|
|
6
|
+
*
|
|
7
|
+
* We use population stddev rather than sample stddev to stay aligned with
|
|
8
|
+
* the roundtable reference implementation. The participant count is the
|
|
9
|
+
* entire panel, not a sample from a larger distribution.
|
|
10
|
+
*/
|
|
11
|
+
export declare function stddev(xs: readonly number[]): number;
|
|
12
|
+
/**
|
|
13
|
+
* Consensus score: `round(clamp(avg − 0.5·stddev, 0, 100))`.
|
|
14
|
+
*
|
|
15
|
+
* Penalizes disagreement (high stddev) against the pack's confidence.
|
|
16
|
+
* Integer-rounded so early-stop deltas are stable.
|
|
17
|
+
*/
|
|
18
|
+
export declare function consensusScore(confidences: readonly number[]): number;
|
|
19
|
+
/**
|
|
20
|
+
* Pairwise disagreement detection. Any two non-errored responses whose
|
|
21
|
+
* confidence differs by at least `threshold` (default 20) generates a
|
|
22
|
+
* Disagreement entry. Deterministic (no text-based heuristic, no extra
|
|
23
|
+
* LLM calls).
|
|
24
|
+
*/
|
|
25
|
+
export declare function detectDisagreements(params: {
|
|
26
|
+
round: number;
|
|
27
|
+
responses: readonly ParticipantResponse[];
|
|
28
|
+
participants: readonly Participant[];
|
|
29
|
+
threshold?: number;
|
|
30
|
+
}): Disagreement[];
|
|
31
|
+
/**
|
|
32
|
+
* Fisher–Yates shuffle. Pure (returns a new array) and takes an injectable
|
|
33
|
+
* RNG so callers can seed it for deterministic replay.
|
|
34
|
+
*/
|
|
35
|
+
export declare function shuffle<T>(input: readonly T[], rng?: () => number): T[];
|
|
36
|
+
/**
|
|
37
|
+
* Small, allocation-free mulberry32 PRNG. Used only when the caller
|
|
38
|
+
* passes `randomSeed` for deterministic runs — otherwise we use
|
|
39
|
+
* `Math.random`. Not cryptographically secure; we don't need it to be.
|
|
40
|
+
*/
|
|
41
|
+
export declare function mulberry32(seed: number): () => number;
|
|
42
|
+
//# sourceMappingURL=stats.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjF,kDAAkD;AAClD,wBAAgB,OAAO,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAKrD;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CASpD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAKrE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC1C,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,YAAY,EAAE,CA0BjB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,GAAE,MAAM,MAAoB,GAAG,CAAC,EAAE,CASpF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,MAAM,CASrD"}
|
package/dist/stats.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────
|
|
2
|
+
// Stats — averages, standard deviation, consensus score,
|
|
3
|
+
// disagreement detection.
|
|
4
|
+
// ─────────────────────────────────────────────────────────────
|
|
5
|
+
/** Arithmetic mean. Returns 0 for empty input. */
|
|
6
|
+
export function average(xs) {
|
|
7
|
+
if (xs.length === 0)
|
|
8
|
+
return 0;
|
|
9
|
+
let sum = 0;
|
|
10
|
+
for (const x of xs)
|
|
11
|
+
sum += x;
|
|
12
|
+
return sum / xs.length;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Population standard deviation (divides by N, not N-1).
|
|
16
|
+
*
|
|
17
|
+
* We use population stddev rather than sample stddev to stay aligned with
|
|
18
|
+
* the roundtable reference implementation. The participant count is the
|
|
19
|
+
* entire panel, not a sample from a larger distribution.
|
|
20
|
+
*/
|
|
21
|
+
export function stddev(xs) {
|
|
22
|
+
if (xs.length === 0)
|
|
23
|
+
return 0;
|
|
24
|
+
const mean = average(xs);
|
|
25
|
+
let varSum = 0;
|
|
26
|
+
for (const x of xs) {
|
|
27
|
+
const d = x - mean;
|
|
28
|
+
varSum += d * d;
|
|
29
|
+
}
|
|
30
|
+
return Math.sqrt(varSum / xs.length);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Consensus score: `round(clamp(avg − 0.5·stddev, 0, 100))`.
|
|
34
|
+
*
|
|
35
|
+
* Penalizes disagreement (high stddev) against the pack's confidence.
|
|
36
|
+
* Integer-rounded so early-stop deltas are stable.
|
|
37
|
+
*/
|
|
38
|
+
export function consensusScore(confidences) {
|
|
39
|
+
if (confidences.length === 0)
|
|
40
|
+
return 0;
|
|
41
|
+
const avg = average(confidences);
|
|
42
|
+
const sd = stddev(confidences);
|
|
43
|
+
return Math.round(Math.max(0, Math.min(100, avg - sd * 0.5)));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Pairwise disagreement detection. Any two non-errored responses whose
|
|
47
|
+
* confidence differs by at least `threshold` (default 20) generates a
|
|
48
|
+
* Disagreement entry. Deterministic (no text-based heuristic, no extra
|
|
49
|
+
* LLM calls).
|
|
50
|
+
*/
|
|
51
|
+
export function detectDisagreements(params) {
|
|
52
|
+
const { round, responses, participants } = params;
|
|
53
|
+
const threshold = params.threshold ?? 20;
|
|
54
|
+
const out = [];
|
|
55
|
+
for (let i = 0; i < responses.length; i++) {
|
|
56
|
+
for (let j = i + 1; j < responses.length; j++) {
|
|
57
|
+
const a = responses[i];
|
|
58
|
+
const b = responses[j];
|
|
59
|
+
if (a.error || b.error)
|
|
60
|
+
continue;
|
|
61
|
+
const delta = Math.abs(a.confidence - b.confidence);
|
|
62
|
+
if (delta < threshold)
|
|
63
|
+
continue;
|
|
64
|
+
const pa = participants.find((p) => p.id === a.participantId);
|
|
65
|
+
const pb = participants.find((p) => p.id === b.participantId);
|
|
66
|
+
const label = pa && pb ? `${pa.persona.name} vs ${pb.persona.name}` : "Confidence split";
|
|
67
|
+
out.push({
|
|
68
|
+
id: `r${round}-${a.participantId}-${b.participantId}`,
|
|
69
|
+
round,
|
|
70
|
+
participantAId: a.participantId,
|
|
71
|
+
participantBId: b.participantId,
|
|
72
|
+
severity: delta,
|
|
73
|
+
label,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Fisher–Yates shuffle. Pure (returns a new array) and takes an injectable
|
|
81
|
+
* RNG so callers can seed it for deterministic replay.
|
|
82
|
+
*/
|
|
83
|
+
export function shuffle(input, rng = Math.random) {
|
|
84
|
+
const out = input.slice();
|
|
85
|
+
for (let i = out.length - 1; i > 0; i--) {
|
|
86
|
+
const j = Math.floor(rng() * (i + 1));
|
|
87
|
+
const tmp = out[i];
|
|
88
|
+
out[i] = out[j];
|
|
89
|
+
out[j] = tmp;
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Small, allocation-free mulberry32 PRNG. Used only when the caller
|
|
95
|
+
* passes `randomSeed` for deterministic runs — otherwise we use
|
|
96
|
+
* `Math.random`. Not cryptographically secure; we don't need it to be.
|
|
97
|
+
*/
|
|
98
|
+
export function mulberry32(seed) {
|
|
99
|
+
let t = seed >>> 0;
|
|
100
|
+
return () => {
|
|
101
|
+
t = (t + 0x6d2b79f5) >>> 0;
|
|
102
|
+
let r = t;
|
|
103
|
+
r = Math.imul(r ^ (r >>> 15), r | 1);
|
|
104
|
+
r ^= r + Math.imul(r ^ (r >>> 7), r | 61);
|
|
105
|
+
return ((r ^ (r >>> 14)) >>> 0) / 4294967296;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=stats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats.js","sourceRoot":"","sources":["../src/stats.ts"],"names":[],"mappings":"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"}
|