@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,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
|
+
}
|