@thispointon/kondi-chat 0.1.2
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 +556 -0
- package/bin/kondi-chat +56 -0
- package/bin/kondi-chat.js +72 -0
- package/package.json +55 -0
- package/scripts/demo.tape +49 -0
- package/scripts/postinstall.cjs +103 -0
- package/src/audit/analytics.ts +261 -0
- package/src/audit/ledger.ts +253 -0
- package/src/audit/telemetry.ts +165 -0
- package/src/cli/backend.ts +675 -0
- package/src/cli/commands.ts +419 -0
- package/src/cli/help.ts +182 -0
- package/src/cli/submit-helpers.ts +159 -0
- package/src/cli/submit.ts +539 -0
- package/src/cli/wizard.ts +121 -0
- package/src/context/bootstrap.ts +138 -0
- package/src/context/budget.ts +100 -0
- package/src/context/manager.ts +666 -0
- package/src/context/memory.ts +160 -0
- package/src/context/preflight.ts +176 -0
- package/src/context/project-brain.ts +101 -0
- package/src/context/receipts.ts +108 -0
- package/src/context/skills.ts +154 -0
- package/src/context/symbol-index.ts +240 -0
- package/src/council/profiles.ts +137 -0
- package/src/council/tool.ts +138 -0
- package/src/council-engine/cli/council-artifacts.ts +230 -0
- package/src/council-engine/cli/council-config.ts +178 -0
- package/src/council-engine/cli/council-session-export.ts +116 -0
- package/src/council-engine/cli/kondi.ts +98 -0
- package/src/council-engine/cli/llm-caller.ts +229 -0
- package/src/council-engine/cli/localStorage-shim.ts +119 -0
- package/src/council-engine/cli/node-platform.ts +68 -0
- package/src/council-engine/cli/run-council.ts +481 -0
- package/src/council-engine/cli/run-pipeline.ts +772 -0
- package/src/council-engine/cli/session-export.ts +153 -0
- package/src/council-engine/configs/councils/analysis.json +101 -0
- package/src/council-engine/configs/councils/code-planning.json +86 -0
- package/src/council-engine/configs/councils/coding.json +89 -0
- package/src/council-engine/configs/councils/debate.json +97 -0
- package/src/council-engine/configs/councils/solo-claude.json +34 -0
- package/src/council-engine/configs/councils/solo-gpt.json +34 -0
- package/src/council-engine/council/coding-orchestrator.ts +1205 -0
- package/src/council-engine/council/context-bootstrap.ts +147 -0
- package/src/council-engine/council/context-inspection.ts +42 -0
- package/src/council-engine/council/context-store.ts +763 -0
- package/src/council-engine/council/deliberation-orchestrator.ts +2762 -0
- package/src/council-engine/council/factory.ts +164 -0
- package/src/council-engine/council/index.ts +201 -0
- package/src/council-engine/council/ledger-store.ts +438 -0
- package/src/council-engine/council/prompts.ts +1689 -0
- package/src/council-engine/council/storage-cleanup.ts +164 -0
- package/src/council-engine/council/store.ts +1110 -0
- package/src/council-engine/council/synthesis.ts +291 -0
- package/src/council-engine/council/types.ts +845 -0
- package/src/council-engine/council/validation.ts +613 -0
- package/src/council-engine/pipeline/build-detect.ts +73 -0
- package/src/council-engine/pipeline/executor.ts +1048 -0
- package/src/council-engine/pipeline/index.ts +9 -0
- package/src/council-engine/pipeline/install-detect.ts +84 -0
- package/src/council-engine/pipeline/memory-store.ts +182 -0
- package/src/council-engine/pipeline/output-parsers.ts +146 -0
- package/src/council-engine/pipeline/run-output.ts +149 -0
- package/src/council-engine/pipeline/session-import.ts +177 -0
- package/src/council-engine/pipeline/store.ts +753 -0
- package/src/council-engine/pipeline/test-detect.ts +82 -0
- package/src/council-engine/pipeline/types.ts +401 -0
- package/src/council-engine/services/deliberationSummary.ts +114 -0
- package/src/council-engine/tsconfig.json +16 -0
- package/src/council-engine/types/mcp.ts +122 -0
- package/src/council-engine/utils/filterTools.ts +73 -0
- package/src/engine/apply.ts +238 -0
- package/src/engine/checkpoints.ts +237 -0
- package/src/engine/consultants.ts +347 -0
- package/src/engine/diff.ts +171 -0
- package/src/engine/errors.ts +102 -0
- package/src/engine/git-tools.ts +246 -0
- package/src/engine/hooks.ts +181 -0
- package/src/engine/loop-guard.ts +155 -0
- package/src/engine/permissions.ts +293 -0
- package/src/engine/pipeline.ts +376 -0
- package/src/engine/sub-agents.ts +133 -0
- package/src/engine/task-card.ts +185 -0
- package/src/engine/task-router.ts +256 -0
- package/src/engine/task-store.ts +86 -0
- package/src/engine/tools.ts +783 -0
- package/src/engine/verify.ts +111 -0
- package/src/mcp/client.ts +225 -0
- package/src/mcp/config.ts +120 -0
- package/src/mcp/tool-manager.ts +192 -0
- package/src/mcp/types.ts +61 -0
- package/src/providers/llm-caller.ts +943 -0
- package/src/providers/rate-limiter.ts +238 -0
- package/src/router/NOTES.md +28 -0
- package/src/router/collector.ts +474 -0
- package/src/router/embeddings.ts +286 -0
- package/src/router/index.ts +299 -0
- package/src/router/intent-router.ts +225 -0
- package/src/router/nn-router.ts +205 -0
- package/src/router/profiles.ts +309 -0
- package/src/router/registry.ts +565 -0
- package/src/router/rules.ts +274 -0
- package/src/router/train.py +408 -0
- package/src/session/store.ts +211 -0
- package/src/test-utils/mock-llm.ts +39 -0
- package/src/types.ts +322 -0
- package/src/web/manager.ts +311 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Council: Synthesis Generation
|
|
3
|
+
* Generate summaries, resolutions, and consensus analysis
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { Council, Persona, Resolution, CouncilMessage } from './types';
|
|
7
|
+
import { buildSynthesisPrompt } from './prompts';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Default synthesis model (can be overridden)
|
|
11
|
+
*/
|
|
12
|
+
const DEFAULT_SYNTHESIS_MODEL = 'claude-sonnet-4-20250514';
|
|
13
|
+
const DEFAULT_SYNTHESIS_PROVIDER = 'anthropic';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Parse synthesis response from LLM
|
|
17
|
+
*/
|
|
18
|
+
export function parseSynthesisResponse(response: string): Resolution | null {
|
|
19
|
+
try {
|
|
20
|
+
// Try to extract JSON from the response
|
|
21
|
+
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
22
|
+
if (!jsonMatch) {
|
|
23
|
+
console.error('[Synthesis] No JSON found in response');
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const data = JSON.parse(jsonMatch[0]);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
summary: data.summary || '',
|
|
31
|
+
consensusLevel: typeof data.consensusLevel === 'number' ? data.consensusLevel : 0.5,
|
|
32
|
+
keyDecisions: Array.isArray(data.keyDecisions) ? data.keyDecisions : [],
|
|
33
|
+
agreements: Array.isArray(data.agreements) ? data.agreements : [],
|
|
34
|
+
tensions: Array.isArray(data.tensions) ? data.tensions : [],
|
|
35
|
+
dissent: Array.isArray(data.dissent) ? data.dissent : [],
|
|
36
|
+
nextSteps: Array.isArray(data.nextSteps) ? data.nextSteps : [],
|
|
37
|
+
generatedBy: 'system',
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('[Synthesis] Failed to parse response:', error);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Calculate consensus level from message sentiments
|
|
47
|
+
*/
|
|
48
|
+
export function calculateConsensus(council: Council): number {
|
|
49
|
+
const personaMessages = council.messages.filter((m) => m.speakerType === 'persona');
|
|
50
|
+
if (personaMessages.length === 0) return 0;
|
|
51
|
+
|
|
52
|
+
// Count agreements and disagreements
|
|
53
|
+
let agreements = 0;
|
|
54
|
+
let disagreements = 0;
|
|
55
|
+
|
|
56
|
+
for (const message of personaMessages) {
|
|
57
|
+
if (message.sentiment === 'agree') agreements++;
|
|
58
|
+
else if (message.sentiment === 'disagree') disagreements++;
|
|
59
|
+
else if (message.sentiment === 'partial') {
|
|
60
|
+
agreements += 0.5;
|
|
61
|
+
disagreements += 0.5;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const total = agreements + disagreements;
|
|
66
|
+
if (total === 0) return 0.5; // Neutral if no explicit sentiments
|
|
67
|
+
|
|
68
|
+
return agreements / total;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Identify key claims made in the discussion
|
|
73
|
+
*/
|
|
74
|
+
export function extractKeyClaims(council: Council): string[] {
|
|
75
|
+
const claims: string[] = [];
|
|
76
|
+
|
|
77
|
+
for (const message of council.messages) {
|
|
78
|
+
if (message.claims) {
|
|
79
|
+
for (const claim of message.claims) {
|
|
80
|
+
if (claim.type === 'assertion' || claim.type === 'proposal') {
|
|
81
|
+
claims.push(claim.text);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return claims;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Identify points of agreement across personas
|
|
92
|
+
*/
|
|
93
|
+
export function findAgreements(council: Council): string[] {
|
|
94
|
+
// Look for messages where sentiment is 'agree' and extract what they agree on
|
|
95
|
+
const agreements: string[] = [];
|
|
96
|
+
|
|
97
|
+
for (let i = 1; i < council.messages.length; i++) {
|
|
98
|
+
const message = council.messages[i];
|
|
99
|
+
if (message.sentiment === 'agree' && message.replyingTo) {
|
|
100
|
+
const original = council.messages.find((m) => m.id === message.replyingTo);
|
|
101
|
+
if (original) {
|
|
102
|
+
// Extract first sentence as the point of agreement
|
|
103
|
+
const firstSentence = original.content.split(/[.!?]/)[0];
|
|
104
|
+
if (firstSentence && firstSentence.length > 10) {
|
|
105
|
+
agreements.push(firstSentence.trim());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return [...new Set(agreements)];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Identify key tensions/disagreements
|
|
116
|
+
*/
|
|
117
|
+
export function findTensions(council: Council): Array<{ between: [string, string]; topic: string }> {
|
|
118
|
+
const tensions: Array<{ between: [string, string]; topic: string }> = [];
|
|
119
|
+
|
|
120
|
+
for (let i = 1; i < council.messages.length; i++) {
|
|
121
|
+
const message = council.messages[i];
|
|
122
|
+
if (message.sentiment === 'disagree' && message.replyingTo) {
|
|
123
|
+
const original = council.messages.find((m) => m.id === message.replyingTo);
|
|
124
|
+
if (original && original.speakerType === 'persona') {
|
|
125
|
+
const speaker1 = council.personas.find((p) => p.id === original.speakerId);
|
|
126
|
+
const speaker2 = council.personas.find((p) => p.id === message.speakerId);
|
|
127
|
+
|
|
128
|
+
if (speaker1 && speaker2) {
|
|
129
|
+
tensions.push({
|
|
130
|
+
between: [speaker1.name, speaker2.name],
|
|
131
|
+
topic: original.content.slice(0, 100),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return tensions;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Generate position summary for each persona
|
|
143
|
+
*/
|
|
144
|
+
export function summarizePositions(
|
|
145
|
+
council: Council
|
|
146
|
+
): Array<{ persona: string; position: string; confidence: number }> {
|
|
147
|
+
const positions: Array<{ persona: string; position: string; confidence: number }> = [];
|
|
148
|
+
|
|
149
|
+
for (const persona of council.personas) {
|
|
150
|
+
const personaMessages = council.messages
|
|
151
|
+
.filter((m) => m.speakerId === persona.id)
|
|
152
|
+
.slice(-3);
|
|
153
|
+
|
|
154
|
+
if (personaMessages.length === 0) {
|
|
155
|
+
positions.push({
|
|
156
|
+
persona: persona.name,
|
|
157
|
+
position: 'Has not spoken yet',
|
|
158
|
+
confidence: 0,
|
|
159
|
+
});
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Use the last message's stance or summarize
|
|
164
|
+
const lastMessage = personaMessages[personaMessages.length - 1];
|
|
165
|
+
positions.push({
|
|
166
|
+
persona: persona.name,
|
|
167
|
+
position: lastMessage.stance || lastMessage.content.slice(0, 150),
|
|
168
|
+
confidence: lastMessage.confidence || 0.5,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return positions;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Configuration for synthesis generation
|
|
177
|
+
*/
|
|
178
|
+
export interface SynthesisConfig {
|
|
179
|
+
synthesizer?: 'system' | Persona;
|
|
180
|
+
model?: string;
|
|
181
|
+
provider?: string;
|
|
182
|
+
includeVotes?: boolean;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Generate synthesis request parameters
|
|
187
|
+
* Returns the prompt and model configuration for the caller to execute
|
|
188
|
+
*/
|
|
189
|
+
export function prepareSynthesisRequest(
|
|
190
|
+
council: Council,
|
|
191
|
+
config?: SynthesisConfig
|
|
192
|
+
): {
|
|
193
|
+
prompt: string;
|
|
194
|
+
model: string;
|
|
195
|
+
provider: string;
|
|
196
|
+
synthesizerId: string;
|
|
197
|
+
} {
|
|
198
|
+
const synthesizer = config?.synthesizer;
|
|
199
|
+
const isPersona = synthesizer && synthesizer !== 'system';
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
prompt: buildSynthesisPrompt(council),
|
|
203
|
+
model: isPersona ? (synthesizer as Persona).model : (config?.model || DEFAULT_SYNTHESIS_MODEL),
|
|
204
|
+
provider: isPersona ? (synthesizer as Persona).provider : (config?.provider || DEFAULT_SYNTHESIS_PROVIDER),
|
|
205
|
+
synthesizerId: isPersona ? (synthesizer as Persona).id : 'system',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Quick consensus check (heuristic-based, no LLM call)
|
|
211
|
+
*/
|
|
212
|
+
export function quickConsensusCheck(council: Council): {
|
|
213
|
+
level: number;
|
|
214
|
+
status: 'converging' | 'diverging' | 'stable';
|
|
215
|
+
recommendation: string;
|
|
216
|
+
} {
|
|
217
|
+
const recentMessages = council.messages.slice(-10);
|
|
218
|
+
const personaMessages = recentMessages.filter((m) => m.speakerType === 'persona');
|
|
219
|
+
|
|
220
|
+
if (personaMessages.length < 3) {
|
|
221
|
+
return {
|
|
222
|
+
level: 0.5,
|
|
223
|
+
status: 'stable',
|
|
224
|
+
recommendation: 'Continue discussion to gather more perspectives',
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Count sentiment trends
|
|
229
|
+
let recentAgree = 0;
|
|
230
|
+
let recentDisagree = 0;
|
|
231
|
+
|
|
232
|
+
for (const msg of personaMessages.slice(-5)) {
|
|
233
|
+
if (msg.sentiment === 'agree') recentAgree++;
|
|
234
|
+
if (msg.sentiment === 'disagree') recentDisagree++;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const level = recentAgree / (recentAgree + recentDisagree || 1);
|
|
238
|
+
|
|
239
|
+
if (recentAgree > recentDisagree * 2) {
|
|
240
|
+
return {
|
|
241
|
+
level,
|
|
242
|
+
status: 'converging',
|
|
243
|
+
recommendation: 'Consider generating a resolution',
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
if (recentDisagree > recentAgree * 2) {
|
|
248
|
+
return {
|
|
249
|
+
level,
|
|
250
|
+
status: 'diverging',
|
|
251
|
+
recommendation: 'Consider a synthesis to identify common ground',
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
level,
|
|
257
|
+
status: 'stable',
|
|
258
|
+
recommendation: 'Continue discussion or ask specific questions',
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Create a round summary message
|
|
264
|
+
*/
|
|
265
|
+
export function createRoundSummary(
|
|
266
|
+
council: Council,
|
|
267
|
+
roundNumber: number
|
|
268
|
+
): CouncilMessage {
|
|
269
|
+
const consensus = quickConsensusCheck(council);
|
|
270
|
+
const positions = summarizePositions(council);
|
|
271
|
+
|
|
272
|
+
const positionSummary = positions
|
|
273
|
+
.map((p) => `• **${p.persona}**: ${p.position.slice(0, 100)}${p.position.length > 100 ? '...' : ''}`)
|
|
274
|
+
.join('\n');
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
id: crypto.randomUUID(),
|
|
278
|
+
timestamp: new Date().toISOString(),
|
|
279
|
+
speakerId: 'system',
|
|
280
|
+
speakerType: 'system',
|
|
281
|
+
content: `**Round ${roundNumber} Summary**
|
|
282
|
+
|
|
283
|
+
${positionSummary}
|
|
284
|
+
|
|
285
|
+
Consensus level: ${Math.round(consensus.level * 100)}%
|
|
286
|
+
Status: ${consensus.status}
|
|
287
|
+
${consensus.recommendation}`,
|
|
288
|
+
tokensUsed: 0,
|
|
289
|
+
latencyMs: 0,
|
|
290
|
+
};
|
|
291
|
+
}
|