@jigyasudham/veto 0.8.3 → 1.1.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.
- package/README.md +217 -54
- package/dist/adapters/index.js +4 -3
- package/dist/agents/executor.js +36 -3
- package/dist/cli.js +246 -7
- package/dist/context/reader.js +113 -0
- package/dist/council/index.js +3 -1
- package/dist/memory/local.js +18 -1
- package/dist/memory/schema.js +12 -10
- package/dist/plugins/loader.js +49 -0
- package/dist/router/index.js +2 -2
- package/dist/router/learning-updater.js +45 -1
- package/dist/server.js +507 -21
- package/dist/watcher/index.js +77 -0
- package/dist/workflow/pipeline.js +64 -0
- package/package.json +12 -3
- package/.claude/settings.local.json +0 -9
- package/src/adapters/claude.ts +0 -70
- package/src/adapters/codex.ts +0 -71
- package/src/adapters/gemini.ts +0 -71
- package/src/adapters/index.ts +0 -217
- package/src/agents/development/api.ts +0 -120
- package/src/agents/development/backend.ts +0 -85
- package/src/agents/development/coder.ts +0 -213
- package/src/agents/development/database.ts +0 -83
- package/src/agents/development/debugger.ts +0 -238
- package/src/agents/development/devops.ts +0 -86
- package/src/agents/development/frontend.ts +0 -85
- package/src/agents/development/migration.ts +0 -144
- package/src/agents/development/performance.ts +0 -144
- package/src/agents/development/refactor.ts +0 -86
- package/src/agents/development/reviewer.ts +0 -268
- package/src/agents/development/tester.ts +0 -151
- package/src/agents/executor.ts +0 -158
- package/src/agents/memory/context-manager.ts +0 -171
- package/src/agents/memory/decision-logger.ts +0 -160
- package/src/agents/memory/knowledge-base.ts +0 -124
- package/src/agents/memory/pattern-learner.ts +0 -143
- package/src/agents/memory/project-mapper.ts +0 -118
- package/src/agents/quality/accessibility.ts +0 -99
- package/src/agents/quality/code-quality.ts +0 -115
- package/src/agents/quality/compatibility.ts +0 -58
- package/src/agents/quality/documentation.ts +0 -105
- package/src/agents/quality/error-handling.ts +0 -96
- package/src/agents/research/competitor-analyzer.ts +0 -45
- package/src/agents/research/cost-analyzer.ts +0 -54
- package/src/agents/research/estimator.ts +0 -60
- package/src/agents/research/ethics-bias.ts +0 -113
- package/src/agents/research/researcher.ts +0 -114
- package/src/agents/research/risk-assessor.ts +0 -63
- package/src/agents/research/tech-advisor.ts +0 -55
- package/src/agents/security/auth.ts +0 -287
- package/src/agents/security/dependency-audit.ts +0 -337
- package/src/agents/security/penetration.ts +0 -262
- package/src/agents/security/privacy.ts +0 -285
- package/src/agents/security/scanner.ts +0 -322
- package/src/agents/security/secrets.ts +0 -249
- package/src/agents/types.ts +0 -66
- package/src/agents/workflow/automation.ts +0 -59
- package/src/agents/workflow/file-manager.ts +0 -52
- package/src/agents/workflow/git-agent.ts +0 -55
- package/src/agents/workflow/reporter.ts +0 -51
- package/src/agents/workflow/search-agent.ts +0 -40
- package/src/agents/workflow/task-coordinator.ts +0 -41
- package/src/agents/workflow/task-planner.ts +0 -47
- package/src/cli.ts +0 -204
- package/src/council/decision-engine.ts +0 -171
- package/src/council/devil-advocate.ts +0 -116
- package/src/council/index.ts +0 -44
- package/src/council/lead-developer.ts +0 -118
- package/src/council/legal-compliance.ts +0 -152
- package/src/council/product-manager.ts +0 -102
- package/src/council/security.ts +0 -172
- package/src/council/system-architect.ts +0 -132
- package/src/council/types.ts +0 -33
- package/src/council/ux-designer.ts +0 -121
- package/src/memory/local.ts +0 -305
- package/src/memory/schema.ts +0 -174
- package/src/memory/sync.ts +0 -274
- package/src/router/complexity-scorer.ts +0 -96
- package/src/router/context-compressor.ts +0 -74
- package/src/router/index.ts +0 -60
- package/src/router/learning-updater.ts +0 -271
- package/src/router/model-selector.ts +0 -83
- package/src/router/rate-monitor.ts +0 -103
- package/src/server.ts +0 -1038
- package/src/skills/development/skill-api-design.ts +0 -329
- package/src/skills/development/skill-auth.ts +0 -271
- package/src/skills/development/skill-ci-cd.ts +0 -0
- package/src/skills/development/skill-crud.ts +0 -209
- package/src/skills/development/skill-db-schema.ts +0 -0
- package/src/skills/development/skill-docker.ts +0 -0
- package/src/skills/development/skill-env-setup.ts +0 -0
- package/src/skills/development/skill-scaffold.ts +0 -323
- package/src/skills/intelligence/skill-complexity-score.ts +0 -69
- package/src/skills/intelligence/skill-cost-track.ts +0 -39
- package/src/skills/intelligence/skill-learning-loop.ts +0 -69
- package/src/skills/intelligence/skill-pattern-detect.ts +0 -38
- package/src/skills/intelligence/skill-rate-watch.ts +0 -61
- package/src/skills/memory/skill-context-compress.ts +0 -98
- package/src/skills/memory/skill-cross-sync.ts +0 -104
- package/src/skills/memory/skill-decision-log.ts +0 -119
- package/src/skills/memory/skill-session-restore.ts +0 -59
- package/src/skills/memory/skill-session-save.ts +0 -94
- package/src/skills/quality/skill-accessibility.ts +0 -0
- package/src/skills/quality/skill-code-review.ts +0 -84
- package/src/skills/quality/skill-docs-gen.ts +0 -0
- package/src/skills/quality/skill-perf-audit.ts +0 -0
- package/src/skills/quality/skill-security-scan.ts +0 -91
- package/src/skills/quality/skill-test-suite.ts +0 -290
- package/src/skills/workflow/skill-deploy.ts +0 -0
- package/src/skills/workflow/skill-git-workflow.ts +0 -0
- package/src/skills/workflow/skill-rollback.ts +0 -0
- package/src/skills/workflow/skill-task-breakdown.ts +0 -0
- package/tsconfig.json +0 -20
package/src/memory/sync.ts
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
// Memory export/import — file-based cross-machine continuity
|
|
2
|
-
// No external services. Export to JSON, copy the file, import on another machine.
|
|
3
|
-
|
|
4
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
5
|
-
import { join } from 'node:path';
|
|
6
|
-
import { homedir } from 'node:os';
|
|
7
|
-
import { getDb, getDbPath } from './local.js';
|
|
8
|
-
|
|
9
|
-
const DEFAULT_EXPORT_PATH = join(homedir(), '.veto', 'veto-export.json');
|
|
10
|
-
|
|
11
|
-
export type ExportResult = {
|
|
12
|
-
success: boolean;
|
|
13
|
-
export_path: string;
|
|
14
|
-
exported: Record<string, number>;
|
|
15
|
-
exported_at: string;
|
|
16
|
-
error?: string;
|
|
17
|
-
next_step: string;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export type ImportResult = {
|
|
21
|
-
success: boolean;
|
|
22
|
-
import_path: string;
|
|
23
|
-
merged: Record<string, number>;
|
|
24
|
-
skipped: Record<string, number>;
|
|
25
|
-
imported_at: string;
|
|
26
|
-
error?: string;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export type DbSizeResult = {
|
|
30
|
-
db_path: string;
|
|
31
|
-
tables: Record<string, number>;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export function exportMemory(outputPath?: string): ExportResult {
|
|
35
|
-
const exportPath = outputPath ?? DEFAULT_EXPORT_PATH;
|
|
36
|
-
const db = getDb();
|
|
37
|
-
|
|
38
|
-
try {
|
|
39
|
-
const sessions = db.prepare('SELECT * FROM sessions ORDER BY created_at DESC').all();
|
|
40
|
-
const decisions = db.prepare('SELECT * FROM decisions ORDER BY made_at DESC').all();
|
|
41
|
-
const knowledge = db.prepare('SELECT * FROM knowledge_base ORDER BY created_at DESC').all();
|
|
42
|
-
const patterns = db.prepare('SELECT * FROM patterns ORDER BY confidence DESC').all();
|
|
43
|
-
const projectMaps = db.prepare('SELECT * FROM project_map ORDER BY updated_at DESC').all();
|
|
44
|
-
const councilOutcomes = db.prepare('SELECT * FROM council_outcomes ORDER BY debated_at DESC').all();
|
|
45
|
-
|
|
46
|
-
const payload = {
|
|
47
|
-
veto_export_version: 1,
|
|
48
|
-
exported_at: new Date().toISOString(),
|
|
49
|
-
db_path: getDbPath(),
|
|
50
|
-
data: {
|
|
51
|
-
sessions,
|
|
52
|
-
decisions,
|
|
53
|
-
knowledge_base: knowledge,
|
|
54
|
-
patterns,
|
|
55
|
-
project_map: projectMaps,
|
|
56
|
-
council_outcomes: councilOutcomes,
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
writeFileSync(exportPath, JSON.stringify(payload, null, 2), 'utf-8');
|
|
61
|
-
|
|
62
|
-
const exported = {
|
|
63
|
-
sessions: sessions.length,
|
|
64
|
-
decisions: decisions.length,
|
|
65
|
-
knowledge_base: knowledge.length,
|
|
66
|
-
patterns: patterns.length,
|
|
67
|
-
project_map: projectMaps.length,
|
|
68
|
-
council_outcomes: councilOutcomes.length,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
return {
|
|
72
|
-
success: true,
|
|
73
|
-
export_path: exportPath,
|
|
74
|
-
exported,
|
|
75
|
-
exported_at: payload.exported_at,
|
|
76
|
-
next_step: `Copy ${exportPath} to your other machine, then call veto_memory_import with that file path.`,
|
|
77
|
-
};
|
|
78
|
-
} catch (err) {
|
|
79
|
-
return {
|
|
80
|
-
success: false,
|
|
81
|
-
export_path: exportPath,
|
|
82
|
-
exported: {},
|
|
83
|
-
exported_at: new Date().toISOString(),
|
|
84
|
-
error: err instanceof Error ? err.message : String(err),
|
|
85
|
-
next_step: 'Fix the error above and retry.',
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function importMemory(inputPath?: string): ImportResult {
|
|
91
|
-
const importPath = inputPath ?? DEFAULT_EXPORT_PATH;
|
|
92
|
-
|
|
93
|
-
if (!existsSync(importPath)) {
|
|
94
|
-
return {
|
|
95
|
-
success: false,
|
|
96
|
-
import_path: importPath,
|
|
97
|
-
merged: {},
|
|
98
|
-
skipped: {},
|
|
99
|
-
imported_at: new Date().toISOString(),
|
|
100
|
-
error: `File not found: ${importPath}. Run veto_memory_export on your other machine first, then copy the file here.`,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const db = getDb();
|
|
105
|
-
const merged: Record<string, number> = {};
|
|
106
|
-
const skipped: Record<string, number> = {};
|
|
107
|
-
|
|
108
|
-
try {
|
|
109
|
-
const raw = readFileSync(importPath, 'utf-8');
|
|
110
|
-
const payload = JSON.parse(raw) as {
|
|
111
|
-
veto_export_version: number;
|
|
112
|
-
data: Record<string, Record<string, unknown>[]>;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
if (payload.veto_export_version !== 1) {
|
|
116
|
-
throw new Error(`Unknown export version: ${payload.veto_export_version}`);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const { sessions = [], decisions = [], knowledge_base = [], patterns = [], project_map = [], council_outcomes = [] } = payload.data;
|
|
120
|
-
|
|
121
|
-
merged['sessions'] = 0; skipped['sessions'] = 0;
|
|
122
|
-
for (const s of sessions) {
|
|
123
|
-
const r = db.prepare(`
|
|
124
|
-
INSERT OR IGNORE INTO sessions
|
|
125
|
-
(id, started_at, ended_at, platform, project_dir, summary, context, task_state, token_count, created_at)
|
|
126
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
127
|
-
`).run(
|
|
128
|
-
String(s['id'] ?? ''), String(s['started_at'] ?? ''),
|
|
129
|
-
s['ended_at'] != null ? String(s['ended_at']) : null,
|
|
130
|
-
String(s['platform'] ?? 'claude'),
|
|
131
|
-
s['project_dir'] != null ? String(s['project_dir']) : null,
|
|
132
|
-
s['summary'] != null ? String(s['summary']) : null,
|
|
133
|
-
s['context'] != null ? String(s['context']) : null,
|
|
134
|
-
s['task_state'] != null ? String(s['task_state']) : null,
|
|
135
|
-
typeof s['token_count'] === 'number' ? s['token_count'] : 0,
|
|
136
|
-
String(s['created_at'] ?? new Date().toISOString())
|
|
137
|
-
) as { changes: number };
|
|
138
|
-
if (r.changes > 0) merged['sessions']++; else skipped['sessions']++;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
merged['decisions'] = 0; skipped['decisions'] = 0;
|
|
142
|
-
for (const d of decisions) {
|
|
143
|
-
const r = db.prepare(`
|
|
144
|
-
INSERT OR IGNORE INTO decisions
|
|
145
|
-
(id, session_id, made_at, decision, rationale, council_verdict, files_affected, overridden)
|
|
146
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
147
|
-
`).run(
|
|
148
|
-
String(d['id'] ?? ''), String(d['session_id'] ?? ''),
|
|
149
|
-
String(d['made_at'] ?? ''), String(d['decision'] ?? ''),
|
|
150
|
-
d['rationale'] != null ? String(d['rationale']) : null,
|
|
151
|
-
d['council_verdict'] != null ? String(d['council_verdict']) : null,
|
|
152
|
-
d['files_affected'] != null ? String(d['files_affected']) : null,
|
|
153
|
-
typeof d['overridden'] === 'number' ? d['overridden'] : 0
|
|
154
|
-
) as { changes: number };
|
|
155
|
-
if (r.changes > 0) merged['decisions']++; else skipped['decisions']++;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
merged['knowledge_base'] = 0; skipped['knowledge_base'] = 0;
|
|
159
|
-
for (const k of knowledge_base) {
|
|
160
|
-
const r = db.prepare(`
|
|
161
|
-
INSERT OR IGNORE INTO knowledge_base
|
|
162
|
-
(id, type, title, content, tags, project_dir, session_id, relevance, accessed_count, created_at, updated_at)
|
|
163
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
164
|
-
`).run(
|
|
165
|
-
String(k['id'] ?? ''),
|
|
166
|
-
k['type'] != null ? String(k['type']) : 'solution',
|
|
167
|
-
String(k['title'] ?? ''), String(k['content'] ?? ''),
|
|
168
|
-
k['tags'] != null ? String(k['tags']) : null,
|
|
169
|
-
k['project_dir'] != null ? String(k['project_dir']) : null,
|
|
170
|
-
k['session_id'] != null ? String(k['session_id']) : null,
|
|
171
|
-
typeof k['relevance'] === 'number' ? k['relevance'] : 1.0,
|
|
172
|
-
typeof k['accessed_count'] === 'number' ? k['accessed_count'] : 0,
|
|
173
|
-
String(k['created_at'] ?? new Date().toISOString()),
|
|
174
|
-
String(k['updated_at'] ?? new Date().toISOString())
|
|
175
|
-
) as { changes: number };
|
|
176
|
-
if (r.changes > 0) merged['knowledge_base']++; else skipped['knowledge_base']++;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
merged['patterns'] = 0; skipped['patterns'] = 0;
|
|
180
|
-
for (const p of patterns) {
|
|
181
|
-
const existing = db.prepare('SELECT seen_count, confidence FROM patterns WHERE pattern_key = ?').get(String(p['pattern_key'] ?? '')) as { seen_count: number; confidence: number } | undefined;
|
|
182
|
-
if (existing) {
|
|
183
|
-
// Merge: take higher confidence, sum seen counts
|
|
184
|
-
const mergedConf = Math.max(existing.confidence, typeof p['confidence'] === 'number' ? p['confidence'] : 1.0);
|
|
185
|
-
const mergedCount = existing.seen_count + (typeof p['seen_count'] === 'number' ? p['seen_count'] : 1);
|
|
186
|
-
db.prepare('UPDATE patterns SET confidence = ?, seen_count = ?, updated_at = ? WHERE pattern_key = ?')
|
|
187
|
-
.run(mergedConf, mergedCount, new Date().toISOString(), String(p['pattern_key'] ?? ''));
|
|
188
|
-
skipped['patterns']++;
|
|
189
|
-
} else {
|
|
190
|
-
db.prepare(`
|
|
191
|
-
INSERT INTO patterns (id, pattern_key, pattern_val, confidence, seen_count, updated_at)
|
|
192
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
193
|
-
`).run(
|
|
194
|
-
String(p['id'] ?? ''), String(p['pattern_key'] ?? ''),
|
|
195
|
-
String(p['pattern_val'] ?? ''),
|
|
196
|
-
typeof p['confidence'] === 'number' ? p['confidence'] : 1.0,
|
|
197
|
-
typeof p['seen_count'] === 'number' ? p['seen_count'] : 1,
|
|
198
|
-
String(p['updated_at'] ?? new Date().toISOString())
|
|
199
|
-
);
|
|
200
|
-
merged['patterns']++;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
merged['project_map'] = 0; skipped['project_map'] = 0;
|
|
205
|
-
for (const m of project_map) {
|
|
206
|
-
const r = db.prepare(`
|
|
207
|
-
INSERT OR IGNORE INTO project_map (id, project_dir, structure, key_modules, tech_stack, updated_at)
|
|
208
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
209
|
-
`).run(
|
|
210
|
-
String(m['id'] ?? ''), String(m['project_dir'] ?? ''),
|
|
211
|
-
String(m['structure'] ?? '{}'),
|
|
212
|
-
m['key_modules'] != null ? String(m['key_modules']) : null,
|
|
213
|
-
m['tech_stack'] != null ? String(m['tech_stack']) : null,
|
|
214
|
-
String(m['updated_at'] ?? new Date().toISOString())
|
|
215
|
-
) as { changes: number };
|
|
216
|
-
if (r.changes > 0) merged['project_map']++; else skipped['project_map']++;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
merged['council_outcomes'] = 0; skipped['council_outcomes'] = 0;
|
|
220
|
-
for (const c of council_outcomes) {
|
|
221
|
-
const r = db.prepare(`
|
|
222
|
-
INSERT OR IGNORE INTO council_outcomes
|
|
223
|
-
(id, session_id, task, verdict, lead_dev, pm, architect, ux, devil, legal, security, recommended, debated_at)
|
|
224
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
225
|
-
`).run(
|
|
226
|
-
String(c['id'] ?? ''),
|
|
227
|
-
c['session_id'] != null ? String(c['session_id']) : null,
|
|
228
|
-
String(c['task'] ?? ''), String(c['verdict'] ?? ''),
|
|
229
|
-
c['lead_dev'] != null ? String(c['lead_dev']) : null,
|
|
230
|
-
c['pm'] != null ? String(c['pm']) : null,
|
|
231
|
-
c['architect'] != null ? String(c['architect']) : null,
|
|
232
|
-
c['ux'] != null ? String(c['ux']) : null,
|
|
233
|
-
c['devil'] != null ? String(c['devil']) : null,
|
|
234
|
-
c['legal'] != null ? String(c['legal']) : null,
|
|
235
|
-
c['security'] != null ? String(c['security']) : null,
|
|
236
|
-
c['recommended'] != null ? String(c['recommended']) : null,
|
|
237
|
-
String(c['debated_at'] ?? new Date().toISOString())
|
|
238
|
-
) as { changes: number };
|
|
239
|
-
if (r.changes > 0) merged['council_outcomes']++; else skipped['council_outcomes']++;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return {
|
|
243
|
-
success: true,
|
|
244
|
-
import_path: importPath,
|
|
245
|
-
merged,
|
|
246
|
-
skipped,
|
|
247
|
-
imported_at: new Date().toISOString(),
|
|
248
|
-
};
|
|
249
|
-
} catch (err) {
|
|
250
|
-
return {
|
|
251
|
-
success: false,
|
|
252
|
-
import_path: importPath,
|
|
253
|
-
merged,
|
|
254
|
-
skipped,
|
|
255
|
-
imported_at: new Date().toISOString(),
|
|
256
|
-
error: err instanceof Error ? err.message : String(err),
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export function getLocalDbSize(): DbSizeResult {
|
|
262
|
-
const db = getDb();
|
|
263
|
-
const tables = ['sessions', 'decisions', 'council_outcomes', 'knowledge_base', 'patterns', 'project_map', 'learning_data'];
|
|
264
|
-
const counts: Record<string, number> = {};
|
|
265
|
-
for (const table of tables) {
|
|
266
|
-
try {
|
|
267
|
-
const row = db.prepare(`SELECT COUNT(*) as c FROM ${table}`).get() as { c: number };
|
|
268
|
-
counts[table] = row.c;
|
|
269
|
-
} catch {
|
|
270
|
-
counts[table] = 0;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
return { db_path: getDbPath(), tables: counts };
|
|
274
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
// Scores a task description 0-100 locally — zero tokens spent
|
|
2
|
-
|
|
3
|
-
export type ComplexityFactors = {
|
|
4
|
-
word_count_score: number;
|
|
5
|
-
keyword_score: number;
|
|
6
|
-
depth_score: number;
|
|
7
|
-
files_score: number;
|
|
8
|
-
council_bonus: number;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export type ComplexityResult = {
|
|
12
|
-
score: number;
|
|
13
|
-
tier: 1 | 2 | 3;
|
|
14
|
-
factors: ComplexityFactors;
|
|
15
|
-
council_required: boolean;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// 5 pts each — strong signal of high complexity
|
|
19
|
-
const HIGH_KEYWORDS = [
|
|
20
|
-
'architecture', 'security', 'authenticat', 'authorizat', 'migrat',
|
|
21
|
-
'refactor', 'scalab', 'concurrent', 'distributed', 'oauth', 'jwt',
|
|
22
|
-
'rbac', 'encryption', 'cryptograph', 'penetration', 'vulnerabilit',
|
|
23
|
-
'infrastructure', 'compliance', 'performance',
|
|
24
|
-
];
|
|
25
|
-
|
|
26
|
-
// 2 pts each — moderate complexity signal
|
|
27
|
-
const MEDIUM_KEYWORDS = [
|
|
28
|
-
'component', 'function', 'test', 'api', 'database', 'query', 'schema',
|
|
29
|
-
'deploy', 'docker', 'implement', 'build', 'feature', 'module',
|
|
30
|
-
'service', 'endpoint', 'integration', 'configuration',
|
|
31
|
-
];
|
|
32
|
-
|
|
33
|
-
// Council is always required for these topics
|
|
34
|
-
const COUNCIL_TRIGGERS = [
|
|
35
|
-
'architecture', 'security', 'auth', 'migrat', 'vulnerabilit',
|
|
36
|
-
'encryption', 'infrastructure', 'breaking change', 'drop table',
|
|
37
|
-
'delete all', 'remove all',
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
export function scoreComplexity(
|
|
41
|
-
task: string,
|
|
42
|
-
filesAffected = 1,
|
|
43
|
-
forceCouncil = false,
|
|
44
|
-
thresholds?: { tier1_max: number; tier2_max: number }
|
|
45
|
-
): ComplexityResult {
|
|
46
|
-
const TIER1_MAX = thresholds?.tier1_max ?? 30;
|
|
47
|
-
const TIER2_MAX = thresholds?.tier2_max ?? 70;
|
|
48
|
-
const lower = task.toLowerCase();
|
|
49
|
-
const words = lower.split(/\s+/).filter(Boolean);
|
|
50
|
-
|
|
51
|
-
// Factor 1: word count → 0-20 pts (1 pt per 3 words, cap 20)
|
|
52
|
-
const word_count_score = Math.min(20, Math.floor(words.length / 3));
|
|
53
|
-
|
|
54
|
-
// Factor 2: technical keywords → 0-30 pts
|
|
55
|
-
let keyword_score = 0;
|
|
56
|
-
let council_required = forceCouncil;
|
|
57
|
-
|
|
58
|
-
for (const word of words) {
|
|
59
|
-
for (const kw of HIGH_KEYWORDS) {
|
|
60
|
-
if (word.includes(kw)) { keyword_score += 5; break; }
|
|
61
|
-
}
|
|
62
|
-
for (const kw of MEDIUM_KEYWORDS) {
|
|
63
|
-
if (word.includes(kw)) { keyword_score += 2; break; }
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
for (const trigger of COUNCIL_TRIGGERS) {
|
|
67
|
-
if (lower.includes(trigger)) { council_required = true; break; }
|
|
68
|
-
}
|
|
69
|
-
keyword_score = Math.min(30, keyword_score);
|
|
70
|
-
|
|
71
|
-
// Factor 3: task depth → 0-20 pts (conjunctions, length, multi-step)
|
|
72
|
-
let depth_score = 0;
|
|
73
|
-
if (lower.includes(' and ') || lower.includes(' with ')) depth_score += 5;
|
|
74
|
-
if (lower.includes(',') || lower.includes(' plus ')) depth_score += 5;
|
|
75
|
-
if (words.length > 20) depth_score += 5;
|
|
76
|
-
if (words.length > 40) depth_score += 5;
|
|
77
|
-
depth_score = Math.min(20, depth_score);
|
|
78
|
-
|
|
79
|
-
// Factor 4: files affected → 0-20 pts
|
|
80
|
-
const files_score = Math.min(20, (filesAffected - 1) * 3);
|
|
81
|
-
|
|
82
|
-
// Factor 5: council bonus — pushes score into Tier 3 territory
|
|
83
|
-
const council_bonus = council_required ? 10 : 0;
|
|
84
|
-
|
|
85
|
-
const raw = word_count_score + keyword_score + depth_score + files_score + council_bonus;
|
|
86
|
-
// Council tasks always land in Tier 3 — floor at 71
|
|
87
|
-
const score = Math.min(100, council_required ? Math.max(71, raw) : raw);
|
|
88
|
-
const tier: 1 | 2 | 3 = score <= TIER1_MAX ? 1 : score <= TIER2_MAX ? 2 : 3;
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
score,
|
|
92
|
-
tier,
|
|
93
|
-
factors: { word_count_score, keyword_score, depth_score, files_score, council_bonus },
|
|
94
|
-
council_required,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
// Trims context before model calls to avoid wasting tokens
|
|
2
|
-
// Thresholds from VETO_VISION_FINAL.md:
|
|
3
|
-
// < 2k tokens → passthrough
|
|
4
|
-
// 2k-8k tokens → compress (drop irrelevant sections)
|
|
5
|
-
// > 8k tokens → hard-compress (tail + file references only)
|
|
6
|
-
|
|
7
|
-
export type CompressionStrategy = 'passthrough' | 'compress' | 'hard-compress';
|
|
8
|
-
|
|
9
|
-
export type CompressionResult = {
|
|
10
|
-
original_tokens: number;
|
|
11
|
-
compressed_tokens: number;
|
|
12
|
-
compression_ratio: number;
|
|
13
|
-
content: string;
|
|
14
|
-
strategy: CompressionStrategy;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// Rough 4-chars-per-token estimate — good enough for routing decisions
|
|
18
|
-
export function estimateTokens(text: string): number {
|
|
19
|
-
return Math.ceil(text.length / 4);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function extractRelevantSections(context: string, relevantFiles: string[]): string {
|
|
23
|
-
if (relevantFiles.length === 0) return context;
|
|
24
|
-
|
|
25
|
-
const lines = context.split('\n');
|
|
26
|
-
const kept: string[] = [];
|
|
27
|
-
let inRelevant = false;
|
|
28
|
-
|
|
29
|
-
for (const line of lines) {
|
|
30
|
-
const isHeader = /^(---|===|##|#)/.test(line.trim());
|
|
31
|
-
if (isHeader) {
|
|
32
|
-
inRelevant = relevantFiles.some((f) => line.includes(f));
|
|
33
|
-
}
|
|
34
|
-
if (inRelevant || relevantFiles.some((f) => line.includes(f))) {
|
|
35
|
-
kept.push(line);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return kept.length > 0 ? kept.join('\n') : context;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function hardCompress(context: string, relevantFiles: string[]): string {
|
|
43
|
-
const lines = context.split('\n');
|
|
44
|
-
const fileRefs = relevantFiles.length > 0
|
|
45
|
-
? lines.filter((l) => relevantFiles.some((f) => l.includes(f)))
|
|
46
|
-
: [];
|
|
47
|
-
const tail = lines.slice(-50);
|
|
48
|
-
const combined = [...new Set([...fileRefs, ...tail])];
|
|
49
|
-
return combined.join('\n');
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function compressContext(context: string, relevantFiles: string[] = []): CompressionResult {
|
|
53
|
-
const original_tokens = estimateTokens(context);
|
|
54
|
-
let strategy: CompressionStrategy;
|
|
55
|
-
let content: string;
|
|
56
|
-
|
|
57
|
-
if (original_tokens < 2000) {
|
|
58
|
-
strategy = 'passthrough';
|
|
59
|
-
content = context;
|
|
60
|
-
} else if (original_tokens < 8000) {
|
|
61
|
-
strategy = 'compress';
|
|
62
|
-
content = extractRelevantSections(context, relevantFiles);
|
|
63
|
-
} else {
|
|
64
|
-
strategy = 'hard-compress';
|
|
65
|
-
content = hardCompress(context, relevantFiles);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const compressed_tokens = estimateTokens(content);
|
|
69
|
-
const compression_ratio = original_tokens > 0
|
|
70
|
-
? Math.round((1 - compressed_tokens / original_tokens) * 100) / 100
|
|
71
|
-
: 0;
|
|
72
|
-
|
|
73
|
-
return { original_tokens, compressed_tokens, compression_ratio, content, strategy };
|
|
74
|
-
}
|
package/src/router/index.ts
DELETED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
// Router entry point — composes all sub-modules into a single routeTask() call
|
|
2
|
-
|
|
3
|
-
import { scoreComplexity } from './complexity-scorer.js';
|
|
4
|
-
import { selectModel } from './model-selector.js';
|
|
5
|
-
import { getRateStatus, trackRequest, getRoutingAdvice } from './rate-monitor.js';
|
|
6
|
-
import { compressContext, estimateTokens } from './context-compressor.js';
|
|
7
|
-
import { recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights } from './learning-updater.js';
|
|
8
|
-
|
|
9
|
-
export type { ComplexityResult, ComplexityFactors } from './complexity-scorer.js';
|
|
10
|
-
export type { AgentType, Tier, ModelRecommendation } from './model-selector.js';
|
|
11
|
-
export type { Platform, RateLimitEntry, RateStatus } from './rate-monitor.js';
|
|
12
|
-
export type { CompressionStrategy, CompressionResult } from './context-compressor.js';
|
|
13
|
-
export type { LearningStats, LearnedThresholds, AgentPerformanceStat, TaskTypeBreakdown, CouncilInsight } from './learning-updater.js';
|
|
14
|
-
export { estimateTokens, getRateStatus, trackRequest, recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights };
|
|
15
|
-
|
|
16
|
-
export type RouteOptions = {
|
|
17
|
-
agentType?: import('./model-selector.js').AgentType;
|
|
18
|
-
filesAffected?: number;
|
|
19
|
-
forceCouncil?: boolean;
|
|
20
|
-
context?: string;
|
|
21
|
-
relevantFiles?: string[];
|
|
22
|
-
preferredPlatform?: import('./rate-monitor.js').Platform;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export type RouteResult = {
|
|
26
|
-
complexity: import('./complexity-scorer.js').ComplexityResult;
|
|
27
|
-
model: import('./model-selector.js').ModelRecommendation;
|
|
28
|
-
rate_status: import('./rate-monitor.js').RateStatus;
|
|
29
|
-
context_plan?: import('./context-compressor.js').CompressionResult;
|
|
30
|
-
effective_platform: import('./rate-monitor.js').Platform;
|
|
31
|
-
routed_at: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export function routeTask(task: string, options: RouteOptions = {}): RouteResult {
|
|
35
|
-
const learned = getLearnedThresholds();
|
|
36
|
-
const complexity = scoreComplexity(task, options.filesAffected, options.forceCouncil, learned.source === 'learned' ? learned : undefined);
|
|
37
|
-
const model = selectModel(complexity.score, options.agentType ?? 'dynamic');
|
|
38
|
-
|
|
39
|
-
const preferred = options.preferredPlatform ?? 'claude';
|
|
40
|
-
// Only shift Tier 1/2 away from Claude on warning; Tier 3 always stays on best model
|
|
41
|
-
const effective_platform =
|
|
42
|
-
model.tier === 3 ? preferred : getRoutingAdvice(preferred);
|
|
43
|
-
|
|
44
|
-
trackRequest(effective_platform);
|
|
45
|
-
|
|
46
|
-
const rate_status = getRateStatus();
|
|
47
|
-
|
|
48
|
-
const context_plan = options.context
|
|
49
|
-
? compressContext(options.context, options.relevantFiles ?? [])
|
|
50
|
-
: undefined;
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
complexity,
|
|
54
|
-
model,
|
|
55
|
-
rate_status,
|
|
56
|
-
context_plan,
|
|
57
|
-
effective_platform,
|
|
58
|
-
routed_at: new Date().toISOString(),
|
|
59
|
-
};
|
|
60
|
-
}
|