@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,309 @@
1
+ /**
2
+ * Budget Profiles — control how the system balances cost vs quality.
3
+ *
4
+ * Three built-in profiles: quality, balanced, cheap.
5
+ * Custom profiles: add JSON files to .kondi-chat/profiles/
6
+ *
7
+ * Example custom profile (.kondi-chat/profiles/aerospace-review.json):
8
+ * {
9
+ * "name": "aerospace-review",
10
+ * "description": "Aerospace engineering analysis with frontier models",
11
+ * "planningPreference": ["reasoning", "aerospace", "architecture"],
12
+ * "executionPreference": ["aerospace", "coding"],
13
+ * "reviewPreference": ["analysis", "reasoning"],
14
+ * "contextBudget": 50000,
15
+ * "maxIterations": 15,
16
+ * "loopCostCap": 5.00,
17
+ * "loopIterationCap": 8,
18
+ * "promotionThreshold": 2,
19
+ * "includeReflection": true,
20
+ * "includeVerification": true,
21
+ * "preferLocal": false,
22
+ * "maxOutputTokens": 16384
23
+ * }
24
+ */
25
+
26
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'node:fs';
27
+ import { join } from 'node:path';
28
+ import { homedir } from 'node:os';
29
+ import type { ProviderId } from '../types.ts';
30
+
31
+ // ---------------------------------------------------------------------------
32
+ // Types
33
+ // ---------------------------------------------------------------------------
34
+
35
+ export type ProfileName = string;
36
+
37
+ export interface BudgetProfile {
38
+ name: string;
39
+ description: string;
40
+ planningPreference: string[];
41
+ executionPreference: string[];
42
+ reviewPreference: string[];
43
+ contextBudget: number;
44
+ maxIterations: number;
45
+ loopCostCap: number;
46
+ loopIterationCap: number;
47
+ promotionThreshold: number;
48
+ includeReflection: boolean;
49
+ includeVerification: boolean;
50
+ preferLocal: boolean;
51
+ maxOutputTokens: number;
52
+ /**
53
+ * Hard-pin specific ledger phases to specific model IDs. When the router
54
+ * is asked to select for a pinned phase, it returns that exact model and
55
+ * skips the NN/intent/rules tiers. Unpinned phases route normally.
56
+ *
57
+ * Useful for profiles that want a deterministic multi-role pipeline
58
+ * (e.g. plan with gpt-5.4, code with gemini-2.5-pro, review with glm-5.1)
59
+ * without relying on capability tags resolving unambiguously across
60
+ * providers.
61
+ *
62
+ * Keys are `LedgerPhase` strings — typically 'dispatch', 'discuss',
63
+ * 'execute', 'reflect', 'compress'. The model ID must be in the active
64
+ * registry (enabled or not) or routing will fall through.
65
+ */
66
+ rolePinning?: Record<string, string>;
67
+ /**
68
+ * Restrict routing to a subset of providers. Applied to the intent
69
+ * router, rule router, classifier LLM, and compactor — nothing escapes
70
+ * to a provider outside this list. When unset, the scope is derived
71
+ * automatically from `rolePinning` (use that for the common case;
72
+ * declare this explicitly when you want a provider allow-list that's
73
+ * broader than the pinned models).
74
+ */
75
+ allowedProviders?: ProviderId[];
76
+ }
77
+
78
+ // ---------------------------------------------------------------------------
79
+ // Built-in profiles
80
+ // ---------------------------------------------------------------------------
81
+
82
+ const BUILTIN_PROFILES: Record<string, BudgetProfile> = {
83
+ quality: {
84
+ name: 'quality',
85
+ description: 'Frontier models, thorough review, generous context',
86
+ planningPreference: ['general', 'planning', 'reasoning', 'architecture'],
87
+ executionPreference: ['coding', 'reasoning'],
88
+ reviewPreference: ['code-review', 'analysis', 'reasoning'],
89
+ contextBudget: 60_000,
90
+ maxIterations: 30,
91
+ loopCostCap: 10.00,
92
+ loopIterationCap: 30,
93
+ promotionThreshold: 2,
94
+ includeReflection: true,
95
+ includeVerification: true,
96
+ preferLocal: false,
97
+ maxOutputTokens: 16_384,
98
+ },
99
+ balanced: {
100
+ name: 'balanced',
101
+ description: 'Good cost/quality balance — default mode',
102
+ planningPreference: ['general', 'planning', 'reasoning'],
103
+ executionPreference: ['coding', 'fast-coding'],
104
+ reviewPreference: ['code-review', 'analysis'],
105
+ contextBudget: 30_000,
106
+ maxIterations: 20,
107
+ loopCostCap: 3.00,
108
+ loopIterationCap: 20,
109
+ promotionThreshold: 2,
110
+ includeReflection: true,
111
+ includeVerification: true,
112
+ preferLocal: false,
113
+ maxOutputTokens: 8_192,
114
+ },
115
+ cheap: {
116
+ name: 'cheap',
117
+ description: 'Cheapest models, tight limits, local when possible',
118
+ planningPreference: ['fast-coding', 'general'],
119
+ executionPreference: ['fast-coding', 'coding'],
120
+ reviewPreference: [],
121
+ contextBudget: 15_000,
122
+ maxIterations: 12,
123
+ loopCostCap: 0.75,
124
+ loopIterationCap: 8,
125
+ promotionThreshold: 3,
126
+ includeReflection: false,
127
+ includeVerification: true,
128
+ preferLocal: true,
129
+ maxOutputTokens: 4_096,
130
+ },
131
+ zai: {
132
+ name: 'zai',
133
+ description: 'Z.AI (GLM) Coding Plan — glm-5.1 plans, glm-4.6 codes, glm-4.5-flash compresses (free)',
134
+ planningPreference: ['planning', 'reasoning', 'analysis', 'code-review'],
135
+ executionPreference: ['coding', 'fast-coding', 'general'],
136
+ reviewPreference: ['code-review', 'analysis', 'reasoning'],
137
+ contextBudget: 30_000,
138
+ maxIterations: 20,
139
+ loopCostCap: 3.00,
140
+ loopIterationCap: 20,
141
+ promotionThreshold: 2,
142
+ includeReflection: true,
143
+ includeVerification: true,
144
+ preferLocal: false,
145
+ maxOutputTokens: 8_192,
146
+ allowedProviders: ['zai'],
147
+ rolePinning: {
148
+ discuss: 'glm-5.1',
149
+ dispatch: 'glm-5.1',
150
+ execute: 'glm-4.6',
151
+ reflect: 'glm-5.1',
152
+ compress: 'glm-4.5-flash',
153
+ state_update: 'glm-4.5-flash',
154
+ },
155
+ },
156
+ 'best-value': {
157
+ name: 'best-value',
158
+ description: 'Sonnet for chat/review, GPT-5.4 plans, Gemini codes (free), GLM-flash compresses (free)',
159
+ planningPreference: ['planning', 'reasoning', 'architecture', 'analysis'],
160
+ executionPreference: ['coding', 'fast-coding', 'refactoring'],
161
+ reviewPreference: ['code-review', 'analysis', 'reasoning'],
162
+ contextBudget: 40_000,
163
+ maxIterations: 24,
164
+ loopCostCap: 5.00,
165
+ loopIterationCap: 24,
166
+ promotionThreshold: 2,
167
+ includeReflection: true,
168
+ includeVerification: true,
169
+ preferLocal: false,
170
+ maxOutputTokens: 8_192,
171
+ rolePinning: {
172
+ discuss: 'claude-sonnet-4-5-20250929',
173
+ dispatch: 'gpt-5.4',
174
+ execute: 'models/gemini-2.5-pro',
175
+ reflect: 'claude-sonnet-4-5-20250929',
176
+ compress: 'glm-4.5-flash',
177
+ state_update: 'glm-4.5-flash',
178
+ },
179
+ },
180
+ orchestra: {
181
+ name: 'orchestra',
182
+ description: 'Multi-provider role pipeline — GPT-5.4 plans, Gemini codes, GLM-5.1 reviews',
183
+ planningPreference: ['planning', 'reasoning', 'analysis'],
184
+ executionPreference: ['coding', 'fast-coding', 'general'],
185
+ reviewPreference: ['code-review', 'analysis', 'reasoning'],
186
+ contextBudget: 40_000,
187
+ maxIterations: 24,
188
+ loopCostCap: 5.00,
189
+ loopIterationCap: 24,
190
+ promotionThreshold: 2,
191
+ includeReflection: true,
192
+ includeVerification: true,
193
+ preferLocal: false,
194
+ maxOutputTokens: 8_192,
195
+ rolePinning: {
196
+ discuss: 'gpt-5.4',
197
+ dispatch: 'gpt-5.4',
198
+ execute: 'models/gemini-2.5-pro',
199
+ reflect: 'glm-5.1',
200
+ compress: 'glm-4.5-flash',
201
+ state_update: 'glm-4.5-flash',
202
+ },
203
+ },
204
+ };
205
+
206
+ // ---------------------------------------------------------------------------
207
+ // Profile Manager
208
+ // ---------------------------------------------------------------------------
209
+
210
+ export class ProfileManager {
211
+ private active: BudgetProfile;
212
+ private custom: Record<string, BudgetProfile> = {};
213
+ private profileDir: string;
214
+
215
+ constructor(initial: ProfileName = 'balanced', storageDir?: string) {
216
+ this.profileDir = storageDir ? join(storageDir, 'profiles') : '';
217
+ if (this.profileDir) {
218
+ mkdirSync(this.profileDir, { recursive: true });
219
+ this.ensureBuiltins();
220
+ // Load user-level profiles first (~/.kondi-chat/profiles/), then
221
+ // project-level profiles (which override user-level on name collision).
222
+ // This way custom profiles like glm/best-value/orchestra are available
223
+ // in every project, not just the one they were created in.
224
+ const userProfileDir = join(homedir(), '.kondi-chat', 'profiles');
225
+ this.loadFromDir(userProfileDir);
226
+ this.loadFromDir(this.profileDir);
227
+ }
228
+ this.active = { ...(this.getAll()[initial] || BUILTIN_PROFILES.balanced) };
229
+ }
230
+
231
+ /**
232
+ * Write built-in profiles to disk so they're visible and editable.
233
+ * Always overwrites — built-in files are owned by the code, not the user.
234
+ * Users who want to customize should copy to a new file under a different
235
+ * name (those are loaded as custom profiles and never overwritten).
236
+ */
237
+ private ensureBuiltins(): void {
238
+ for (const [name, profile] of Object.entries(BUILTIN_PROFILES)) {
239
+ const path = join(this.profileDir, `${name}.json`);
240
+ writeFileSync(path, JSON.stringify(profile, null, 2));
241
+ }
242
+ }
243
+
244
+ getActive(): BudgetProfile {
245
+ return this.active;
246
+ }
247
+
248
+ setProfile(name: ProfileName): void {
249
+ const all = this.getAll();
250
+ if (!all[name]) {
251
+ const available = Object.keys(all).join(', ');
252
+ throw new Error(`Unknown profile: ${name}. Available: ${available}`);
253
+ }
254
+ this.active = { ...all[name] };
255
+ }
256
+
257
+ getProfile(name: ProfileName): BudgetProfile {
258
+ const all = this.getAll();
259
+ return { ...(all[name] || BUILTIN_PROFILES.balanced) };
260
+ }
261
+
262
+ getNames(): string[] {
263
+ return Object.keys(this.getAll());
264
+ }
265
+
266
+ /** Get all profiles — disk versions override built-in defaults */
267
+ getAll(): Record<string, BudgetProfile> {
268
+ // Custom (from disk) takes priority — this includes edited built-ins
269
+ return { ...BUILTIN_PROFILES, ...this.custom };
270
+ }
271
+
272
+ /** Load profiles from a directory into this.custom */
273
+ private loadFromDir(dir: string): void {
274
+ if (!dir || !existsSync(dir)) return;
275
+ const files = readdirSync(dir).filter(f => f.endsWith('.json'));
276
+ for (const file of files) {
277
+ try {
278
+ const raw = readFileSync(join(dir, file), 'utf-8');
279
+ const profile = JSON.parse(raw) as BudgetProfile;
280
+ if (profile.name) {
281
+ this.custom[profile.name] = profile;
282
+ }
283
+ } catch {
284
+ // Skip invalid files
285
+ }
286
+ }
287
+ }
288
+
289
+ /** Reload profiles from disk (user-level + project-level) */
290
+ reload(): void {
291
+ this.custom = {};
292
+ const userProfileDir = join(homedir(), '.kondi-chat', 'profiles');
293
+ this.loadFromDir(userProfileDir);
294
+ this.loadFromDir(this.profileDir);
295
+ }
296
+
297
+ format(): string {
298
+ const all = this.getAll();
299
+ const lines: string[] = [];
300
+ for (const [name, profile] of Object.entries(all)) {
301
+ const marker = name === this.active.name ? ' (active)' : '';
302
+ const isCustom = this.custom[name] ? ' [custom]' : '';
303
+ lines.push(`${name}${marker}${isCustom}: ${profile.description}`);
304
+ lines.push(` Context: ${profile.contextBudget.toLocaleString()} | Loop: ${profile.loopIterationCap} iters, $${profile.loopCostCap.toFixed(2)} cap | Local: ${profile.preferLocal ? 'yes' : 'no'}`);
305
+ lines.push('');
306
+ }
307
+ return lines.join('\n');
308
+ }
309
+ }