@jigyasudham/veto 0.8.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/.claude/settings.local.json +9 -0
- package/README.md +190 -0
- package/dist/adapters/claude.js +57 -0
- package/dist/adapters/codex.js +58 -0
- package/dist/adapters/gemini.js +58 -0
- package/dist/adapters/index.js +156 -0
- package/dist/agents/development/api.js +116 -0
- package/dist/agents/development/backend.js +82 -0
- package/dist/agents/development/coder.js +207 -0
- package/dist/agents/development/database.js +81 -0
- package/dist/agents/development/debugger.js +234 -0
- package/dist/agents/development/devops.js +84 -0
- package/dist/agents/development/frontend.js +83 -0
- package/dist/agents/development/migration.js +141 -0
- package/dist/agents/development/performance.js +142 -0
- package/dist/agents/development/refactor.js +85 -0
- package/dist/agents/development/reviewer.js +260 -0
- package/dist/agents/development/tester.js +143 -0
- package/dist/agents/executor.js +144 -0
- package/dist/agents/memory/context-manager.js +167 -0
- package/dist/agents/memory/decision-logger.js +157 -0
- package/dist/agents/memory/knowledge-base.js +120 -0
- package/dist/agents/memory/pattern-learner.js +140 -0
- package/dist/agents/memory/project-mapper.js +114 -0
- package/dist/agents/quality/accessibility.js +89 -0
- package/dist/agents/quality/code-quality.js +109 -0
- package/dist/agents/quality/compatibility.js +55 -0
- package/dist/agents/quality/documentation.js +95 -0
- package/dist/agents/quality/error-handling.js +87 -0
- package/dist/agents/research/competitor-analyzer.js +44 -0
- package/dist/agents/research/cost-analyzer.js +51 -0
- package/dist/agents/research/estimator.js +57 -0
- package/dist/agents/research/ethics-bias.js +111 -0
- package/dist/agents/research/researcher.js +112 -0
- package/dist/agents/research/risk-assessor.js +61 -0
- package/dist/agents/research/tech-advisor.js +52 -0
- package/dist/agents/security/auth.js +269 -0
- package/dist/agents/security/dependency-audit.js +273 -0
- package/dist/agents/security/penetration.js +245 -0
- package/dist/agents/security/privacy.js +259 -0
- package/dist/agents/security/scanner.js +288 -0
- package/dist/agents/security/secrets.js +212 -0
- package/dist/agents/types.js +2 -0
- package/dist/agents/workflow/automation.js +56 -0
- package/dist/agents/workflow/file-manager.js +49 -0
- package/dist/agents/workflow/git-agent.js +52 -0
- package/dist/agents/workflow/reporter.js +48 -0
- package/dist/agents/workflow/search-agent.js +39 -0
- package/dist/agents/workflow/task-coordinator.js +40 -0
- package/dist/agents/workflow/task-planner.js +46 -0
- package/dist/cli.js +132 -0
- package/dist/council/decision-engine.js +136 -0
- package/dist/council/devil-advocate.js +106 -0
- package/dist/council/index.js +37 -0
- package/dist/council/lead-developer.js +108 -0
- package/dist/council/legal-compliance.js +142 -0
- package/dist/council/product-manager.js +92 -0
- package/dist/council/security.js +162 -0
- package/dist/council/system-architect.js +122 -0
- package/dist/council/types.js +2 -0
- package/dist/council/ux-designer.js +109 -0
- package/dist/memory/local.js +182 -0
- package/dist/memory/schema.js +116 -0
- package/dist/memory/sync.js +199 -0
- package/dist/router/complexity-scorer.js +78 -0
- package/dist/router/context-compressor.js +58 -0
- package/dist/router/index.js +29 -0
- package/dist/router/learning-updater.js +186 -0
- package/dist/router/model-selector.js +51 -0
- package/dist/router/rate-monitor.js +73 -0
- package/dist/server.js +949 -0
- package/dist/skills/development/skill-api-design.js +313 -0
- package/dist/skills/development/skill-auth.js +255 -0
- package/dist/skills/development/skill-ci-cd.js +2 -0
- package/dist/skills/development/skill-crud.js +193 -0
- package/dist/skills/development/skill-db-schema.js +2 -0
- package/dist/skills/development/skill-docker.js +2 -0
- package/dist/skills/development/skill-env-setup.js +2 -0
- package/dist/skills/development/skill-scaffold.js +299 -0
- package/dist/skills/intelligence/skill-complexity-score.js +66 -0
- package/dist/skills/intelligence/skill-cost-track.js +36 -0
- package/dist/skills/intelligence/skill-learning-loop.js +66 -0
- package/dist/skills/intelligence/skill-pattern-detect.js +35 -0
- package/dist/skills/intelligence/skill-rate-watch.js +58 -0
- package/dist/skills/memory/skill-context-compress.js +82 -0
- package/dist/skills/memory/skill-cross-sync.js +88 -0
- package/dist/skills/memory/skill-decision-log.js +103 -0
- package/dist/skills/memory/skill-session-restore.js +44 -0
- package/dist/skills/memory/skill-session-save.js +78 -0
- package/dist/skills/quality/skill-accessibility.js +2 -0
- package/dist/skills/quality/skill-code-review.js +60 -0
- package/dist/skills/quality/skill-docs-gen.js +2 -0
- package/dist/skills/quality/skill-perf-audit.js +2 -0
- package/dist/skills/quality/skill-security-scan.js +67 -0
- package/dist/skills/quality/skill-test-suite.js +274 -0
- package/dist/skills/workflow/skill-deploy.js +2 -0
- package/dist/skills/workflow/skill-git-workflow.js +2 -0
- package/dist/skills/workflow/skill-rollback.js +2 -0
- package/dist/skills/workflow/skill-task-breakdown.js +2 -0
- package/package.json +30 -0
- package/src/adapters/claude.ts +70 -0
- package/src/adapters/codex.ts +71 -0
- package/src/adapters/gemini.ts +71 -0
- package/src/adapters/index.ts +217 -0
- package/src/agents/development/api.ts +120 -0
- package/src/agents/development/backend.ts +85 -0
- package/src/agents/development/coder.ts +213 -0
- package/src/agents/development/database.ts +83 -0
- package/src/agents/development/debugger.ts +238 -0
- package/src/agents/development/devops.ts +86 -0
- package/src/agents/development/frontend.ts +85 -0
- package/src/agents/development/migration.ts +144 -0
- package/src/agents/development/performance.ts +144 -0
- package/src/agents/development/refactor.ts +86 -0
- package/src/agents/development/reviewer.ts +268 -0
- package/src/agents/development/tester.ts +151 -0
- package/src/agents/executor.ts +158 -0
- package/src/agents/memory/context-manager.ts +171 -0
- package/src/agents/memory/decision-logger.ts +160 -0
- package/src/agents/memory/knowledge-base.ts +124 -0
- package/src/agents/memory/pattern-learner.ts +143 -0
- package/src/agents/memory/project-mapper.ts +118 -0
- package/src/agents/quality/accessibility.ts +99 -0
- package/src/agents/quality/code-quality.ts +115 -0
- package/src/agents/quality/compatibility.ts +58 -0
- package/src/agents/quality/documentation.ts +105 -0
- package/src/agents/quality/error-handling.ts +96 -0
- package/src/agents/research/competitor-analyzer.ts +45 -0
- package/src/agents/research/cost-analyzer.ts +54 -0
- package/src/agents/research/estimator.ts +60 -0
- package/src/agents/research/ethics-bias.ts +113 -0
- package/src/agents/research/researcher.ts +114 -0
- package/src/agents/research/risk-assessor.ts +63 -0
- package/src/agents/research/tech-advisor.ts +55 -0
- package/src/agents/security/auth.ts +287 -0
- package/src/agents/security/dependency-audit.ts +337 -0
- package/src/agents/security/penetration.ts +262 -0
- package/src/agents/security/privacy.ts +285 -0
- package/src/agents/security/scanner.ts +322 -0
- package/src/agents/security/secrets.ts +249 -0
- package/src/agents/types.ts +66 -0
- package/src/agents/workflow/automation.ts +59 -0
- package/src/agents/workflow/file-manager.ts +52 -0
- package/src/agents/workflow/git-agent.ts +55 -0
- package/src/agents/workflow/reporter.ts +51 -0
- package/src/agents/workflow/search-agent.ts +40 -0
- package/src/agents/workflow/task-coordinator.ts +41 -0
- package/src/agents/workflow/task-planner.ts +47 -0
- package/src/cli.ts +143 -0
- package/src/council/decision-engine.ts +171 -0
- package/src/council/devil-advocate.ts +116 -0
- package/src/council/index.ts +44 -0
- package/src/council/lead-developer.ts +118 -0
- package/src/council/legal-compliance.ts +152 -0
- package/src/council/product-manager.ts +102 -0
- package/src/council/security.ts +172 -0
- package/src/council/system-architect.ts +132 -0
- package/src/council/types.ts +33 -0
- package/src/council/ux-designer.ts +121 -0
- package/src/memory/local.ts +305 -0
- package/src/memory/schema.ts +174 -0
- package/src/memory/sync.ts +274 -0
- package/src/router/complexity-scorer.ts +96 -0
- package/src/router/context-compressor.ts +74 -0
- package/src/router/index.ts +60 -0
- package/src/router/learning-updater.ts +271 -0
- package/src/router/model-selector.ts +83 -0
- package/src/router/rate-monitor.ts +103 -0
- package/src/server.ts +1038 -0
- package/src/skills/development/skill-api-design.ts +329 -0
- package/src/skills/development/skill-auth.ts +271 -0
- package/src/skills/development/skill-ci-cd.ts +0 -0
- package/src/skills/development/skill-crud.ts +209 -0
- 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 +323 -0
- package/src/skills/intelligence/skill-complexity-score.ts +69 -0
- package/src/skills/intelligence/skill-cost-track.ts +39 -0
- package/src/skills/intelligence/skill-learning-loop.ts +69 -0
- package/src/skills/intelligence/skill-pattern-detect.ts +38 -0
- package/src/skills/intelligence/skill-rate-watch.ts +61 -0
- package/src/skills/memory/skill-context-compress.ts +98 -0
- package/src/skills/memory/skill-cross-sync.ts +104 -0
- package/src/skills/memory/skill-decision-log.ts +119 -0
- package/src/skills/memory/skill-session-restore.ts +59 -0
- package/src/skills/memory/skill-session-save.ts +94 -0
- package/src/skills/quality/skill-accessibility.ts +0 -0
- package/src/skills/quality/skill-code-review.ts +84 -0
- 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 +91 -0
- package/src/skills/quality/skill-test-suite.ts +290 -0
- 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 +20 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Scores a task description 0-100 locally — zero tokens spent
|
|
2
|
+
// 5 pts each — strong signal of high complexity
|
|
3
|
+
const HIGH_KEYWORDS = [
|
|
4
|
+
'architecture', 'security', 'authenticat', 'authorizat', 'migrat',
|
|
5
|
+
'refactor', 'scalab', 'concurrent', 'distributed', 'oauth', 'jwt',
|
|
6
|
+
'rbac', 'encryption', 'cryptograph', 'penetration', 'vulnerabilit',
|
|
7
|
+
'infrastructure', 'compliance', 'performance',
|
|
8
|
+
];
|
|
9
|
+
// 2 pts each — moderate complexity signal
|
|
10
|
+
const MEDIUM_KEYWORDS = [
|
|
11
|
+
'component', 'function', 'test', 'api', 'database', 'query', 'schema',
|
|
12
|
+
'deploy', 'docker', 'implement', 'build', 'feature', 'module',
|
|
13
|
+
'service', 'endpoint', 'integration', 'configuration',
|
|
14
|
+
];
|
|
15
|
+
// Council is always required for these topics
|
|
16
|
+
const COUNCIL_TRIGGERS = [
|
|
17
|
+
'architecture', 'security', 'auth', 'migrat', 'vulnerabilit',
|
|
18
|
+
'encryption', 'infrastructure', 'breaking change', 'drop table',
|
|
19
|
+
'delete all', 'remove all',
|
|
20
|
+
];
|
|
21
|
+
export function scoreComplexity(task, filesAffected = 1, forceCouncil = false, thresholds) {
|
|
22
|
+
const TIER1_MAX = thresholds?.tier1_max ?? 30;
|
|
23
|
+
const TIER2_MAX = thresholds?.tier2_max ?? 70;
|
|
24
|
+
const lower = task.toLowerCase();
|
|
25
|
+
const words = lower.split(/\s+/).filter(Boolean);
|
|
26
|
+
// Factor 1: word count → 0-20 pts (1 pt per 3 words, cap 20)
|
|
27
|
+
const word_count_score = Math.min(20, Math.floor(words.length / 3));
|
|
28
|
+
// Factor 2: technical keywords → 0-30 pts
|
|
29
|
+
let keyword_score = 0;
|
|
30
|
+
let council_required = forceCouncil;
|
|
31
|
+
for (const word of words) {
|
|
32
|
+
for (const kw of HIGH_KEYWORDS) {
|
|
33
|
+
if (word.includes(kw)) {
|
|
34
|
+
keyword_score += 5;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (const kw of MEDIUM_KEYWORDS) {
|
|
39
|
+
if (word.includes(kw)) {
|
|
40
|
+
keyword_score += 2;
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const trigger of COUNCIL_TRIGGERS) {
|
|
46
|
+
if (lower.includes(trigger)) {
|
|
47
|
+
council_required = true;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
keyword_score = Math.min(30, keyword_score);
|
|
52
|
+
// Factor 3: task depth → 0-20 pts (conjunctions, length, multi-step)
|
|
53
|
+
let depth_score = 0;
|
|
54
|
+
if (lower.includes(' and ') || lower.includes(' with '))
|
|
55
|
+
depth_score += 5;
|
|
56
|
+
if (lower.includes(',') || lower.includes(' plus '))
|
|
57
|
+
depth_score += 5;
|
|
58
|
+
if (words.length > 20)
|
|
59
|
+
depth_score += 5;
|
|
60
|
+
if (words.length > 40)
|
|
61
|
+
depth_score += 5;
|
|
62
|
+
depth_score = Math.min(20, depth_score);
|
|
63
|
+
// Factor 4: files affected → 0-20 pts
|
|
64
|
+
const files_score = Math.min(20, (filesAffected - 1) * 3);
|
|
65
|
+
// Factor 5: council bonus — pushes score into Tier 3 territory
|
|
66
|
+
const council_bonus = council_required ? 10 : 0;
|
|
67
|
+
const raw = word_count_score + keyword_score + depth_score + files_score + council_bonus;
|
|
68
|
+
// Council tasks always land in Tier 3 — floor at 71
|
|
69
|
+
const score = Math.min(100, council_required ? Math.max(71, raw) : raw);
|
|
70
|
+
const tier = score <= TIER1_MAX ? 1 : score <= TIER2_MAX ? 2 : 3;
|
|
71
|
+
return {
|
|
72
|
+
score,
|
|
73
|
+
tier,
|
|
74
|
+
factors: { word_count_score, keyword_score, depth_score, files_score, council_bonus },
|
|
75
|
+
council_required,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=complexity-scorer.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
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
|
+
// Rough 4-chars-per-token estimate — good enough for routing decisions
|
|
7
|
+
export function estimateTokens(text) {
|
|
8
|
+
return Math.ceil(text.length / 4);
|
|
9
|
+
}
|
|
10
|
+
function extractRelevantSections(context, relevantFiles) {
|
|
11
|
+
if (relevantFiles.length === 0)
|
|
12
|
+
return context;
|
|
13
|
+
const lines = context.split('\n');
|
|
14
|
+
const kept = [];
|
|
15
|
+
let inRelevant = false;
|
|
16
|
+
for (const line of lines) {
|
|
17
|
+
const isHeader = /^(---|===|##|#)/.test(line.trim());
|
|
18
|
+
if (isHeader) {
|
|
19
|
+
inRelevant = relevantFiles.some((f) => line.includes(f));
|
|
20
|
+
}
|
|
21
|
+
if (inRelevant || relevantFiles.some((f) => line.includes(f))) {
|
|
22
|
+
kept.push(line);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return kept.length > 0 ? kept.join('\n') : context;
|
|
26
|
+
}
|
|
27
|
+
function hardCompress(context, relevantFiles) {
|
|
28
|
+
const lines = context.split('\n');
|
|
29
|
+
const fileRefs = relevantFiles.length > 0
|
|
30
|
+
? lines.filter((l) => relevantFiles.some((f) => l.includes(f)))
|
|
31
|
+
: [];
|
|
32
|
+
const tail = lines.slice(-50);
|
|
33
|
+
const combined = [...new Set([...fileRefs, ...tail])];
|
|
34
|
+
return combined.join('\n');
|
|
35
|
+
}
|
|
36
|
+
export function compressContext(context, relevantFiles = []) {
|
|
37
|
+
const original_tokens = estimateTokens(context);
|
|
38
|
+
let strategy;
|
|
39
|
+
let content;
|
|
40
|
+
if (original_tokens < 2000) {
|
|
41
|
+
strategy = 'passthrough';
|
|
42
|
+
content = context;
|
|
43
|
+
}
|
|
44
|
+
else if (original_tokens < 8000) {
|
|
45
|
+
strategy = 'compress';
|
|
46
|
+
content = extractRelevantSections(context, relevantFiles);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
strategy = 'hard-compress';
|
|
50
|
+
content = hardCompress(context, relevantFiles);
|
|
51
|
+
}
|
|
52
|
+
const compressed_tokens = estimateTokens(content);
|
|
53
|
+
const compression_ratio = original_tokens > 0
|
|
54
|
+
? Math.round((1 - compressed_tokens / original_tokens) * 100) / 100
|
|
55
|
+
: 0;
|
|
56
|
+
return { original_tokens, compressed_tokens, compression_ratio, content, strategy };
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=context-compressor.js.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Router entry point — composes all sub-modules into a single routeTask() call
|
|
2
|
+
import { scoreComplexity } from './complexity-scorer.js';
|
|
3
|
+
import { selectModel } from './model-selector.js';
|
|
4
|
+
import { getRateStatus, trackRequest, getRoutingAdvice } from './rate-monitor.js';
|
|
5
|
+
import { compressContext, estimateTokens } from './context-compressor.js';
|
|
6
|
+
import { recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights } from './learning-updater.js';
|
|
7
|
+
export { estimateTokens, getRateStatus, trackRequest, recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights };
|
|
8
|
+
export function routeTask(task, options = {}) {
|
|
9
|
+
const learned = getLearnedThresholds();
|
|
10
|
+
const complexity = scoreComplexity(task, options.filesAffected, options.forceCouncil, learned.source === 'learned' ? learned : undefined);
|
|
11
|
+
const model = selectModel(complexity.score, options.agentType ?? 'dynamic');
|
|
12
|
+
const preferred = options.preferredPlatform ?? 'claude';
|
|
13
|
+
// Only shift Tier 1/2 away from Claude on warning; Tier 3 always stays on best model
|
|
14
|
+
const effective_platform = model.tier === 3 ? preferred : getRoutingAdvice(preferred);
|
|
15
|
+
trackRequest(effective_platform);
|
|
16
|
+
const rate_status = getRateStatus();
|
|
17
|
+
const context_plan = options.context
|
|
18
|
+
? compressContext(options.context, options.relevantFiles ?? [])
|
|
19
|
+
: undefined;
|
|
20
|
+
return {
|
|
21
|
+
complexity,
|
|
22
|
+
model,
|
|
23
|
+
rate_status,
|
|
24
|
+
context_plan,
|
|
25
|
+
effective_platform,
|
|
26
|
+
routed_at: new Date().toISOString(),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// Records routing outcomes to SQLite and adjusts tier thresholds from historical quality data
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { getDb } from '../memory/local.js';
|
|
4
|
+
// ─── Record ───────────────────────────────────────────────────────────────────
|
|
5
|
+
export function recordOutcome(taskType, complexity, modelTier, agent, outputQuality, tokensUsed = 0) {
|
|
6
|
+
const db = getDb();
|
|
7
|
+
db.prepare(`INSERT INTO learning_data (id, task_type, complexity, model_tier, output_quality, tokens_used, agent)
|
|
8
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), taskType, complexity, modelTier, outputQuality, tokensUsed, agent);
|
|
9
|
+
}
|
|
10
|
+
// ─── Stats ────────────────────────────────────────────────────────────────────
|
|
11
|
+
export function getLearningStats() {
|
|
12
|
+
const db = getDb();
|
|
13
|
+
const rows = db.prepare(`SELECT model_tier, COUNT(*) as count, AVG(output_quality) as avg_quality
|
|
14
|
+
FROM learning_data GROUP BY model_tier`).all();
|
|
15
|
+
const total_tasks = rows.reduce((sum, r) => sum + r.count, 0);
|
|
16
|
+
const tier_breakdown = {};
|
|
17
|
+
for (const r of rows) {
|
|
18
|
+
tier_breakdown[r.model_tier] = {
|
|
19
|
+
count: r.count,
|
|
20
|
+
avg_quality: Math.round((r.avg_quality ?? 0) * 100) / 100,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const thresholds = computeSuggestedThresholds(tier_breakdown);
|
|
24
|
+
return { total_tasks, tier_breakdown, suggested_thresholds: thresholds };
|
|
25
|
+
}
|
|
26
|
+
export function getAgentPerformanceStats() {
|
|
27
|
+
const db = getDb();
|
|
28
|
+
const rows = db.prepare(`
|
|
29
|
+
SELECT agent,
|
|
30
|
+
COUNT(*) as task_count,
|
|
31
|
+
AVG(output_quality) as avg_quality,
|
|
32
|
+
AVG(tokens_used) as avg_tokens,
|
|
33
|
+
SUM(CASE WHEN output_quality < 60 THEN 1 ELSE 0 END) as low_quality_count
|
|
34
|
+
FROM learning_data
|
|
35
|
+
WHERE agent IS NOT NULL
|
|
36
|
+
GROUP BY agent
|
|
37
|
+
ORDER BY avg_quality ASC
|
|
38
|
+
`).all();
|
|
39
|
+
return rows.map(r => ({
|
|
40
|
+
agent: r.agent,
|
|
41
|
+
task_count: r.task_count,
|
|
42
|
+
avg_quality: Math.round(r.avg_quality * 10) / 10,
|
|
43
|
+
avg_tokens: Math.round(r.avg_tokens),
|
|
44
|
+
low_quality_count: r.low_quality_count,
|
|
45
|
+
needs_attention: r.avg_quality < 70 || (r.low_quality_count / r.task_count) > 0.25,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
export function getTaskTypeBreakdown() {
|
|
49
|
+
const db = getDb();
|
|
50
|
+
const rows = db.prepare(`
|
|
51
|
+
SELECT task_type,
|
|
52
|
+
COUNT(*) as count,
|
|
53
|
+
AVG(complexity) as avg_complexity,
|
|
54
|
+
AVG(output_quality) as avg_quality,
|
|
55
|
+
model_tier as dominant_tier
|
|
56
|
+
FROM learning_data
|
|
57
|
+
GROUP BY task_type, model_tier
|
|
58
|
+
HAVING COUNT(*) = (
|
|
59
|
+
SELECT MAX(c) FROM (
|
|
60
|
+
SELECT COUNT(*) as c FROM learning_data l2
|
|
61
|
+
WHERE l2.task_type = learning_data.task_type
|
|
62
|
+
GROUP BY l2.model_tier
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
ORDER BY count DESC
|
|
66
|
+
LIMIT 50
|
|
67
|
+
`).all();
|
|
68
|
+
return rows.map(r => {
|
|
69
|
+
// Under-tiered: task scored high quality but at a tier below what complexity suggests
|
|
70
|
+
const expectedTier = r.avg_complexity <= 30 ? 1 : r.avg_complexity <= 70 ? 2 : 3;
|
|
71
|
+
const under_tiered = r.dominant_tier < expectedTier && r.avg_quality < 70;
|
|
72
|
+
return {
|
|
73
|
+
task_type: r.task_type,
|
|
74
|
+
count: r.count,
|
|
75
|
+
avg_complexity: Math.round(r.avg_complexity),
|
|
76
|
+
avg_quality: Math.round(r.avg_quality * 10) / 10,
|
|
77
|
+
dominant_tier: r.dominant_tier,
|
|
78
|
+
under_tiered,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
export function getCouncilInsights() {
|
|
83
|
+
const db = getDb();
|
|
84
|
+
// Correlate council approvals (GREEN/YELLOW) with debugging sessions that came after
|
|
85
|
+
const rows = db.prepare(`
|
|
86
|
+
SELECT co.verdict, co.task, co.debated_at,
|
|
87
|
+
d.decision as related_decision, d.made_at as decision_at
|
|
88
|
+
FROM council_outcomes co
|
|
89
|
+
LEFT JOIN decisions d ON d.session_id = co.session_id
|
|
90
|
+
AND d.decision LIKE '%debug%' OR d.decision LIKE '%fix%' OR d.decision LIKE '%bug%'
|
|
91
|
+
WHERE co.verdict IN ('GREEN', 'YELLOW')
|
|
92
|
+
ORDER BY co.debated_at DESC
|
|
93
|
+
LIMIT 30
|
|
94
|
+
`).all();
|
|
95
|
+
return rows.map(r => {
|
|
96
|
+
let days_to_debug = null;
|
|
97
|
+
if (r.decision_at) {
|
|
98
|
+
const debated = new Date(r.debated_at).getTime();
|
|
99
|
+
const decided = new Date(r.decision_at).getTime();
|
|
100
|
+
days_to_debug = Math.round((decided - debated) / (1000 * 60 * 60 * 24));
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
verdict: r.verdict,
|
|
104
|
+
task_snippet: r.task.substring(0, 80),
|
|
105
|
+
debated_at: r.debated_at,
|
|
106
|
+
days_to_debug,
|
|
107
|
+
related_decision: r.related_decision,
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
// ─── Threshold Computation ────────────────────────────────────────────────────
|
|
112
|
+
function computeSuggestedThresholds(tier_breakdown) {
|
|
113
|
+
const t1 = tier_breakdown[1];
|
|
114
|
+
const t2 = tier_breakdown[2];
|
|
115
|
+
const t3 = tier_breakdown[3];
|
|
116
|
+
let tier1_max = 30;
|
|
117
|
+
let tier2_max = 70;
|
|
118
|
+
// Tier 2 consistently great → can handle more tasks, raise Tier 1 ceiling slightly
|
|
119
|
+
if (t2 && t2.count >= 20 && t2.avg_quality >= 88)
|
|
120
|
+
tier1_max = 35;
|
|
121
|
+
// Tier 2 quality poor → tighten Tier 1 ceiling (fewer tasks escape to T2)
|
|
122
|
+
if (t2 && t2.count >= 10 && t2.avg_quality < 65)
|
|
123
|
+
tier1_max = 25;
|
|
124
|
+
// Tier 3 consistently great → can push harder tasks to T3, lower T2 ceiling
|
|
125
|
+
if (t3 && t3.count >= 10 && t3.avg_quality >= 88)
|
|
126
|
+
tier2_max = 65;
|
|
127
|
+
// Tier 3 quality poor → be more conservative, raise T2 ceiling (keep more in T2)
|
|
128
|
+
if (t3 && t3.count >= 10 && t3.avg_quality < 65)
|
|
129
|
+
tier2_max = 75;
|
|
130
|
+
// Tier 1 quality poor → lower its ceiling (fewer tasks stay at T1)
|
|
131
|
+
if (t1 && t1.count >= 10 && t1.avg_quality < 65)
|
|
132
|
+
tier1_max = Math.max(20, tier1_max - 5);
|
|
133
|
+
return { tier1_max, tier2_max };
|
|
134
|
+
}
|
|
135
|
+
// ─── Apply ────────────────────────────────────────────────────────────────────
|
|
136
|
+
export function applyLearnedThresholds() {
|
|
137
|
+
const db = getDb();
|
|
138
|
+
const total = db.prepare('SELECT COUNT(*) as c FROM learning_data').get().c;
|
|
139
|
+
// Need at least 20 data points before adjusting
|
|
140
|
+
if (total < 20) {
|
|
141
|
+
return {
|
|
142
|
+
applied: false,
|
|
143
|
+
thresholds: { tier1_max: 30, tier2_max: 70 },
|
|
144
|
+
data_points: total,
|
|
145
|
+
reason: `Not enough data yet. Need 20 task outcomes, have ${total}. Record outcomes via veto_record_outcome.`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const stats = getLearningStats();
|
|
149
|
+
const { tier1_max, tier2_max } = stats.suggested_thresholds;
|
|
150
|
+
// Persist to patterns table so the router picks them up on next call
|
|
151
|
+
const now = new Date().toISOString();
|
|
152
|
+
for (const [key, val] of [['router.tier1_max', String(tier1_max)], ['router.tier2_max', String(tier2_max)]]) {
|
|
153
|
+
const existing = db.prepare('SELECT id FROM patterns WHERE pattern_key = ?').get(key);
|
|
154
|
+
if (existing) {
|
|
155
|
+
db.prepare('UPDATE patterns SET pattern_val = ?, updated_at = ? WHERE pattern_key = ?').run(val, now, key);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
db.prepare('INSERT INTO patterns (id, pattern_key, pattern_val, confidence, seen_count, updated_at) VALUES (?, ?, ?, ?, ?, ?)')
|
|
159
|
+
.run(randomUUID(), key, val, 1.0, total, now);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
applied: true,
|
|
164
|
+
thresholds: { tier1_max, tier2_max },
|
|
165
|
+
data_points: total,
|
|
166
|
+
reason: `Thresholds updated from ${total} task outcomes. Default was 30/70; learned is ${tier1_max}/${tier2_max}.`,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
export function getLearnedThresholds() {
|
|
170
|
+
const db = getDb();
|
|
171
|
+
const t1row = db.prepare('SELECT pattern_val, seen_count FROM patterns WHERE pattern_key = ?').get('router.tier1_max');
|
|
172
|
+
const t2row = db.prepare('SELECT pattern_val, seen_count FROM patterns WHERE pattern_key = ?').get('router.tier2_max');
|
|
173
|
+
if (!t1row || !t2row) {
|
|
174
|
+
return { tier1_max: 30, tier2_max: 70, source: 'default', data_points: 0 };
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
tier1_max: parseInt(t1row.pattern_val, 10),
|
|
178
|
+
tier2_max: parseInt(t2row.pattern_val, 10),
|
|
179
|
+
source: 'learned',
|
|
180
|
+
data_points: t1row.seen_count,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
export function getSuggestedThresholds() {
|
|
184
|
+
return getLearningStats().suggested_thresholds;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=learning-updater.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Assigns Tier 1/2/3 and specific model recommendations
|
|
2
|
+
// Some agents are permanently locked to a tier regardless of complexity score
|
|
3
|
+
const TIER3_LOCKED = new Set([
|
|
4
|
+
'lead-developer', 'system-architect', 'security-scanner',
|
|
5
|
+
'devil-advocate', 'decision-engine', 'risk-assessor',
|
|
6
|
+
]);
|
|
7
|
+
const TIER2_LOCKED = new Set([
|
|
8
|
+
'coder', 'tester', 'reviewer', 'database', 'documentation',
|
|
9
|
+
]);
|
|
10
|
+
const TIER1_LOCKED = new Set([
|
|
11
|
+
'file-manager', 'git-agent', 'search-agent', 'secrets', 'reporter',
|
|
12
|
+
]);
|
|
13
|
+
const TIER_MODELS = {
|
|
14
|
+
1: { claude: 'claude-haiku-4-5', gemini: 'gemini-2.0-flash', codex: 'gpt-4o-mini' },
|
|
15
|
+
2: { claude: 'claude-sonnet-4-6', gemini: 'gemini-2.0-pro', codex: 'gpt-4o' },
|
|
16
|
+
3: { claude: 'claude-sonnet-4-6', gemini: 'gemini-2.0-advanced', codex: 'gpt-4o' },
|
|
17
|
+
};
|
|
18
|
+
export function selectModel(complexityScore, agentType = 'dynamic') {
|
|
19
|
+
if (TIER3_LOCKED.has(agentType)) {
|
|
20
|
+
return {
|
|
21
|
+
tier: 3,
|
|
22
|
+
models: TIER_MODELS[3],
|
|
23
|
+
reason: `${agentType} is always Tier 3 — stakes too high for cheaper models`,
|
|
24
|
+
agent_locked: true,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (TIER2_LOCKED.has(agentType)) {
|
|
28
|
+
return {
|
|
29
|
+
tier: 2,
|
|
30
|
+
models: TIER_MODELS[2],
|
|
31
|
+
reason: `${agentType} is always Tier 2 — balanced complexity`,
|
|
32
|
+
agent_locked: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (TIER1_LOCKED.has(agentType)) {
|
|
36
|
+
return {
|
|
37
|
+
tier: 1,
|
|
38
|
+
models: TIER_MODELS[1],
|
|
39
|
+
reason: `${agentType} is always Tier 1 — simple structured operations`,
|
|
40
|
+
agent_locked: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const tier = complexityScore <= 30 ? 1 : complexityScore <= 70 ? 2 : 3;
|
|
44
|
+
const reason = tier === 1
|
|
45
|
+
? `Score ${complexityScore}/100 — simple task, use fastest/cheapest model`
|
|
46
|
+
: tier === 2
|
|
47
|
+
? `Score ${complexityScore}/100 — mid-range task, use balanced model`
|
|
48
|
+
: `Score ${complexityScore}/100 — complex task, use best available model`;
|
|
49
|
+
return { tier, models: TIER_MODELS[tier], reason, agent_locked: false };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=model-selector.js.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Tracks request counts per platform per day, exposes routing advice
|
|
2
|
+
// Veto cannot read actual API rate limits — it tracks its own routed requests
|
|
3
|
+
// and lets users configure their daily limits in patterns table
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { getDb } from '../memory/local.js';
|
|
6
|
+
const DEFAULT_LIMITS = {
|
|
7
|
+
claude: 100,
|
|
8
|
+
gemini: 200,
|
|
9
|
+
codex: 150,
|
|
10
|
+
};
|
|
11
|
+
function getTodayKey() {
|
|
12
|
+
return new Date().toISOString().slice(0, 10); // YYYY-MM-DD
|
|
13
|
+
}
|
|
14
|
+
function getNextResetISO() {
|
|
15
|
+
const reset = new Date();
|
|
16
|
+
reset.setUTCHours(0, 0, 0, 0);
|
|
17
|
+
reset.setUTCDate(reset.getUTCDate() + 1);
|
|
18
|
+
return reset.toISOString();
|
|
19
|
+
}
|
|
20
|
+
export function trackRequest(platform, count = 1) {
|
|
21
|
+
const db = getDb();
|
|
22
|
+
const today = getTodayKey();
|
|
23
|
+
const existing = db.prepare('SELECT id, request_count FROM rate_usage WHERE platform = ? AND date_key = ?').get(platform, today);
|
|
24
|
+
if (existing) {
|
|
25
|
+
db.prepare('UPDATE rate_usage SET request_count = ?, updated_at = ? WHERE id = ?').run(existing.request_count + count, new Date().toISOString(), existing.id);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
db.prepare('INSERT INTO rate_usage (id, platform, date_key, request_count) VALUES (?, ?, ?, ?)').run(randomUUID(), platform, today, count);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function getRequestCount(platform) {
|
|
32
|
+
const db = getDb();
|
|
33
|
+
const row = db.prepare('SELECT request_count FROM rate_usage WHERE platform = ? AND date_key = ?').get(platform, getTodayKey());
|
|
34
|
+
return row?.request_count ?? 0;
|
|
35
|
+
}
|
|
36
|
+
function buildEntry(platform) {
|
|
37
|
+
const requests_today = getRequestCount(platform);
|
|
38
|
+
const daily_limit = DEFAULT_LIMITS[platform];
|
|
39
|
+
const used_percent = Math.min(100, Math.round((requests_today / daily_limit) * 100));
|
|
40
|
+
let status;
|
|
41
|
+
if (used_percent >= 90)
|
|
42
|
+
status = 'critical';
|
|
43
|
+
else if (used_percent >= 70)
|
|
44
|
+
status = 'warning';
|
|
45
|
+
else
|
|
46
|
+
status = 'normal';
|
|
47
|
+
return { platform, requests_today, daily_limit, used_percent, resets_at: getNextResetISO(), status };
|
|
48
|
+
}
|
|
49
|
+
export function getRateStatus() {
|
|
50
|
+
return {
|
|
51
|
+
claude: buildEntry('claude'),
|
|
52
|
+
gemini: buildEntry('gemini'),
|
|
53
|
+
codex: buildEntry('codex'),
|
|
54
|
+
updated_at: new Date().toISOString(),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// Returns a fallback platform when the preferred one is at warning/critical level
|
|
58
|
+
export function getRoutingAdvice(preferred) {
|
|
59
|
+
const entry = buildEntry(preferred);
|
|
60
|
+
if (entry.status === 'critical') {
|
|
61
|
+
if (preferred === 'claude')
|
|
62
|
+
return 'gemini';
|
|
63
|
+
if (preferred === 'gemini')
|
|
64
|
+
return 'codex';
|
|
65
|
+
return 'claude';
|
|
66
|
+
}
|
|
67
|
+
if (entry.status === 'warning' && preferred === 'claude') {
|
|
68
|
+
// Tier 1+2 tasks shift to Gemini; caller decides based on tier
|
|
69
|
+
return 'gemini';
|
|
70
|
+
}
|
|
71
|
+
return preferred;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=rate-monitor.js.map
|