@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
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
// Records routing outcomes to SQLite and adjusts tier thresholds from historical quality data
|
|
2
|
-
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import { getDb } from '../memory/local.js';
|
|
5
|
-
|
|
6
|
-
export type LearningStats = {
|
|
7
|
-
total_tasks: number;
|
|
8
|
-
tier_breakdown: Record<number, { count: number; avg_quality: number }>;
|
|
9
|
-
suggested_thresholds: { tier1_max: number; tier2_max: number };
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export type AgentPerformanceStat = {
|
|
13
|
-
agent: string;
|
|
14
|
-
task_count: number;
|
|
15
|
-
avg_quality: number;
|
|
16
|
-
avg_tokens: number;
|
|
17
|
-
low_quality_count: number;
|
|
18
|
-
needs_attention: boolean;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export type TaskTypeBreakdown = {
|
|
22
|
-
task_type: string;
|
|
23
|
-
count: number;
|
|
24
|
-
avg_complexity: number;
|
|
25
|
-
avg_quality: number;
|
|
26
|
-
dominant_tier: number;
|
|
27
|
-
under_tiered: boolean;
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export type CouncilInsight = {
|
|
31
|
-
verdict: string;
|
|
32
|
-
task_snippet: string;
|
|
33
|
-
debated_at: string;
|
|
34
|
-
days_to_debug: number | null;
|
|
35
|
-
related_decision: string | null;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export type LearnedThresholds = {
|
|
39
|
-
tier1_max: number;
|
|
40
|
-
tier2_max: number;
|
|
41
|
-
source: 'learned' | 'default';
|
|
42
|
-
data_points: number;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
// ─── Record ───────────────────────────────────────────────────────────────────
|
|
46
|
-
|
|
47
|
-
export function recordOutcome(
|
|
48
|
-
taskType: string,
|
|
49
|
-
complexity: number,
|
|
50
|
-
modelTier: 1 | 2 | 3,
|
|
51
|
-
agent: string,
|
|
52
|
-
outputQuality: number,
|
|
53
|
-
tokensUsed = 0
|
|
54
|
-
): void {
|
|
55
|
-
const db = getDb();
|
|
56
|
-
db.prepare(
|
|
57
|
-
`INSERT INTO learning_data (id, task_type, complexity, model_tier, output_quality, tokens_used, agent)
|
|
58
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
59
|
-
).run(randomUUID(), taskType, complexity, modelTier, outputQuality, tokensUsed, agent);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ─── Stats ────────────────────────────────────────────────────────────────────
|
|
63
|
-
|
|
64
|
-
export function getLearningStats(): LearningStats {
|
|
65
|
-
const db = getDb();
|
|
66
|
-
const rows = db.prepare(
|
|
67
|
-
`SELECT model_tier, COUNT(*) as count, AVG(output_quality) as avg_quality
|
|
68
|
-
FROM learning_data GROUP BY model_tier`
|
|
69
|
-
).all() as Array<{ model_tier: number; count: number; avg_quality: number }>;
|
|
70
|
-
|
|
71
|
-
const total_tasks = rows.reduce((sum, r) => sum + r.count, 0);
|
|
72
|
-
const tier_breakdown: Record<number, { count: number; avg_quality: number }> = {};
|
|
73
|
-
for (const r of rows) {
|
|
74
|
-
tier_breakdown[r.model_tier] = {
|
|
75
|
-
count: r.count,
|
|
76
|
-
avg_quality: Math.round((r.avg_quality ?? 0) * 100) / 100,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const thresholds = computeSuggestedThresholds(tier_breakdown);
|
|
81
|
-
return { total_tasks, tier_breakdown, suggested_thresholds: thresholds };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function getAgentPerformanceStats(): AgentPerformanceStat[] {
|
|
85
|
-
const db = getDb();
|
|
86
|
-
const rows = db.prepare(`
|
|
87
|
-
SELECT agent,
|
|
88
|
-
COUNT(*) as task_count,
|
|
89
|
-
AVG(output_quality) as avg_quality,
|
|
90
|
-
AVG(tokens_used) as avg_tokens,
|
|
91
|
-
SUM(CASE WHEN output_quality < 60 THEN 1 ELSE 0 END) as low_quality_count
|
|
92
|
-
FROM learning_data
|
|
93
|
-
WHERE agent IS NOT NULL
|
|
94
|
-
GROUP BY agent
|
|
95
|
-
ORDER BY avg_quality ASC
|
|
96
|
-
`).all() as Array<{
|
|
97
|
-
agent: string; task_count: number; avg_quality: number;
|
|
98
|
-
avg_tokens: number; low_quality_count: number;
|
|
99
|
-
}>;
|
|
100
|
-
|
|
101
|
-
return rows.map(r => ({
|
|
102
|
-
agent: r.agent,
|
|
103
|
-
task_count: r.task_count,
|
|
104
|
-
avg_quality: Math.round(r.avg_quality * 10) / 10,
|
|
105
|
-
avg_tokens: Math.round(r.avg_tokens),
|
|
106
|
-
low_quality_count: r.low_quality_count,
|
|
107
|
-
needs_attention: r.avg_quality < 70 || (r.low_quality_count / r.task_count) > 0.25,
|
|
108
|
-
}));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export function getTaskTypeBreakdown(): TaskTypeBreakdown[] {
|
|
112
|
-
const db = getDb();
|
|
113
|
-
const rows = db.prepare(`
|
|
114
|
-
SELECT task_type,
|
|
115
|
-
COUNT(*) as count,
|
|
116
|
-
AVG(complexity) as avg_complexity,
|
|
117
|
-
AVG(output_quality) as avg_quality,
|
|
118
|
-
model_tier as dominant_tier
|
|
119
|
-
FROM learning_data
|
|
120
|
-
GROUP BY task_type, model_tier
|
|
121
|
-
HAVING COUNT(*) = (
|
|
122
|
-
SELECT MAX(c) FROM (
|
|
123
|
-
SELECT COUNT(*) as c FROM learning_data l2
|
|
124
|
-
WHERE l2.task_type = learning_data.task_type
|
|
125
|
-
GROUP BY l2.model_tier
|
|
126
|
-
)
|
|
127
|
-
)
|
|
128
|
-
ORDER BY count DESC
|
|
129
|
-
LIMIT 50
|
|
130
|
-
`).all() as Array<{
|
|
131
|
-
task_type: string; count: number; avg_complexity: number;
|
|
132
|
-
avg_quality: number; dominant_tier: number;
|
|
133
|
-
}>;
|
|
134
|
-
|
|
135
|
-
return rows.map(r => {
|
|
136
|
-
// Under-tiered: task scored high quality but at a tier below what complexity suggests
|
|
137
|
-
const expectedTier = r.avg_complexity <= 30 ? 1 : r.avg_complexity <= 70 ? 2 : 3;
|
|
138
|
-
const under_tiered = r.dominant_tier < expectedTier && r.avg_quality < 70;
|
|
139
|
-
return {
|
|
140
|
-
task_type: r.task_type,
|
|
141
|
-
count: r.count,
|
|
142
|
-
avg_complexity: Math.round(r.avg_complexity),
|
|
143
|
-
avg_quality: Math.round(r.avg_quality * 10) / 10,
|
|
144
|
-
dominant_tier: r.dominant_tier,
|
|
145
|
-
under_tiered,
|
|
146
|
-
};
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export function getCouncilInsights(): CouncilInsight[] {
|
|
151
|
-
const db = getDb();
|
|
152
|
-
|
|
153
|
-
// Correlate council approvals (GREEN/YELLOW) with debugging sessions that came after
|
|
154
|
-
const rows = db.prepare(`
|
|
155
|
-
SELECT co.verdict, co.task, co.debated_at,
|
|
156
|
-
d.decision as related_decision, d.made_at as decision_at
|
|
157
|
-
FROM council_outcomes co
|
|
158
|
-
LEFT JOIN decisions d ON d.session_id = co.session_id
|
|
159
|
-
AND d.decision LIKE '%debug%' OR d.decision LIKE '%fix%' OR d.decision LIKE '%bug%'
|
|
160
|
-
WHERE co.verdict IN ('GREEN', 'YELLOW')
|
|
161
|
-
ORDER BY co.debated_at DESC
|
|
162
|
-
LIMIT 30
|
|
163
|
-
`).all() as Array<{
|
|
164
|
-
verdict: string; task: string; debated_at: string;
|
|
165
|
-
related_decision: string | null; decision_at: string | null;
|
|
166
|
-
}>;
|
|
167
|
-
|
|
168
|
-
return rows.map(r => {
|
|
169
|
-
let days_to_debug: number | null = null;
|
|
170
|
-
if (r.decision_at) {
|
|
171
|
-
const debated = new Date(r.debated_at).getTime();
|
|
172
|
-
const decided = new Date(r.decision_at).getTime();
|
|
173
|
-
days_to_debug = Math.round((decided - debated) / (1000 * 60 * 60 * 24));
|
|
174
|
-
}
|
|
175
|
-
return {
|
|
176
|
-
verdict: r.verdict,
|
|
177
|
-
task_snippet: r.task.substring(0, 80),
|
|
178
|
-
debated_at: r.debated_at,
|
|
179
|
-
days_to_debug,
|
|
180
|
-
related_decision: r.related_decision,
|
|
181
|
-
};
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// ─── Threshold Computation ────────────────────────────────────────────────────
|
|
186
|
-
|
|
187
|
-
function computeSuggestedThresholds(
|
|
188
|
-
tier_breakdown: Record<number, { count: number; avg_quality: number }>
|
|
189
|
-
): { tier1_max: number; tier2_max: number } {
|
|
190
|
-
const t1 = tier_breakdown[1];
|
|
191
|
-
const t2 = tier_breakdown[2];
|
|
192
|
-
const t3 = tier_breakdown[3];
|
|
193
|
-
|
|
194
|
-
let tier1_max = 30;
|
|
195
|
-
let tier2_max = 70;
|
|
196
|
-
|
|
197
|
-
// Tier 2 consistently great → can handle more tasks, raise Tier 1 ceiling slightly
|
|
198
|
-
if (t2 && t2.count >= 20 && t2.avg_quality >= 88) tier1_max = 35;
|
|
199
|
-
// Tier 2 quality poor → tighten Tier 1 ceiling (fewer tasks escape to T2)
|
|
200
|
-
if (t2 && t2.count >= 10 && t2.avg_quality < 65) tier1_max = 25;
|
|
201
|
-
|
|
202
|
-
// Tier 3 consistently great → can push harder tasks to T3, lower T2 ceiling
|
|
203
|
-
if (t3 && t3.count >= 10 && t3.avg_quality >= 88) tier2_max = 65;
|
|
204
|
-
// Tier 3 quality poor → be more conservative, raise T2 ceiling (keep more in T2)
|
|
205
|
-
if (t3 && t3.count >= 10 && t3.avg_quality < 65) tier2_max = 75;
|
|
206
|
-
|
|
207
|
-
// Tier 1 quality poor → lower its ceiling (fewer tasks stay at T1)
|
|
208
|
-
if (t1 && t1.count >= 10 && t1.avg_quality < 65) tier1_max = Math.max(20, tier1_max - 5);
|
|
209
|
-
|
|
210
|
-
return { tier1_max, tier2_max };
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// ─── Apply ────────────────────────────────────────────────────────────────────
|
|
214
|
-
|
|
215
|
-
export function applyLearnedThresholds(): { applied: boolean; thresholds: { tier1_max: number; tier2_max: number }; data_points: number; reason: string } {
|
|
216
|
-
const db = getDb();
|
|
217
|
-
const total = (db.prepare('SELECT COUNT(*) as c FROM learning_data').get() as { c: number }).c;
|
|
218
|
-
|
|
219
|
-
// Need at least 20 data points before adjusting
|
|
220
|
-
if (total < 20) {
|
|
221
|
-
return {
|
|
222
|
-
applied: false,
|
|
223
|
-
thresholds: { tier1_max: 30, tier2_max: 70 },
|
|
224
|
-
data_points: total,
|
|
225
|
-
reason: `Not enough data yet. Need 20 task outcomes, have ${total}. Record outcomes via veto_record_outcome.`,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const stats = getLearningStats();
|
|
230
|
-
const { tier1_max, tier2_max } = stats.suggested_thresholds;
|
|
231
|
-
|
|
232
|
-
// Persist to patterns table so the router picks them up on next call
|
|
233
|
-
const now = new Date().toISOString();
|
|
234
|
-
for (const [key, val] of [['router.tier1_max', String(tier1_max)], ['router.tier2_max', String(tier2_max)]]) {
|
|
235
|
-
const existing = db.prepare('SELECT id FROM patterns WHERE pattern_key = ?').get(key) as { id: string } | undefined;
|
|
236
|
-
if (existing) {
|
|
237
|
-
db.prepare('UPDATE patterns SET pattern_val = ?, updated_at = ? WHERE pattern_key = ?').run(val, now, key);
|
|
238
|
-
} else {
|
|
239
|
-
db.prepare('INSERT INTO patterns (id, pattern_key, pattern_val, confidence, seen_count, updated_at) VALUES (?, ?, ?, ?, ?, ?)')
|
|
240
|
-
.run(randomUUID(), key, val, 1.0, total, now);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return {
|
|
245
|
-
applied: true,
|
|
246
|
-
thresholds: { tier1_max, tier2_max },
|
|
247
|
-
data_points: total,
|
|
248
|
-
reason: `Thresholds updated from ${total} task outcomes. Default was 30/70; learned is ${tier1_max}/${tier2_max}.`,
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
export function getLearnedThresholds(): LearnedThresholds {
|
|
253
|
-
const db = getDb();
|
|
254
|
-
const t1row = db.prepare('SELECT pattern_val, seen_count FROM patterns WHERE pattern_key = ?').get('router.tier1_max') as { pattern_val: string; seen_count: number } | undefined;
|
|
255
|
-
const t2row = db.prepare('SELECT pattern_val, seen_count FROM patterns WHERE pattern_key = ?').get('router.tier2_max') as { pattern_val: string; seen_count: number } | undefined;
|
|
256
|
-
|
|
257
|
-
if (!t1row || !t2row) {
|
|
258
|
-
return { tier1_max: 30, tier2_max: 70, source: 'default', data_points: 0 };
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
return {
|
|
262
|
-
tier1_max: parseInt(t1row.pattern_val, 10),
|
|
263
|
-
tier2_max: parseInt(t2row.pattern_val, 10),
|
|
264
|
-
source: 'learned',
|
|
265
|
-
data_points: t1row.seen_count,
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
export function getSuggestedThresholds(): { tier1_max: number; tier2_max: number } {
|
|
270
|
-
return getLearningStats().suggested_thresholds;
|
|
271
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
// Assigns Tier 1/2/3 and specific model recommendations
|
|
2
|
-
// Some agents are permanently locked to a tier regardless of complexity score
|
|
3
|
-
|
|
4
|
-
export type AgentType =
|
|
5
|
-
// Tier 3 locked — stakes too high
|
|
6
|
-
| 'lead-developer' | 'system-architect' | 'security-scanner'
|
|
7
|
-
| 'devil-advocate' | 'decision-engine' | 'risk-assessor'
|
|
8
|
-
// Tier 2 locked — balanced complexity
|
|
9
|
-
| 'coder' | 'tester' | 'reviewer' | 'database' | 'documentation'
|
|
10
|
-
// Tier 1 locked — simple/structured operations
|
|
11
|
-
| 'file-manager' | 'git-agent' | 'search-agent' | 'secrets' | 'reporter'
|
|
12
|
-
// Dynamic — complexity score decides
|
|
13
|
-
| 'dynamic';
|
|
14
|
-
|
|
15
|
-
export type Tier = 1 | 2 | 3;
|
|
16
|
-
|
|
17
|
-
export type ModelRecommendation = {
|
|
18
|
-
tier: Tier;
|
|
19
|
-
models: { claude: string; gemini: string; codex: string };
|
|
20
|
-
reason: string;
|
|
21
|
-
agent_locked: boolean;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
const TIER3_LOCKED = new Set<AgentType>([
|
|
25
|
-
'lead-developer', 'system-architect', 'security-scanner',
|
|
26
|
-
'devil-advocate', 'decision-engine', 'risk-assessor',
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
const TIER2_LOCKED = new Set<AgentType>([
|
|
30
|
-
'coder', 'tester', 'reviewer', 'database', 'documentation',
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
const TIER1_LOCKED = new Set<AgentType>([
|
|
34
|
-
'file-manager', 'git-agent', 'search-agent', 'secrets', 'reporter',
|
|
35
|
-
]);
|
|
36
|
-
|
|
37
|
-
const TIER_MODELS: Record<Tier, { claude: string; gemini: string; codex: string }> = {
|
|
38
|
-
1: { claude: 'claude-haiku-4-5', gemini: 'gemini-2.0-flash', codex: 'gpt-4o-mini' },
|
|
39
|
-
2: { claude: 'claude-sonnet-4-6', gemini: 'gemini-2.0-pro', codex: 'gpt-4o' },
|
|
40
|
-
3: { claude: 'claude-sonnet-4-6', gemini: 'gemini-2.0-advanced', codex: 'gpt-4o' },
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export function selectModel(
|
|
44
|
-
complexityScore: number,
|
|
45
|
-
agentType: AgentType = 'dynamic'
|
|
46
|
-
): ModelRecommendation {
|
|
47
|
-
if (TIER3_LOCKED.has(agentType)) {
|
|
48
|
-
return {
|
|
49
|
-
tier: 3,
|
|
50
|
-
models: TIER_MODELS[3],
|
|
51
|
-
reason: `${agentType} is always Tier 3 — stakes too high for cheaper models`,
|
|
52
|
-
agent_locked: true,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
if (TIER2_LOCKED.has(agentType)) {
|
|
57
|
-
return {
|
|
58
|
-
tier: 2,
|
|
59
|
-
models: TIER_MODELS[2],
|
|
60
|
-
reason: `${agentType} is always Tier 2 — balanced complexity`,
|
|
61
|
-
agent_locked: true,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (TIER1_LOCKED.has(agentType)) {
|
|
66
|
-
return {
|
|
67
|
-
tier: 1,
|
|
68
|
-
models: TIER_MODELS[1],
|
|
69
|
-
reason: `${agentType} is always Tier 1 — simple structured operations`,
|
|
70
|
-
agent_locked: true,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const tier: Tier = complexityScore <= 30 ? 1 : complexityScore <= 70 ? 2 : 3;
|
|
75
|
-
const reason =
|
|
76
|
-
tier === 1
|
|
77
|
-
? `Score ${complexityScore}/100 — simple task, use fastest/cheapest model`
|
|
78
|
-
: tier === 2
|
|
79
|
-
? `Score ${complexityScore}/100 — mid-range task, use balanced model`
|
|
80
|
-
: `Score ${complexityScore}/100 — complex task, use best available model`;
|
|
81
|
-
|
|
82
|
-
return { tier, models: TIER_MODELS[tier], reason, agent_locked: false };
|
|
83
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
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
|
-
|
|
5
|
-
import { randomUUID } from 'node:crypto';
|
|
6
|
-
import { getDb } from '../memory/local.js';
|
|
7
|
-
|
|
8
|
-
export type Platform = 'claude' | 'gemini' | 'codex';
|
|
9
|
-
|
|
10
|
-
export type RateLimitEntry = {
|
|
11
|
-
platform: Platform;
|
|
12
|
-
requests_today: number;
|
|
13
|
-
daily_limit: number;
|
|
14
|
-
used_percent: number;
|
|
15
|
-
resets_at: string;
|
|
16
|
-
status: 'normal' | 'warning' | 'critical';
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type RateStatus = {
|
|
20
|
-
claude: RateLimitEntry;
|
|
21
|
-
gemini: RateLimitEntry;
|
|
22
|
-
codex: RateLimitEntry;
|
|
23
|
-
updated_at: string;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const DEFAULT_LIMITS: Record<Platform, number> = {
|
|
27
|
-
claude: 100,
|
|
28
|
-
gemini: 200,
|
|
29
|
-
codex: 150,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
function getTodayKey(): string {
|
|
33
|
-
return new Date().toISOString().slice(0, 10); // YYYY-MM-DD
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function getNextResetISO(): string {
|
|
37
|
-
const reset = new Date();
|
|
38
|
-
reset.setUTCHours(0, 0, 0, 0);
|
|
39
|
-
reset.setUTCDate(reset.getUTCDate() + 1);
|
|
40
|
-
return reset.toISOString();
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function trackRequest(platform: Platform, count = 1): void {
|
|
44
|
-
const db = getDb();
|
|
45
|
-
const today = getTodayKey();
|
|
46
|
-
|
|
47
|
-
const existing = db.prepare(
|
|
48
|
-
'SELECT id, request_count FROM rate_usage WHERE platform = ? AND date_key = ?'
|
|
49
|
-
).get(platform, today) as { id: string; request_count: number } | undefined;
|
|
50
|
-
|
|
51
|
-
if (existing) {
|
|
52
|
-
db.prepare(
|
|
53
|
-
'UPDATE rate_usage SET request_count = ?, updated_at = ? WHERE id = ?'
|
|
54
|
-
).run(existing.request_count + count, new Date().toISOString(), existing.id);
|
|
55
|
-
} else {
|
|
56
|
-
db.prepare(
|
|
57
|
-
'INSERT INTO rate_usage (id, platform, date_key, request_count) VALUES (?, ?, ?, ?)'
|
|
58
|
-
).run(randomUUID(), platform, today, count);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function getRequestCount(platform: Platform): number {
|
|
63
|
-
const db = getDb();
|
|
64
|
-
const row = db.prepare(
|
|
65
|
-
'SELECT request_count FROM rate_usage WHERE platform = ? AND date_key = ?'
|
|
66
|
-
).get(platform, getTodayKey()) as { request_count: number } | undefined;
|
|
67
|
-
return row?.request_count ?? 0;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function buildEntry(platform: Platform): RateLimitEntry {
|
|
71
|
-
const requests_today = getRequestCount(platform);
|
|
72
|
-
const daily_limit = DEFAULT_LIMITS[platform];
|
|
73
|
-
const used_percent = Math.min(100, Math.round((requests_today / daily_limit) * 100));
|
|
74
|
-
let status: 'normal' | 'warning' | 'critical';
|
|
75
|
-
if (used_percent >= 90) status = 'critical';
|
|
76
|
-
else if (used_percent >= 70) status = 'warning';
|
|
77
|
-
else status = 'normal';
|
|
78
|
-
return { platform, requests_today, daily_limit, used_percent, resets_at: getNextResetISO(), status };
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function getRateStatus(): RateStatus {
|
|
82
|
-
return {
|
|
83
|
-
claude: buildEntry('claude'),
|
|
84
|
-
gemini: buildEntry('gemini'),
|
|
85
|
-
codex: buildEntry('codex'),
|
|
86
|
-
updated_at: new Date().toISOString(),
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Returns a fallback platform when the preferred one is at warning/critical level
|
|
91
|
-
export function getRoutingAdvice(preferred: Platform): Platform {
|
|
92
|
-
const entry = buildEntry(preferred);
|
|
93
|
-
if (entry.status === 'critical') {
|
|
94
|
-
if (preferred === 'claude') return 'gemini';
|
|
95
|
-
if (preferred === 'gemini') return 'codex';
|
|
96
|
-
return 'claude';
|
|
97
|
-
}
|
|
98
|
-
if (entry.status === 'warning' && preferred === 'claude') {
|
|
99
|
-
// Tier 1+2 tasks shift to Gemini; caller decides based on tier
|
|
100
|
-
return 'gemini';
|
|
101
|
-
}
|
|
102
|
-
return preferred;
|
|
103
|
-
}
|