@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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +556 -0
  3. package/bin/kondi-chat +56 -0
  4. package/bin/kondi-chat.js +72 -0
  5. package/package.json +55 -0
  6. package/scripts/demo.tape +49 -0
  7. package/scripts/postinstall.cjs +103 -0
  8. package/src/audit/analytics.ts +261 -0
  9. package/src/audit/ledger.ts +253 -0
  10. package/src/audit/telemetry.ts +165 -0
  11. package/src/cli/backend.ts +675 -0
  12. package/src/cli/commands.ts +419 -0
  13. package/src/cli/help.ts +182 -0
  14. package/src/cli/submit-helpers.ts +159 -0
  15. package/src/cli/submit.ts +539 -0
  16. package/src/cli/wizard.ts +121 -0
  17. package/src/context/bootstrap.ts +138 -0
  18. package/src/context/budget.ts +100 -0
  19. package/src/context/manager.ts +666 -0
  20. package/src/context/memory.ts +160 -0
  21. package/src/context/preflight.ts +176 -0
  22. package/src/context/project-brain.ts +101 -0
  23. package/src/context/receipts.ts +108 -0
  24. package/src/context/skills.ts +154 -0
  25. package/src/context/symbol-index.ts +240 -0
  26. package/src/council/profiles.ts +137 -0
  27. package/src/council/tool.ts +138 -0
  28. package/src/council-engine/cli/council-artifacts.ts +230 -0
  29. package/src/council-engine/cli/council-config.ts +178 -0
  30. package/src/council-engine/cli/council-session-export.ts +116 -0
  31. package/src/council-engine/cli/kondi.ts +98 -0
  32. package/src/council-engine/cli/llm-caller.ts +229 -0
  33. package/src/council-engine/cli/localStorage-shim.ts +119 -0
  34. package/src/council-engine/cli/node-platform.ts +68 -0
  35. package/src/council-engine/cli/run-council.ts +481 -0
  36. package/src/council-engine/cli/run-pipeline.ts +772 -0
  37. package/src/council-engine/cli/session-export.ts +153 -0
  38. package/src/council-engine/configs/councils/analysis.json +101 -0
  39. package/src/council-engine/configs/councils/code-planning.json +86 -0
  40. package/src/council-engine/configs/councils/coding.json +89 -0
  41. package/src/council-engine/configs/councils/debate.json +97 -0
  42. package/src/council-engine/configs/councils/solo-claude.json +34 -0
  43. package/src/council-engine/configs/councils/solo-gpt.json +34 -0
  44. package/src/council-engine/council/coding-orchestrator.ts +1205 -0
  45. package/src/council-engine/council/context-bootstrap.ts +147 -0
  46. package/src/council-engine/council/context-inspection.ts +42 -0
  47. package/src/council-engine/council/context-store.ts +763 -0
  48. package/src/council-engine/council/deliberation-orchestrator.ts +2762 -0
  49. package/src/council-engine/council/factory.ts +164 -0
  50. package/src/council-engine/council/index.ts +201 -0
  51. package/src/council-engine/council/ledger-store.ts +438 -0
  52. package/src/council-engine/council/prompts.ts +1689 -0
  53. package/src/council-engine/council/storage-cleanup.ts +164 -0
  54. package/src/council-engine/council/store.ts +1110 -0
  55. package/src/council-engine/council/synthesis.ts +291 -0
  56. package/src/council-engine/council/types.ts +845 -0
  57. package/src/council-engine/council/validation.ts +613 -0
  58. package/src/council-engine/pipeline/build-detect.ts +73 -0
  59. package/src/council-engine/pipeline/executor.ts +1048 -0
  60. package/src/council-engine/pipeline/index.ts +9 -0
  61. package/src/council-engine/pipeline/install-detect.ts +84 -0
  62. package/src/council-engine/pipeline/memory-store.ts +182 -0
  63. package/src/council-engine/pipeline/output-parsers.ts +146 -0
  64. package/src/council-engine/pipeline/run-output.ts +149 -0
  65. package/src/council-engine/pipeline/session-import.ts +177 -0
  66. package/src/council-engine/pipeline/store.ts +753 -0
  67. package/src/council-engine/pipeline/test-detect.ts +82 -0
  68. package/src/council-engine/pipeline/types.ts +401 -0
  69. package/src/council-engine/services/deliberationSummary.ts +114 -0
  70. package/src/council-engine/tsconfig.json +16 -0
  71. package/src/council-engine/types/mcp.ts +122 -0
  72. package/src/council-engine/utils/filterTools.ts +73 -0
  73. package/src/engine/apply.ts +238 -0
  74. package/src/engine/checkpoints.ts +237 -0
  75. package/src/engine/consultants.ts +347 -0
  76. package/src/engine/diff.ts +171 -0
  77. package/src/engine/errors.ts +102 -0
  78. package/src/engine/git-tools.ts +246 -0
  79. package/src/engine/hooks.ts +181 -0
  80. package/src/engine/loop-guard.ts +155 -0
  81. package/src/engine/permissions.ts +293 -0
  82. package/src/engine/pipeline.ts +376 -0
  83. package/src/engine/sub-agents.ts +133 -0
  84. package/src/engine/task-card.ts +185 -0
  85. package/src/engine/task-router.ts +256 -0
  86. package/src/engine/task-store.ts +86 -0
  87. package/src/engine/tools.ts +783 -0
  88. package/src/engine/verify.ts +111 -0
  89. package/src/mcp/client.ts +225 -0
  90. package/src/mcp/config.ts +120 -0
  91. package/src/mcp/tool-manager.ts +192 -0
  92. package/src/mcp/types.ts +61 -0
  93. package/src/providers/llm-caller.ts +943 -0
  94. package/src/providers/rate-limiter.ts +238 -0
  95. package/src/router/NOTES.md +28 -0
  96. package/src/router/collector.ts +474 -0
  97. package/src/router/embeddings.ts +286 -0
  98. package/src/router/index.ts +299 -0
  99. package/src/router/intent-router.ts +225 -0
  100. package/src/router/nn-router.ts +205 -0
  101. package/src/router/profiles.ts +309 -0
  102. package/src/router/registry.ts +565 -0
  103. package/src/router/rules.ts +274 -0
  104. package/src/router/train.py +408 -0
  105. package/src/session/store.ts +211 -0
  106. package/src/test-utils/mock-llm.ts +39 -0
  107. package/src/types.ts +322 -0
  108. package/src/web/manager.ts +311 -0
@@ -0,0 +1,438 @@
1
+ /**
2
+ * Council: Ledger Store
3
+ * Append-only, chunked storage for deliberation audit trail
4
+ *
5
+ * Storage layout:
6
+ * - ledger-index-{councilId}: LedgerIndex (entry count, chunk boundaries, total tokens)
7
+ * - ledger-chunk-{councilId}-{n}: LedgerEntry[] (chunk of entries)
8
+ */
9
+
10
+ import type { LedgerEntry, LedgerEntryType, LedgerIndex, DeliberationPhase } from './types';
11
+ import { councilDataStore } from './storage-cleanup';
12
+
13
+ const LEDGER_INDEX_PREFIX = 'ledger-index-';
14
+ const LEDGER_CHUNK_PREFIX = 'ledger-chunk-';
15
+ const ENTRIES_PER_CHUNK = 20;
16
+
17
+ // ============================================================================
18
+ // Ledger Index Operations
19
+ // ============================================================================
20
+
21
+ function getLedgerIndexKey(councilId: string): string {
22
+ return `${LEDGER_INDEX_PREFIX}${councilId}`;
23
+ }
24
+
25
+ function getLedgerChunkKey(councilId: string, chunkIndex: number): string {
26
+ return `${LEDGER_CHUNK_PREFIX}${councilId}-${chunkIndex}`;
27
+ }
28
+
29
+ function loadLedgerIndex(councilId: string): LedgerIndex {
30
+ try {
31
+ const key = getLedgerIndexKey(councilId);
32
+ const raw = councilDataStore.getItem(key);
33
+ if (!raw) {
34
+ return createEmptyIndex(councilId);
35
+ }
36
+ return JSON.parse(raw) as LedgerIndex;
37
+ } catch (error) {
38
+ console.error('[LedgerStore] Failed to load index:', error);
39
+ return createEmptyIndex(councilId);
40
+ }
41
+ }
42
+
43
+ function saveLedgerIndex(index: LedgerIndex): void {
44
+ const key = getLedgerIndexKey(index.councilId);
45
+ index.lastUpdated = new Date().toISOString();
46
+ councilDataStore.setItem(key, JSON.stringify(index));
47
+ }
48
+
49
+ function createEmptyIndex(councilId: string): LedgerIndex {
50
+ return {
51
+ councilId,
52
+ entryCount: 0,
53
+ chunkCount: 0,
54
+ chunkBoundaries: [],
55
+ totalTokens: 0,
56
+ lastUpdated: new Date().toISOString(),
57
+ };
58
+ }
59
+
60
+ // ============================================================================
61
+ // Chunk Operations
62
+ // ============================================================================
63
+
64
+ function loadChunk(councilId: string, chunkIndex: number): LedgerEntry[] {
65
+ try {
66
+ const key = getLedgerChunkKey(councilId, chunkIndex);
67
+ const raw = councilDataStore.getItem(key);
68
+ if (!raw) {
69
+ return [];
70
+ }
71
+ return JSON.parse(raw) as LedgerEntry[];
72
+ } catch (error) {
73
+ console.error('[LedgerStore] Failed to load chunk:', chunkIndex, error);
74
+ return [];
75
+ }
76
+ }
77
+
78
+ function saveChunk(councilId: string, chunkIndex: number, entries: LedgerEntry[]): void {
79
+ const key = getLedgerChunkKey(councilId, chunkIndex);
80
+ const data = JSON.stringify(entries);
81
+ councilDataStore.setItem(key, data);
82
+ }
83
+
84
+ function deleteChunk(councilId: string, chunkIndex: number): void {
85
+ try {
86
+ const key = getLedgerChunkKey(councilId, chunkIndex);
87
+ councilDataStore.removeItem(key);
88
+ } catch (error) {
89
+ console.error('[LedgerStore] Failed to delete chunk:', chunkIndex, error);
90
+ }
91
+ }
92
+
93
+ // ============================================================================
94
+ // Public API
95
+ // ============================================================================
96
+
97
+ /**
98
+ * Append a new entry to the ledger (append-only)
99
+ */
100
+ export function appendEntry(councilId: string, entry: LedgerEntry): void {
101
+ const index = loadLedgerIndex(councilId);
102
+
103
+ // Determine which chunk to write to
104
+ let currentChunkIndex = index.chunkCount > 0 ? index.chunkCount - 1 : 0;
105
+ let chunk = loadChunk(councilId, currentChunkIndex);
106
+
107
+ // Check if we need a new chunk
108
+ if (chunk.length >= ENTRIES_PER_CHUNK) {
109
+ currentChunkIndex++;
110
+ index.chunkBoundaries.push(index.entryCount);
111
+ chunk = [];
112
+ }
113
+
114
+ // Append entry
115
+ chunk.push(entry);
116
+ saveChunk(councilId, currentChunkIndex, chunk);
117
+
118
+ // Update index
119
+ index.entryCount++;
120
+ index.chunkCount = currentChunkIndex + 1;
121
+ index.totalTokens += entry.tokensUsed ?? 0;
122
+ saveLedgerIndex(index);
123
+
124
+ console.log('[LedgerStore] Appended entry:', entry.id, 'type:', entry.entryType);
125
+ }
126
+
127
+ /**
128
+ * Get entries with optional filtering
129
+ */
130
+ export function getEntries(
131
+ councilId: string,
132
+ options?: {
133
+ types?: LedgerEntryType[];
134
+ phase?: DeliberationPhase;
135
+ round?: number;
136
+ authorPersonaId?: string;
137
+ limit?: number;
138
+ offset?: number;
139
+ }
140
+ ): LedgerEntry[] {
141
+ const index = loadLedgerIndex(councilId);
142
+
143
+ if (index.entryCount === 0) {
144
+ return [];
145
+ }
146
+
147
+ // Load all entries
148
+ let allEntries: LedgerEntry[] = [];
149
+ for (let i = 0; i < index.chunkCount; i++) {
150
+ const chunk = loadChunk(councilId, i);
151
+ allEntries = allEntries.concat(chunk);
152
+ }
153
+
154
+ // Apply filters
155
+ let filtered = allEntries;
156
+
157
+ if (options?.types && options.types.length > 0) {
158
+ filtered = filtered.filter((e) => options.types!.includes(e.entryType));
159
+ }
160
+
161
+ if (options?.phase) {
162
+ filtered = filtered.filter((e) => e.phase === options.phase);
163
+ }
164
+
165
+ if (options?.round !== undefined) {
166
+ filtered = filtered.filter((e) => e.roundNumber === options.round);
167
+ }
168
+
169
+ if (options?.authorPersonaId) {
170
+ filtered = filtered.filter((e) => e.authorPersonaId === options.authorPersonaId);
171
+ }
172
+
173
+ // Apply offset and limit
174
+ if (options?.offset) {
175
+ filtered = filtered.slice(options.offset);
176
+ }
177
+
178
+ if (options?.limit) {
179
+ filtered = filtered.slice(0, options.limit);
180
+ }
181
+
182
+ return filtered;
183
+ }
184
+
185
+ /**
186
+ * Get all entries (no filtering)
187
+ */
188
+ export function getAllEntries(councilId: string): LedgerEntry[] {
189
+ return getEntries(councilId);
190
+ }
191
+
192
+ /**
193
+ * Get a single entry by ID
194
+ */
195
+ export function getEntry(councilId: string, entryId: string): LedgerEntry | null {
196
+ const entries = getEntries(councilId);
197
+ return entries.find((e) => e.id === entryId) ?? null;
198
+ }
199
+
200
+ /**
201
+ * Get the most recent entry of a specific type
202
+ */
203
+ export function getLatestOfType(councilId: string, type: LedgerEntryType): LedgerEntry | null {
204
+ const entries = getEntries(councilId, { types: [type] });
205
+ return entries.length > 0 ? entries[entries.length - 1] : null;
206
+ }
207
+
208
+ /**
209
+ * Get entries for a specific round
210
+ */
211
+ export function getEntriesForRound(councilId: string, round: number): LedgerEntry[] {
212
+ return getEntries(councilId, { round });
213
+ }
214
+
215
+ /**
216
+ * Get entries by author
217
+ */
218
+ export function getEntriesByAuthor(councilId: string, authorPersonaId: string): LedgerEntry[] {
219
+ return getEntries(councilId, { authorPersonaId });
220
+ }
221
+
222
+ /**
223
+ * Get the total token count for the ledger
224
+ */
225
+ export function getLedgerTokenCount(councilId: string): number {
226
+ const index = loadLedgerIndex(councilId);
227
+ return index.totalTokens;
228
+ }
229
+
230
+ /**
231
+ * Get the entry count for the ledger
232
+ */
233
+ export function getLedgerEntryCount(councilId: string): number {
234
+ const index = loadLedgerIndex(councilId);
235
+ return index.entryCount;
236
+ }
237
+
238
+ /**
239
+ * Get the ledger index
240
+ */
241
+ export function getLedgerIndex(councilId: string): LedgerIndex {
242
+ return loadLedgerIndex(councilId);
243
+ }
244
+
245
+ /**
246
+ * Get entries for recent rounds (for context building)
247
+ * Returns entries from the most recent N rounds
248
+ */
249
+ export function getRecentRoundEntries(councilId: string, numRounds: number): LedgerEntry[] {
250
+ const allEntries = getEntries(councilId);
251
+
252
+ // Find all unique round numbers
253
+ const rounds = new Set<number>();
254
+ for (const entry of allEntries) {
255
+ if (entry.roundNumber !== undefined) {
256
+ rounds.add(entry.roundNumber);
257
+ }
258
+ }
259
+
260
+ // Get the most recent N rounds
261
+ const sortedRounds = Array.from(rounds).sort((a, b) => b - a);
262
+ const recentRounds = sortedRounds.slice(0, numRounds);
263
+
264
+ // Filter entries to only include recent rounds
265
+ return allEntries.filter(
266
+ (e) => e.roundNumber !== undefined && recentRounds.includes(e.roundNumber)
267
+ );
268
+ }
269
+
270
+ /**
271
+ * Get manager notes (questions and redirects) - these are never summarized
272
+ */
273
+ export function getManagerNotes(councilId: string, beforeRound?: number): LedgerEntry[] {
274
+ const entries = getEntries(councilId, {
275
+ types: ['manager_question', 'manager_redirect'],
276
+ });
277
+
278
+ if (beforeRound !== undefined) {
279
+ return entries.filter((e) => (e.roundNumber ?? 0) < beforeRound);
280
+ }
281
+
282
+ return entries;
283
+ }
284
+
285
+ /**
286
+ * Clear all entries for a council (use with caution - violates append-only principle)
287
+ * Only for testing or explicit user request
288
+ */
289
+ export function clearLedger(councilId: string): void {
290
+ const index = loadLedgerIndex(councilId);
291
+
292
+ // Delete all chunks
293
+ for (let i = 0; i < index.chunkCount; i++) {
294
+ deleteChunk(councilId, i);
295
+ }
296
+
297
+ // Reset index
298
+ saveLedgerIndex(createEmptyIndex(councilId));
299
+
300
+ console.log('[LedgerStore] Cleared ledger for council:', councilId);
301
+ }
302
+
303
+ /**
304
+ * Delete the entire ledger for a council
305
+ */
306
+ export function deleteLedger(councilId: string): void {
307
+ const index = loadLedgerIndex(councilId);
308
+
309
+ // Delete all chunks
310
+ for (let i = 0; i < index.chunkCount; i++) {
311
+ deleteChunk(councilId, i);
312
+ }
313
+
314
+ // Delete index
315
+ try {
316
+ const key = getLedgerIndexKey(councilId);
317
+ councilDataStore.removeItem(key);
318
+ } catch (error) {
319
+ console.error('[LedgerStore] Failed to delete index:', error);
320
+ }
321
+
322
+ console.log('[LedgerStore] Deleted ledger for council:', councilId);
323
+ }
324
+
325
+ /**
326
+ * Check if a ledger exists for a council
327
+ */
328
+ export function ledgerExists(councilId: string): boolean {
329
+ const key = getLedgerIndexKey(councilId);
330
+ return councilDataStore.getItem(key) !== null;
331
+ }
332
+
333
+ /**
334
+ * Get paginated entries (for UI)
335
+ */
336
+ export function getPaginatedEntries(
337
+ councilId: string,
338
+ page: number,
339
+ pageSize: number = 20
340
+ ): { entries: LedgerEntry[]; total: number; hasMore: boolean } {
341
+ const index = loadLedgerIndex(councilId);
342
+ const offset = page * pageSize;
343
+ const entries = getEntries(councilId, { offset, limit: pageSize });
344
+
345
+ return {
346
+ entries,
347
+ total: index.entryCount,
348
+ hasMore: offset + entries.length < index.entryCount,
349
+ };
350
+ }
351
+
352
+ /**
353
+ * Format entries for context building
354
+ */
355
+ export function formatEntriesForContext(entries: LedgerEntry[]): string {
356
+ return entries
357
+ .map((e) => {
358
+ const roundLabel = e.roundNumber !== undefined ? `, Round ${e.roundNumber}` : '';
359
+ return `[${e.authorPersonaId}${roundLabel}, ${e.entryType}]:\n${e.content}`;
360
+ })
361
+ .join('\n\n');
362
+ }
363
+
364
+ /**
365
+ * Build mechanical summary (no API call)
366
+ */
367
+ export function buildMechanicalSummary(entries: LedgerEntry[]): string {
368
+ return entries
369
+ .filter((e) => ['analysis', 'response', 'proposal'].includes(e.entryType))
370
+ .map((e) => {
371
+ const sentences = e.content.split(/[.!?]\s/);
372
+ const firstTwo = sentences.slice(0, 2).join('. ') + '.';
373
+ return `${e.authorPersonaId} (${e.entryType}): ${firstTwo}`;
374
+ })
375
+ .join('\n\n');
376
+ }
377
+
378
+ // ============================================================================
379
+ // Ledger Store Class (for React integration)
380
+ // ============================================================================
381
+
382
+ export class LedgerStore {
383
+ private listeners: Map<string, Set<() => void>> = new Map();
384
+
385
+ subscribe(councilId: string, listener: () => void): () => void {
386
+ if (!this.listeners.has(councilId)) {
387
+ this.listeners.set(councilId, new Set());
388
+ }
389
+ this.listeners.get(councilId)!.add(listener);
390
+ return () => this.listeners.get(councilId)?.delete(listener);
391
+ }
392
+
393
+ private notify(councilId: string): void {
394
+ this.listeners.get(councilId)?.forEach((listener) => listener());
395
+ }
396
+
397
+ append(councilId: string, entry: LedgerEntry): void {
398
+ appendEntry(councilId, entry);
399
+ this.notify(councilId);
400
+ }
401
+
402
+ getAll(councilId: string): LedgerEntry[] {
403
+ return getAllEntries(councilId);
404
+ }
405
+
406
+ get(councilId: string, entryId: string): LedgerEntry | null {
407
+ return getEntry(councilId, entryId);
408
+ }
409
+
410
+ getByType(councilId: string, type: LedgerEntryType): LedgerEntry[] {
411
+ return getEntries(councilId, { types: [type] });
412
+ }
413
+
414
+ getByRound(councilId: string, round: number): LedgerEntry[] {
415
+ return getEntriesForRound(councilId, round);
416
+ }
417
+
418
+ getLatest(councilId: string, type: LedgerEntryType): LedgerEntry | null {
419
+ return getLatestOfType(councilId, type);
420
+ }
421
+
422
+ getTokenCount(councilId: string): number {
423
+ return getLedgerTokenCount(councilId);
424
+ }
425
+
426
+ clear(councilId: string): void {
427
+ clearLedger(councilId);
428
+ this.notify(councilId);
429
+ }
430
+
431
+ delete(councilId: string): void {
432
+ deleteLedger(councilId);
433
+ this.listeners.delete(councilId);
434
+ }
435
+ }
436
+
437
+ // Singleton instance for app-wide use
438
+ export const ledgerStore = new LedgerStore();