@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.
Files changed (198) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/README.md +190 -0
  3. package/dist/adapters/claude.js +57 -0
  4. package/dist/adapters/codex.js +58 -0
  5. package/dist/adapters/gemini.js +58 -0
  6. package/dist/adapters/index.js +156 -0
  7. package/dist/agents/development/api.js +116 -0
  8. package/dist/agents/development/backend.js +82 -0
  9. package/dist/agents/development/coder.js +207 -0
  10. package/dist/agents/development/database.js +81 -0
  11. package/dist/agents/development/debugger.js +234 -0
  12. package/dist/agents/development/devops.js +84 -0
  13. package/dist/agents/development/frontend.js +83 -0
  14. package/dist/agents/development/migration.js +141 -0
  15. package/dist/agents/development/performance.js +142 -0
  16. package/dist/agents/development/refactor.js +85 -0
  17. package/dist/agents/development/reviewer.js +260 -0
  18. package/dist/agents/development/tester.js +143 -0
  19. package/dist/agents/executor.js +144 -0
  20. package/dist/agents/memory/context-manager.js +167 -0
  21. package/dist/agents/memory/decision-logger.js +157 -0
  22. package/dist/agents/memory/knowledge-base.js +120 -0
  23. package/dist/agents/memory/pattern-learner.js +140 -0
  24. package/dist/agents/memory/project-mapper.js +114 -0
  25. package/dist/agents/quality/accessibility.js +89 -0
  26. package/dist/agents/quality/code-quality.js +109 -0
  27. package/dist/agents/quality/compatibility.js +55 -0
  28. package/dist/agents/quality/documentation.js +95 -0
  29. package/dist/agents/quality/error-handling.js +87 -0
  30. package/dist/agents/research/competitor-analyzer.js +44 -0
  31. package/dist/agents/research/cost-analyzer.js +51 -0
  32. package/dist/agents/research/estimator.js +57 -0
  33. package/dist/agents/research/ethics-bias.js +111 -0
  34. package/dist/agents/research/researcher.js +112 -0
  35. package/dist/agents/research/risk-assessor.js +61 -0
  36. package/dist/agents/research/tech-advisor.js +52 -0
  37. package/dist/agents/security/auth.js +269 -0
  38. package/dist/agents/security/dependency-audit.js +273 -0
  39. package/dist/agents/security/penetration.js +245 -0
  40. package/dist/agents/security/privacy.js +259 -0
  41. package/dist/agents/security/scanner.js +288 -0
  42. package/dist/agents/security/secrets.js +212 -0
  43. package/dist/agents/types.js +2 -0
  44. package/dist/agents/workflow/automation.js +56 -0
  45. package/dist/agents/workflow/file-manager.js +49 -0
  46. package/dist/agents/workflow/git-agent.js +52 -0
  47. package/dist/agents/workflow/reporter.js +48 -0
  48. package/dist/agents/workflow/search-agent.js +39 -0
  49. package/dist/agents/workflow/task-coordinator.js +40 -0
  50. package/dist/agents/workflow/task-planner.js +46 -0
  51. package/dist/cli.js +132 -0
  52. package/dist/council/decision-engine.js +136 -0
  53. package/dist/council/devil-advocate.js +106 -0
  54. package/dist/council/index.js +37 -0
  55. package/dist/council/lead-developer.js +108 -0
  56. package/dist/council/legal-compliance.js +142 -0
  57. package/dist/council/product-manager.js +92 -0
  58. package/dist/council/security.js +162 -0
  59. package/dist/council/system-architect.js +122 -0
  60. package/dist/council/types.js +2 -0
  61. package/dist/council/ux-designer.js +109 -0
  62. package/dist/memory/local.js +182 -0
  63. package/dist/memory/schema.js +116 -0
  64. package/dist/memory/sync.js +199 -0
  65. package/dist/router/complexity-scorer.js +78 -0
  66. package/dist/router/context-compressor.js +58 -0
  67. package/dist/router/index.js +29 -0
  68. package/dist/router/learning-updater.js +186 -0
  69. package/dist/router/model-selector.js +51 -0
  70. package/dist/router/rate-monitor.js +73 -0
  71. package/dist/server.js +949 -0
  72. package/dist/skills/development/skill-api-design.js +313 -0
  73. package/dist/skills/development/skill-auth.js +255 -0
  74. package/dist/skills/development/skill-ci-cd.js +2 -0
  75. package/dist/skills/development/skill-crud.js +193 -0
  76. package/dist/skills/development/skill-db-schema.js +2 -0
  77. package/dist/skills/development/skill-docker.js +2 -0
  78. package/dist/skills/development/skill-env-setup.js +2 -0
  79. package/dist/skills/development/skill-scaffold.js +299 -0
  80. package/dist/skills/intelligence/skill-complexity-score.js +66 -0
  81. package/dist/skills/intelligence/skill-cost-track.js +36 -0
  82. package/dist/skills/intelligence/skill-learning-loop.js +66 -0
  83. package/dist/skills/intelligence/skill-pattern-detect.js +35 -0
  84. package/dist/skills/intelligence/skill-rate-watch.js +58 -0
  85. package/dist/skills/memory/skill-context-compress.js +82 -0
  86. package/dist/skills/memory/skill-cross-sync.js +88 -0
  87. package/dist/skills/memory/skill-decision-log.js +103 -0
  88. package/dist/skills/memory/skill-session-restore.js +44 -0
  89. package/dist/skills/memory/skill-session-save.js +78 -0
  90. package/dist/skills/quality/skill-accessibility.js +2 -0
  91. package/dist/skills/quality/skill-code-review.js +60 -0
  92. package/dist/skills/quality/skill-docs-gen.js +2 -0
  93. package/dist/skills/quality/skill-perf-audit.js +2 -0
  94. package/dist/skills/quality/skill-security-scan.js +67 -0
  95. package/dist/skills/quality/skill-test-suite.js +274 -0
  96. package/dist/skills/workflow/skill-deploy.js +2 -0
  97. package/dist/skills/workflow/skill-git-workflow.js +2 -0
  98. package/dist/skills/workflow/skill-rollback.js +2 -0
  99. package/dist/skills/workflow/skill-task-breakdown.js +2 -0
  100. package/package.json +30 -0
  101. package/src/adapters/claude.ts +70 -0
  102. package/src/adapters/codex.ts +71 -0
  103. package/src/adapters/gemini.ts +71 -0
  104. package/src/adapters/index.ts +217 -0
  105. package/src/agents/development/api.ts +120 -0
  106. package/src/agents/development/backend.ts +85 -0
  107. package/src/agents/development/coder.ts +213 -0
  108. package/src/agents/development/database.ts +83 -0
  109. package/src/agents/development/debugger.ts +238 -0
  110. package/src/agents/development/devops.ts +86 -0
  111. package/src/agents/development/frontend.ts +85 -0
  112. package/src/agents/development/migration.ts +144 -0
  113. package/src/agents/development/performance.ts +144 -0
  114. package/src/agents/development/refactor.ts +86 -0
  115. package/src/agents/development/reviewer.ts +268 -0
  116. package/src/agents/development/tester.ts +151 -0
  117. package/src/agents/executor.ts +158 -0
  118. package/src/agents/memory/context-manager.ts +171 -0
  119. package/src/agents/memory/decision-logger.ts +160 -0
  120. package/src/agents/memory/knowledge-base.ts +124 -0
  121. package/src/agents/memory/pattern-learner.ts +143 -0
  122. package/src/agents/memory/project-mapper.ts +118 -0
  123. package/src/agents/quality/accessibility.ts +99 -0
  124. package/src/agents/quality/code-quality.ts +115 -0
  125. package/src/agents/quality/compatibility.ts +58 -0
  126. package/src/agents/quality/documentation.ts +105 -0
  127. package/src/agents/quality/error-handling.ts +96 -0
  128. package/src/agents/research/competitor-analyzer.ts +45 -0
  129. package/src/agents/research/cost-analyzer.ts +54 -0
  130. package/src/agents/research/estimator.ts +60 -0
  131. package/src/agents/research/ethics-bias.ts +113 -0
  132. package/src/agents/research/researcher.ts +114 -0
  133. package/src/agents/research/risk-assessor.ts +63 -0
  134. package/src/agents/research/tech-advisor.ts +55 -0
  135. package/src/agents/security/auth.ts +287 -0
  136. package/src/agents/security/dependency-audit.ts +337 -0
  137. package/src/agents/security/penetration.ts +262 -0
  138. package/src/agents/security/privacy.ts +285 -0
  139. package/src/agents/security/scanner.ts +322 -0
  140. package/src/agents/security/secrets.ts +249 -0
  141. package/src/agents/types.ts +66 -0
  142. package/src/agents/workflow/automation.ts +59 -0
  143. package/src/agents/workflow/file-manager.ts +52 -0
  144. package/src/agents/workflow/git-agent.ts +55 -0
  145. package/src/agents/workflow/reporter.ts +51 -0
  146. package/src/agents/workflow/search-agent.ts +40 -0
  147. package/src/agents/workflow/task-coordinator.ts +41 -0
  148. package/src/agents/workflow/task-planner.ts +47 -0
  149. package/src/cli.ts +143 -0
  150. package/src/council/decision-engine.ts +171 -0
  151. package/src/council/devil-advocate.ts +116 -0
  152. package/src/council/index.ts +44 -0
  153. package/src/council/lead-developer.ts +118 -0
  154. package/src/council/legal-compliance.ts +152 -0
  155. package/src/council/product-manager.ts +102 -0
  156. package/src/council/security.ts +172 -0
  157. package/src/council/system-architect.ts +132 -0
  158. package/src/council/types.ts +33 -0
  159. package/src/council/ux-designer.ts +121 -0
  160. package/src/memory/local.ts +305 -0
  161. package/src/memory/schema.ts +174 -0
  162. package/src/memory/sync.ts +274 -0
  163. package/src/router/complexity-scorer.ts +96 -0
  164. package/src/router/context-compressor.ts +74 -0
  165. package/src/router/index.ts +60 -0
  166. package/src/router/learning-updater.ts +271 -0
  167. package/src/router/model-selector.ts +83 -0
  168. package/src/router/rate-monitor.ts +103 -0
  169. package/src/server.ts +1038 -0
  170. package/src/skills/development/skill-api-design.ts +329 -0
  171. package/src/skills/development/skill-auth.ts +271 -0
  172. package/src/skills/development/skill-ci-cd.ts +0 -0
  173. package/src/skills/development/skill-crud.ts +209 -0
  174. package/src/skills/development/skill-db-schema.ts +0 -0
  175. package/src/skills/development/skill-docker.ts +0 -0
  176. package/src/skills/development/skill-env-setup.ts +0 -0
  177. package/src/skills/development/skill-scaffold.ts +323 -0
  178. package/src/skills/intelligence/skill-complexity-score.ts +69 -0
  179. package/src/skills/intelligence/skill-cost-track.ts +39 -0
  180. package/src/skills/intelligence/skill-learning-loop.ts +69 -0
  181. package/src/skills/intelligence/skill-pattern-detect.ts +38 -0
  182. package/src/skills/intelligence/skill-rate-watch.ts +61 -0
  183. package/src/skills/memory/skill-context-compress.ts +98 -0
  184. package/src/skills/memory/skill-cross-sync.ts +104 -0
  185. package/src/skills/memory/skill-decision-log.ts +119 -0
  186. package/src/skills/memory/skill-session-restore.ts +59 -0
  187. package/src/skills/memory/skill-session-save.ts +94 -0
  188. package/src/skills/quality/skill-accessibility.ts +0 -0
  189. package/src/skills/quality/skill-code-review.ts +84 -0
  190. package/src/skills/quality/skill-docs-gen.ts +0 -0
  191. package/src/skills/quality/skill-perf-audit.ts +0 -0
  192. package/src/skills/quality/skill-security-scan.ts +91 -0
  193. package/src/skills/quality/skill-test-suite.ts +290 -0
  194. package/src/skills/workflow/skill-deploy.ts +0 -0
  195. package/src/skills/workflow/skill-git-workflow.ts +0 -0
  196. package/src/skills/workflow/skill-rollback.ts +0 -0
  197. package/src/skills/workflow/skill-task-breakdown.ts +0 -0
  198. 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