mcp-rubber-duck 1.2.4 → 1.3.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.
Files changed (48) hide show
  1. package/.eslintrc.json +1 -0
  2. package/.github/workflows/security.yml +4 -2
  3. package/.github/workflows/semantic-release.yml +4 -2
  4. package/CHANGELOG.md +20 -0
  5. package/README.md +116 -2
  6. package/audit-ci.json +3 -1
  7. package/dist/config/types.d.ts +78 -0
  8. package/dist/config/types.d.ts.map +1 -1
  9. package/dist/server.d.ts.map +1 -1
  10. package/dist/server.js +150 -0
  11. package/dist/server.js.map +1 -1
  12. package/dist/services/consensus.d.ts +28 -0
  13. package/dist/services/consensus.d.ts.map +1 -0
  14. package/dist/services/consensus.js +257 -0
  15. package/dist/services/consensus.js.map +1 -0
  16. package/dist/services/mcp-client-manager.d.ts.map +1 -1
  17. package/dist/services/mcp-client-manager.js +1 -3
  18. package/dist/services/mcp-client-manager.js.map +1 -1
  19. package/dist/tools/duck-debate.d.ts +16 -0
  20. package/dist/tools/duck-debate.d.ts.map +1 -0
  21. package/dist/tools/duck-debate.js +272 -0
  22. package/dist/tools/duck-debate.js.map +1 -0
  23. package/dist/tools/duck-iterate.d.ts +14 -0
  24. package/dist/tools/duck-iterate.d.ts.map +1 -0
  25. package/dist/tools/duck-iterate.js +195 -0
  26. package/dist/tools/duck-iterate.js.map +1 -0
  27. package/dist/tools/duck-judge.d.ts +15 -0
  28. package/dist/tools/duck-judge.d.ts.map +1 -0
  29. package/dist/tools/duck-judge.js +208 -0
  30. package/dist/tools/duck-judge.js.map +1 -0
  31. package/dist/tools/duck-vote.d.ts +14 -0
  32. package/dist/tools/duck-vote.d.ts.map +1 -0
  33. package/dist/tools/duck-vote.js +46 -0
  34. package/dist/tools/duck-vote.js.map +1 -0
  35. package/package.json +1 -1
  36. package/src/config/types.ts +92 -0
  37. package/src/server.ts +154 -0
  38. package/src/services/consensus.ts +324 -0
  39. package/src/services/mcp-client-manager.ts +1 -3
  40. package/src/tools/duck-debate.ts +383 -0
  41. package/src/tools/duck-iterate.ts +253 -0
  42. package/src/tools/duck-judge.ts +301 -0
  43. package/src/tools/duck-vote.ts +87 -0
  44. package/tests/consensus.test.ts +282 -0
  45. package/tests/duck-debate.test.ts +286 -0
  46. package/tests/duck-iterate.test.ts +249 -0
  47. package/tests/duck-judge.test.ts +296 -0
  48. package/tests/duck-vote.test.ts +250 -0
@@ -0,0 +1,14 @@
1
+ import { ProviderManager } from '../providers/manager.js';
2
+ export interface DuckIterateArgs {
3
+ prompt: string;
4
+ iterations?: number;
5
+ providers: [string, string];
6
+ mode: 'refine' | 'critique-improve';
7
+ }
8
+ export declare function duckIterateTool(providerManager: ProviderManager, args: Record<string, unknown>): Promise<{
9
+ content: {
10
+ type: string;
11
+ text: string;
12
+ }[];
13
+ }>;
14
+ //# sourceMappingURL=duck-iterate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duck-iterate.d.ts","sourceRoot":"","sources":["../../src/tools/duck-iterate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI1D,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,IAAI,EAAE,QAAQ,GAAG,kBAAkB,CAAC;CACrC;AAKD,wBAAsB,eAAe,CACnC,eAAe,EAAE,eAAe,EAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;GAkH9B"}
@@ -0,0 +1,195 @@
1
+ import { logger } from '../utils/logger.js';
2
+ const DEFAULT_ITERATIONS = 3;
3
+ const CONVERGENCE_THRESHOLD = 0.8; // 80% similarity indicates convergence
4
+ export async function duckIterateTool(providerManager, args) {
5
+ const { prompt, iterations = DEFAULT_ITERATIONS, providers, mode, } = args;
6
+ // Validate inputs
7
+ if (!prompt || typeof prompt !== 'string') {
8
+ throw new Error('Prompt is required');
9
+ }
10
+ if (!providers || !Array.isArray(providers) || providers.length !== 2) {
11
+ throw new Error('Exactly 2 providers are required for iteration');
12
+ }
13
+ if (!mode || !['refine', 'critique-improve'].includes(mode)) {
14
+ throw new Error('Mode must be either "refine" or "critique-improve"');
15
+ }
16
+ if (iterations < 1 || iterations > 10) {
17
+ throw new Error('Iterations must be between 1 and 10');
18
+ }
19
+ // Validate providers exist
20
+ const providerNames = providerManager.getProviderNames();
21
+ for (const p of providers) {
22
+ if (!providerNames.includes(p)) {
23
+ throw new Error(`Provider "${p}" not found`);
24
+ }
25
+ }
26
+ logger.info(`Starting ${mode} iteration with ${providers.join(' <-> ')} for ${iterations} rounds`);
27
+ const rounds = [];
28
+ let lastResponse = '';
29
+ let converged = false;
30
+ // Round 1: Initial generation by provider A
31
+ const initialResponse = await providerManager.askDuck(providers[0], prompt);
32
+ const providerAInfo = providerManager.getProvider(providers[0]);
33
+ rounds.push({
34
+ round: 1,
35
+ provider: providers[0],
36
+ nickname: providerAInfo.nickname,
37
+ role: 'generator',
38
+ content: initialResponse.content,
39
+ timestamp: new Date(),
40
+ });
41
+ lastResponse = initialResponse.content;
42
+ logger.info(`Round 1: ${providers[0]} generated initial response`);
43
+ // Subsequent rounds: Alternate between providers
44
+ for (let i = 2; i <= iterations; i++) {
45
+ const isProviderA = i % 2 === 1;
46
+ const currentProvider = isProviderA ? providers[0] : providers[1];
47
+ const providerInfo = providerManager.getProvider(currentProvider);
48
+ const iterationPrompt = buildIterationPrompt(prompt, lastResponse, mode, i, rounds);
49
+ const response = await providerManager.askDuck(currentProvider, iterationPrompt);
50
+ // Check for convergence
51
+ if (checkConvergence(lastResponse, response.content)) {
52
+ converged = true;
53
+ logger.info(`Convergence detected at round ${i}`);
54
+ }
55
+ const role = mode === 'refine' ? 'refiner' : (i % 2 === 0 ? 'critic' : 'refiner');
56
+ rounds.push({
57
+ round: i,
58
+ provider: currentProvider,
59
+ nickname: providerInfo.nickname,
60
+ role,
61
+ content: response.content,
62
+ timestamp: new Date(),
63
+ });
64
+ lastResponse = response.content;
65
+ logger.info(`Round ${i}: ${currentProvider} ${role === 'critic' ? 'critiqued' : 'refined'}`);
66
+ if (converged) {
67
+ break;
68
+ }
69
+ }
70
+ const result = {
71
+ prompt,
72
+ mode,
73
+ providers,
74
+ rounds,
75
+ finalResponse: lastResponse,
76
+ totalIterations: rounds.length,
77
+ converged,
78
+ };
79
+ // Format output
80
+ const formattedOutput = formatIterationResult(result);
81
+ logger.info(`Iteration completed: ${rounds.length} rounds, converged: ${converged}`);
82
+ return {
83
+ content: [
84
+ {
85
+ type: 'text',
86
+ text: formattedOutput,
87
+ },
88
+ ],
89
+ };
90
+ }
91
+ function buildIterationPrompt(originalPrompt, previousResponse, mode, round, previousRounds) {
92
+ if (mode === 'refine') {
93
+ return `You are refining a response through iterative improvement.
94
+
95
+ ORIGINAL TASK:
96
+ ${originalPrompt}
97
+
98
+ PREVIOUS RESPONSE (Round ${round - 1}):
99
+ ${previousResponse}
100
+
101
+ YOUR TASK:
102
+ Improve upon the previous response. Make it:
103
+ - More accurate
104
+ - More complete
105
+ - Clearer and better structured
106
+ - More practical and actionable
107
+
108
+ Provide your improved version directly. Do not explain what you changed - just give the improved response.`;
109
+ }
110
+ else {
111
+ // critique-improve mode
112
+ const isEvenRound = round % 2 === 0;
113
+ if (isEvenRound) {
114
+ // Critic round
115
+ return `You are a critical reviewer evaluating a response.
116
+
117
+ ORIGINAL TASK:
118
+ ${originalPrompt}
119
+
120
+ RESPONSE TO CRITIQUE:
121
+ ${previousResponse}
122
+
123
+ YOUR TASK:
124
+ Provide a thorough critique of this response:
125
+ 1. Identify specific weaknesses, errors, or gaps
126
+ 2. Point out unclear or confusing parts
127
+ 3. Suggest concrete improvements
128
+ 4. Note any missing considerations
129
+
130
+ Be constructive but thorough. Format as a bulleted critique.`;
131
+ }
132
+ else {
133
+ // Improvement round based on critique
134
+ const lastCritique = previousRounds[previousRounds.length - 1]?.content || '';
135
+ const lastGoodResponse = previousRounds[previousRounds.length - 2]?.content || previousResponse;
136
+ return `You are improving a response based on critical feedback.
137
+
138
+ ORIGINAL TASK:
139
+ ${originalPrompt}
140
+
141
+ PREVIOUS RESPONSE:
142
+ ${lastGoodResponse}
143
+
144
+ CRITIQUE RECEIVED:
145
+ ${lastCritique}
146
+
147
+ YOUR TASK:
148
+ Create an improved response that addresses the critique points while maintaining the strengths of the original. Provide only the improved response, not meta-commentary.`;
149
+ }
150
+ }
151
+ }
152
+ function checkConvergence(previous, current) {
153
+ // Simple similarity check based on length and common words
154
+ const prevWords = new Set(previous.toLowerCase().split(/\s+/));
155
+ const currWords = new Set(current.toLowerCase().split(/\s+/));
156
+ const intersection = new Set([...prevWords].filter(x => currWords.has(x)));
157
+ const union = new Set([...prevWords, ...currWords]);
158
+ const similarity = intersection.size / union.size;
159
+ // Also check if lengths are similar
160
+ const lengthRatio = Math.min(previous.length, current.length) / Math.max(previous.length, current.length);
161
+ return similarity > CONVERGENCE_THRESHOLD && lengthRatio > 0.8;
162
+ }
163
+ function formatIterationResult(result) {
164
+ let output = `🔄 **Iterative Refinement Results**\n`;
165
+ output += `═══════════════════════════════════════\n\n`;
166
+ output += `**Mode:** ${result.mode}\n`;
167
+ output += `**Providers:** ${result.providers.join(' ↔ ')}\n`;
168
+ output += `**Iterations:** ${result.totalIterations}`;
169
+ if (result.converged) {
170
+ output += ` (converged early ✓)`;
171
+ }
172
+ output += `\n\n`;
173
+ // Show each round
174
+ output += `**Iteration History:**\n`;
175
+ output += `─────────────────────────────────────\n`;
176
+ for (const round of result.rounds) {
177
+ const roleEmoji = round.role === 'generator' ? '🎯' :
178
+ round.role === 'critic' ? '🔍' : '✨';
179
+ output += `\n${roleEmoji} **Round ${round.round}: ${round.nickname}** (${round.role})\n`;
180
+ // Truncate long content for display
181
+ const displayContent = round.content.length > 500
182
+ ? round.content.substring(0, 500) + '...[truncated]'
183
+ : round.content;
184
+ output += `${displayContent}\n`;
185
+ }
186
+ // Final response
187
+ output += `\n═══════════════════════════════════════\n`;
188
+ output += `🏁 **Final Response:**\n`;
189
+ output += `─────────────────────────────────────\n`;
190
+ output += `${result.finalResponse}\n`;
191
+ output += `\n═══════════════════════════════════════\n`;
192
+ output += `📊 ${result.totalIterations} rounds completed\n`;
193
+ return output;
194
+ }
195
+ //# sourceMappingURL=duck-iterate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duck-iterate.js","sourceRoot":"","sources":["../../src/tools/duck-iterate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { ProviderManager } from '../providers/manager.js';
2
+ import { DuckResponse } from '../config/types.js';
3
+ export interface DuckJudgeArgs {
4
+ responses: DuckResponse[];
5
+ judge?: string;
6
+ criteria?: string[];
7
+ persona?: string;
8
+ }
9
+ export declare function duckJudgeTool(providerManager: ProviderManager, args: Record<string, unknown>): Promise<{
10
+ content: {
11
+ type: string;
12
+ text: string;
13
+ }[];
14
+ }>;
15
+ //# sourceMappingURL=duck-judge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duck-judge.d.ts","sourceRoot":"","sources":["../../src/tools/duck-judge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAmB,MAAM,oBAAoB,CAAC;AAGnE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAcD,wBAAsB,aAAa,CACjC,eAAe,EAAE,eAAe,EAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;GAwD9B"}
@@ -0,0 +1,208 @@
1
+ import { logger } from '../utils/logger.js';
2
+ const DEFAULT_CRITERIA = ['accuracy', 'completeness', 'clarity'];
3
+ export async function duckJudgeTool(providerManager, args) {
4
+ const { responses, judge, criteria = DEFAULT_CRITERIA, persona, } = args;
5
+ // Validate inputs
6
+ if (!responses || !Array.isArray(responses) || responses.length === 0) {
7
+ throw new Error('At least one response is required to judge');
8
+ }
9
+ if (responses.length === 1) {
10
+ throw new Error('At least two responses are required for comparison');
11
+ }
12
+ // Determine judge provider
13
+ const judgeProvider = judge || providerManager.getProviderNames()[0];
14
+ if (!judgeProvider) {
15
+ throw new Error('No judge provider available');
16
+ }
17
+ logger.info(`Starting judgment with ${judgeProvider} on ${responses.length} responses`);
18
+ // Build the judgment prompt
19
+ const prompt = buildJudgePrompt(responses, criteria, persona);
20
+ // Get judgment from the judge duck
21
+ const judgeResponse = await providerManager.askDuck(judgeProvider, prompt);
22
+ // Parse the judgment
23
+ const evaluation = parseJudgment(judgeResponse.content, judgeResponse.provider, judgeResponse.nickname, responses, criteria);
24
+ // Format output
25
+ const formattedOutput = formatJudgeResult(evaluation);
26
+ logger.info(`Judgment completed by ${judgeProvider}: #1 is ${evaluation.rankings[0]?.provider || 'unknown'}`);
27
+ return {
28
+ content: [
29
+ {
30
+ type: 'text',
31
+ text: formattedOutput,
32
+ },
33
+ ],
34
+ };
35
+ }
36
+ function buildJudgePrompt(responses, criteria, persona) {
37
+ const criteriaList = criteria.map((c, i) => `${i + 1}. ${c}`).join('\n');
38
+ const responsesText = responses.map((r, i) => `--- Response ${i + 1} (${r.nickname} / ${r.provider}) ---\n${r.content}\n`).join('\n');
39
+ const personaText = persona
40
+ ? `You are a ${persona} evaluating these responses.\n\n`
41
+ : '';
42
+ return `${personaText}You are a judge evaluating ${responses.length} responses to the same prompt.
43
+
44
+ RESPONSES TO EVALUATE:
45
+ ${responsesText}
46
+
47
+ EVALUATION CRITERIA:
48
+ ${criteriaList}
49
+
50
+ INSTRUCTIONS:
51
+ 1. Evaluate each response against ALL criteria
52
+ 2. Assign a score from 0-100 for each response
53
+ 3. Rank responses from best to worst
54
+ 4. Provide a brief justification for each ranking
55
+ 5. Give a final summary
56
+
57
+ Respond with ONLY a JSON object in this exact format:
58
+ {
59
+ "rankings": [
60
+ {"provider": "<provider name>", "score": <0-100>, "justification": "<brief explanation>"},
61
+ {"provider": "<provider name>", "score": <0-100>, "justification": "<brief explanation>"}
62
+ ],
63
+ "criteria_scores": {
64
+ "<provider>": {${criteria.map(c => `"${c}": <0-100>`).join(', ')}}
65
+ },
66
+ "summary": "<overall assessment and recommendation>"
67
+ }
68
+
69
+ IMPORTANT:
70
+ - Rankings must be ordered from highest score to lowest
71
+ - Use the exact provider names from the responses
72
+ - Do NOT include any text before or after the JSON
73
+ - Do NOT use markdown code blocks`;
74
+ }
75
+ function matchProvider(judgeProviderName, originalResponses) {
76
+ const nameLower = judgeProviderName.toLowerCase();
77
+ // Try exact match first
78
+ const exactMatch = originalResponses.find(r => r.provider.toLowerCase() === nameLower);
79
+ if (exactMatch)
80
+ return exactMatch;
81
+ // Try matching by provider name contained in judge's response
82
+ const containsMatch = originalResponses.find(r => nameLower.includes(r.provider.toLowerCase()) ||
83
+ nameLower.includes(r.nickname.toLowerCase()));
84
+ if (containsMatch)
85
+ return containsMatch;
86
+ // Try matching by nickname
87
+ const nicknameMatch = originalResponses.find(r => r.nickname.toLowerCase() === nameLower);
88
+ if (nicknameMatch)
89
+ return nicknameMatch;
90
+ return undefined;
91
+ }
92
+ function parseJudgment(response, judgeProvider, judgeNickname, originalResponses, criteria) {
93
+ const evaluation = {
94
+ judge: judgeProvider,
95
+ judgeNickname: judgeNickname,
96
+ prompt: '', // Will be filled by caller if needed
97
+ criteria,
98
+ rankings: [],
99
+ criteriaScores: {},
100
+ summary: '',
101
+ rawResponse: response,
102
+ };
103
+ try {
104
+ // Try to extract JSON from the response
105
+ const jsonMatch = response.match(/\{[\s\S]*\}/);
106
+ if (!jsonMatch) {
107
+ logger.warn(`No JSON found in judge response from ${judgeProvider}`);
108
+ return createFallbackEvaluation(evaluation, originalResponses, response);
109
+ }
110
+ const parsed = JSON.parse(jsonMatch[0]);
111
+ const matchedProviders = new Set();
112
+ // Parse rankings
113
+ if (Array.isArray(parsed.rankings)) {
114
+ for (const [index, r] of parsed.rankings.entries()) {
115
+ const matched = matchProvider(r.provider, originalResponses);
116
+ if (matched && !matchedProviders.has(matched.provider)) {
117
+ matchedProviders.add(matched.provider);
118
+ evaluation.rankings.push({
119
+ provider: matched.provider,
120
+ nickname: matched.nickname,
121
+ rank: index + 1,
122
+ score: typeof r.score === 'number' ? Math.max(0, Math.min(100, r.score)) : 0,
123
+ justification: r.justification?.toString() || '',
124
+ });
125
+ }
126
+ }
127
+ }
128
+ // Parse criteria scores
129
+ if (parsed.criteria_scores && typeof parsed.criteria_scores === 'object') {
130
+ evaluation.criteriaScores = parsed.criteria_scores;
131
+ }
132
+ // Parse summary
133
+ if (parsed.summary) {
134
+ evaluation.summary = parsed.summary.toString();
135
+ }
136
+ }
137
+ catch (error) {
138
+ logger.warn(`Failed to parse JSON judgment from ${judgeProvider}:`, error);
139
+ return createFallbackEvaluation(evaluation, originalResponses, response);
140
+ }
141
+ // Ensure all original responses are represented
142
+ const rankedProviders = new Set(evaluation.rankings.map(r => r.provider));
143
+ for (const resp of originalResponses) {
144
+ if (!rankedProviders.has(resp.provider)) {
145
+ evaluation.rankings.push({
146
+ provider: resp.provider,
147
+ nickname: resp.nickname,
148
+ rank: evaluation.rankings.length + 1,
149
+ score: 0,
150
+ justification: 'Not evaluated by judge',
151
+ });
152
+ }
153
+ }
154
+ return evaluation;
155
+ }
156
+ function createFallbackEvaluation(evaluation, originalResponses, rawResponse) {
157
+ // Create a basic evaluation when parsing fails
158
+ evaluation.rankings = originalResponses.map((r, index) => ({
159
+ provider: r.provider,
160
+ nickname: r.nickname,
161
+ rank: index + 1,
162
+ score: 50,
163
+ justification: 'Unable to parse judge response',
164
+ }));
165
+ evaluation.summary = `Judge evaluation parsing failed. Raw response available for review.`;
166
+ evaluation.rawResponse = rawResponse;
167
+ return evaluation;
168
+ }
169
+ function formatJudgeResult(evaluation) {
170
+ let output = `⚖️ **Judge Evaluation**\n`;
171
+ output += `═══════════════════════════════════════\n\n`;
172
+ output += `**Judge:** ${evaluation.judgeNickname} (${evaluation.judge})\n`;
173
+ output += `**Criteria:** ${evaluation.criteria.join(', ')}\n\n`;
174
+ // Rankings
175
+ output += `**Rankings:**\n`;
176
+ output += `─────────────────────────────────────\n`;
177
+ for (const ranking of evaluation.rankings) {
178
+ const medal = ranking.rank === 1 ? '🥇' : ranking.rank === 2 ? '🥈' : ranking.rank === 3 ? '🥉' : ' ';
179
+ const bar = '█'.repeat(Math.floor(ranking.score / 10));
180
+ const emptyBar = '░'.repeat(10 - Math.floor(ranking.score / 10));
181
+ output += `${medal} **#${ranking.rank} ${ranking.nickname}** (${ranking.provider})\n`;
182
+ output += ` Score: ${bar}${emptyBar} ${ranking.score}/100\n`;
183
+ output += ` 💭 "${ranking.justification}"\n\n`;
184
+ }
185
+ // Criteria breakdown if available
186
+ if (Object.keys(evaluation.criteriaScores).length > 0) {
187
+ output += `**Criteria Breakdown:**\n`;
188
+ output += `─────────────────────────────────────\n`;
189
+ for (const [provider, scores] of Object.entries(evaluation.criteriaScores)) {
190
+ output += `📊 **${provider}:**\n`;
191
+ for (const [criterion, score] of Object.entries(scores)) {
192
+ const criterionScore = typeof score === 'number' ? score : 0;
193
+ output += ` • ${criterion}: ${criterionScore}/100\n`;
194
+ }
195
+ output += `\n`;
196
+ }
197
+ }
198
+ // Summary
199
+ if (evaluation.summary) {
200
+ output += `**Summary:**\n`;
201
+ output += `─────────────────────────────────────\n`;
202
+ output += `${evaluation.summary}\n\n`;
203
+ }
204
+ output += `═══════════════════════════════════════\n`;
205
+ output += `📋 Evaluated ${evaluation.rankings.length} responses\n`;
206
+ return output;
207
+ }
208
+ //# sourceMappingURL=duck-judge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duck-judge.js","sourceRoot":"","sources":["../../src/tools/duck-judge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import { ProviderManager } from '../providers/manager.js';
2
+ export interface DuckVoteArgs {
3
+ question: string;
4
+ options: string[];
5
+ voters?: string[];
6
+ require_reasoning?: boolean;
7
+ }
8
+ export declare function duckVoteTool(providerManager: ProviderManager, args: Record<string, unknown>): Promise<{
9
+ content: {
10
+ type: string;
11
+ text: string;
12
+ }[];
13
+ }>;
14
+ //# sourceMappingURL=duck-vote.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duck-vote.d.ts","sourceRoot":"","sources":["../../src/tools/duck-vote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAK1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,wBAAsB,YAAY,CAChC,eAAe,EAAE,eAAe,EAChC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;;;GAwE9B"}
@@ -0,0 +1,46 @@
1
+ import { ConsensusService } from '../services/consensus.js';
2
+ import { logger } from '../utils/logger.js';
3
+ export async function duckVoteTool(providerManager, args) {
4
+ const { question, options, voters, require_reasoning = true, } = args;
5
+ // Validate inputs
6
+ if (!question || typeof question !== 'string') {
7
+ throw new Error('Question is required');
8
+ }
9
+ if (!options || !Array.isArray(options) || options.length < 2) {
10
+ throw new Error('At least 2 options are required');
11
+ }
12
+ if (options.length > 10) {
13
+ throw new Error('Maximum 10 options allowed');
14
+ }
15
+ // Get voters (all providers if not specified)
16
+ const voterNames = voters && voters.length > 0
17
+ ? voters
18
+ : providerManager.getProviderNames();
19
+ if (voterNames.length === 0) {
20
+ throw new Error('No voters available');
21
+ }
22
+ logger.info(`Starting vote with ${voterNames.length} voters on: "${question}"`);
23
+ const consensusService = new ConsensusService();
24
+ const votePrompt = consensusService.buildVotePrompt(question, options, require_reasoning);
25
+ // Get votes from all ducks in parallel
26
+ const responses = await providerManager.compareDucks(votePrompt, voterNames);
27
+ // Parse votes
28
+ const votes = responses.map(response => {
29
+ return consensusService.parseVote(response.content, response.provider, response.nickname, options);
30
+ });
31
+ // Aggregate results
32
+ const aggregatedResult = consensusService.aggregateVotes(question, options, votes);
33
+ // Format output
34
+ const formattedOutput = consensusService.formatVoteResult(aggregatedResult);
35
+ logger.info(`Vote completed: ${aggregatedResult.consensusLevel} consensus, ` +
36
+ `winner: ${aggregatedResult.winner || 'none'}`);
37
+ return {
38
+ content: [
39
+ {
40
+ type: 'text',
41
+ text: formattedOutput,
42
+ },
43
+ ],
44
+ };
45
+ }
46
+ //# sourceMappingURL=duck-vote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duck-vote.js","sourceRoot":"","sources":["../../src/tools/duck-vote.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAE5D,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAS5C,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,eAAgC,EAChC,IAA6B;IAE7B,MAAM,EACJ,QAAQ,EACR,OAAO,EACP,MAAM,EACN,iBAAiB,GAAG,IAAI,GACzB,GAAG,IAA+B,CAAC;IAEpC,kBAAkB;IAClB,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9D,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IAED,8CAA8C;IAC9C,MAAM,UAAU,GAAG,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAC5C,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,eAAe,CAAC,gBAAgB,EAAE,CAAC;IAEvC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,sBAAsB,UAAU,CAAC,MAAM,gBAAgB,QAAQ,GAAG,CAAC,CAAC;IAEhF,MAAM,gBAAgB,GAAG,IAAI,gBAAgB,EAAE,CAAC;IAChD,MAAM,UAAU,GAAG,gBAAgB,CAAC,eAAe,CACjD,QAAQ,EACR,OAAO,EACP,iBAAiB,CAClB,CAAC;IAEF,uCAAuC;IACvC,MAAM,SAAS,GAAG,MAAM,eAAe,CAAC,YAAY,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAE7E,cAAc;IACd,MAAM,KAAK,GAAiB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE;QACnD,OAAO,gBAAgB,CAAC,SAAS,CAC/B,QAAQ,CAAC,OAAO,EAChB,QAAQ,CAAC,QAAQ,EACjB,QAAQ,CAAC,QAAQ,EACjB,OAAO,CACR,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,oBAAoB;IACpB,MAAM,gBAAgB,GAAG,gBAAgB,CAAC,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAEnF,gBAAgB;IAChB,MAAM,eAAe,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC;IAE5E,MAAM,CAAC,IAAI,CACT,mBAAmB,gBAAgB,CAAC,cAAc,cAAc;QAChE,WAAW,gBAAgB,CAAC,MAAM,IAAI,MAAM,EAAE,CAC/C,CAAC;IAEF,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,eAAe;aACtB;SACF;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-rubber-duck",
3
- "version": "1.2.4",
3
+ "version": "1.3.0",
4
4
  "description": "An MCP server that bridges to multiple OpenAI-compatible LLMs - your AI rubber duck debugging panel",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -86,4 +86,96 @@ export interface DuckResponse {
86
86
  };
87
87
  latency: number;
88
88
  cached: boolean;
89
+ }
90
+
91
+ // Consensus & Voting Types
92
+ export interface VoteResult {
93
+ voter: string;
94
+ nickname: string;
95
+ choice: string;
96
+ confidence: number;
97
+ reasoning: string;
98
+ rawResponse: string;
99
+ }
100
+
101
+ export interface AggregatedVote {
102
+ question: string;
103
+ options: string[];
104
+ winner: string | null;
105
+ isTie: boolean;
106
+ tally: Record<string, number>;
107
+ confidenceByOption: Record<string, number>;
108
+ votes: VoteResult[];
109
+ totalVoters: number;
110
+ validVotes: number;
111
+ consensusLevel: 'unanimous' | 'majority' | 'plurality' | 'split' | 'none';
112
+ }
113
+
114
+ // Judge Evaluation Types
115
+ export interface JudgeRanking {
116
+ provider: string;
117
+ nickname: string;
118
+ rank: number;
119
+ score: number;
120
+ justification: string;
121
+ }
122
+
123
+ export interface JudgeEvaluation {
124
+ judge: string;
125
+ judgeNickname: string;
126
+ prompt: string;
127
+ criteria: string[];
128
+ rankings: JudgeRanking[];
129
+ criteriaScores: Record<string, Record<string, number>>;
130
+ summary: string;
131
+ rawResponse: string;
132
+ }
133
+
134
+ // Iteration Types
135
+ export interface IterationRound {
136
+ round: number;
137
+ provider: string;
138
+ nickname: string;
139
+ role: 'generator' | 'critic' | 'refiner';
140
+ content: string;
141
+ timestamp: Date;
142
+ }
143
+
144
+ export interface IterationResult {
145
+ prompt: string;
146
+ mode: 'refine' | 'critique-improve';
147
+ providers: [string, string];
148
+ rounds: IterationRound[];
149
+ finalResponse: string;
150
+ totalIterations: number;
151
+ converged: boolean;
152
+ }
153
+
154
+ // Debate Types
155
+ export type DebateFormat = 'oxford' | 'socratic' | 'adversarial';
156
+ export type DebatePosition = 'pro' | 'con' | 'neutral';
157
+
158
+ export interface DebateParticipant {
159
+ provider: string;
160
+ nickname: string;
161
+ position: DebatePosition;
162
+ }
163
+
164
+ export interface DebateArgument {
165
+ round: number;
166
+ provider: string;
167
+ nickname: string;
168
+ position: DebatePosition;
169
+ content: string;
170
+ timestamp: Date;
171
+ }
172
+
173
+ export interface DebateResult {
174
+ topic: string;
175
+ format: DebateFormat;
176
+ participants: DebateParticipant[];
177
+ rounds: DebateArgument[][];
178
+ synthesis: string;
179
+ synthesizer: string;
180
+ totalRounds: number;
89
181
  }