@jigyasudham/veto 1.2.12 → 1.2.14

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/dist/server.js CHANGED
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env node
2
- // Veto MCP Server — 45 tools, 16 phases, self-learning router
2
+ // Veto MCP Server — 45 tools, 17 phases complete, self-learning router
3
3
  // Suppress node:sqlite experimental warning — it would corrupt the MCP stdio protocol
4
4
  process.removeAllListeners('warning');
5
5
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
6
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
7
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
8
8
  import { buildContextString } from './context/reader.js';
9
- import { saveSession, restoreSession, listSessions, closeSession, getDbPath, saveCouncilOutcome, storeKnowledge, searchKnowledge, deleteKnowledge, updateProjectMap, getProjectMap, upsertPattern, getPatterns, getContextStatus, fetchAndCacheDocs, saveTaskPlan, getUsageStatus, getAuditLog, getHealthStats, CONTEXT_WINDOWS, } from './memory/local.js';
9
+ import { saveSession, restoreSession, listSessions, closeSession, getDbPath, saveCouncilOutcome, storeKnowledge, searchKnowledge, deleteKnowledge, updateProjectMap, getProjectMap, upsertPattern, getPatterns, getContextStatus, fetchAndCacheDocs, saveTaskPlan, getUsageStatus, getAuditLog, getHealthStats, CONTEXT_WINDOWS, logUsage, getUsageLogs, } from './memory/local.js';
10
10
  import { exportMemory, importMemory } from './memory/sync.js';
11
11
  import { runDebate } from './council/index.js';
12
- import { routeTask, getRateStatus, recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights, getRecommendedAgent } from './router/index.js';
12
+ import { routeTask, getRateStatus, trackTokens, recordOutcome, getLearningStats, getLearnedThresholds, applyLearnedThresholds, getAgentPerformanceStats, getTaskTypeBreakdown, getCouncilInsights, getRecommendedAgent } from './router/index.js';
13
+ import { getConfig, setConfig } from './memory/config.js';
13
14
  import { executeParallel, executeOne } from './agents/executor.js';
14
15
  import { handoff, continueSession, getPlatformSetup } from './adapters/index.js';
15
16
  import { startWatch, pollWatch, stopWatch } from './watcher/index.js';
@@ -63,9 +64,62 @@ const server = new Server({ name: 'veto', version: VERSION }, {
63
64
  prompts: {},
64
65
  },
65
66
  });
67
+ // ─── Tool Risk Annotations (#21) ─────────────────────────────────────────────
68
+ // readOnlyHint: tool makes no writes. destructiveHint: writes are irreversible.
69
+ // openWorldHint: tool reaches outside the local DB (network, filesystem, processes).
70
+ const TOOL_ANNOTATIONS = {
71
+ // read-only — query/inspect only
72
+ veto_status: { readOnlyHint: true },
73
+ veto_autosave_status: { readOnlyHint: true },
74
+ veto_sessions_list: { readOnlyHint: true },
75
+ veto_rate_status: { readOnlyHint: true },
76
+ veto_route_task: { readOnlyHint: true },
77
+ veto_agent_plan: { readOnlyHint: true },
78
+ veto_code_review: { readOnlyHint: true },
79
+ veto_diff_review: { readOnlyHint: true },
80
+ veto_security_scan: { readOnlyHint: true },
81
+ veto_secrets_scan: { readOnlyHint: true },
82
+ veto_project_map_get: { readOnlyHint: true },
83
+ veto_patterns_list: { readOnlyHint: true },
84
+ veto_learning_stats: { readOnlyHint: true },
85
+ veto_watch_poll: { readOnlyHint: true },
86
+ veto_plugins: { readOnlyHint: true },
87
+ veto_context_status: { readOnlyHint: true },
88
+ veto_audit_log: { readOnlyHint: true },
89
+ veto_health: { readOnlyHint: true },
90
+ veto_discover: { readOnlyHint: true },
91
+ veto_summarize: { readOnlyHint: true },
92
+ veto_explain: { readOnlyHint: true },
93
+ // read-only + open world (external network)
94
+ veto_docs_fetch: { readOnlyHint: true, openWorldHint: true },
95
+ veto_pr_review: { readOnlyHint: true, openWorldHint: true },
96
+ // reversible writes (local DB — can be deleted/reset)
97
+ veto_council_debate: { readOnlyHint: false, destructiveHint: false },
98
+ veto_execute_parallel: { readOnlyHint: false, destructiveHint: false },
99
+ veto_session_save: { readOnlyHint: false, destructiveHint: false },
100
+ veto_session_restore: { readOnlyHint: false, destructiveHint: false },
101
+ veto_memory_store: { readOnlyHint: false, destructiveHint: false },
102
+ veto_project_map_update: { readOnlyHint: false, destructiveHint: false },
103
+ veto_pattern_store: { readOnlyHint: false, destructiveHint: false },
104
+ veto_memory_export: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
105
+ veto_record_outcome: { readOnlyHint: false, destructiveHint: false },
106
+ veto_learning_apply: { readOnlyHint: false, destructiveHint: false },
107
+ veto_handoff: { readOnlyHint: false, destructiveHint: false },
108
+ veto_continue: { readOnlyHint: false, destructiveHint: false },
109
+ veto_task_parse: { readOnlyHint: false, destructiveHint: false },
110
+ veto_watch: { readOnlyHint: false, destructiveHint: false, openWorldHint: true },
111
+ veto_watch_stop: { readOnlyHint: false, destructiveHint: false },
112
+ veto_workflow: { readOnlyHint: false, destructiveHint: false },
113
+ veto_ci_gate: { readOnlyHint: false, destructiveHint: false },
114
+ veto_usage_status: { readOnlyHint: false, destructiveHint: false },
115
+ // destructive — permanent deletes or config overwrites
116
+ veto_memory_delete: { readOnlyHint: false, destructiveHint: true },
117
+ veto_memory_import: { readOnlyHint: false, destructiveHint: true },
118
+ veto_platform_setup: { readOnlyHint: false, destructiveHint: true, openWorldHint: true },
119
+ };
66
120
  // ─── Tool Definitions ─────────────────────────────────────────────────────────
67
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
68
- tools: [
121
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
122
+ const tools = [
69
123
  {
70
124
  name: 'veto_status',
71
125
  description: 'Returns Veto server status, version, and database info. Pass token_count to trigger auto-save if context usage crosses 70%.',
@@ -240,6 +294,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
240
294
  type: 'string',
241
295
  description: 'Optional: session ID to associate this council outcome with an active session.',
242
296
  },
297
+ max_tokens: {
298
+ type: 'number',
299
+ description: 'Optional: token budget for this operation. Veto estimates output tokens and warns in the response if the estimate exceeds this limit. Logged to usage_log for tracking.',
300
+ },
243
301
  },
244
302
  required: ['task'],
245
303
  },
@@ -333,6 +391,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
333
391
  },
334
392
  },
335
393
  project_dir: { type: 'string', description: 'Optional: project directory applied to all tasks (per-task project_dir overrides this). Auto-injects codebase context.' },
394
+ max_tokens: {
395
+ type: 'number',
396
+ description: 'Optional: token budget for this parallel execution. Veto estimates combined output tokens and warns if the estimate exceeds this limit. Logged to usage_log.',
397
+ },
336
398
  },
337
399
  required: ['tasks'],
338
400
  },
@@ -763,8 +825,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
763
825
  required: [],
764
826
  },
765
827
  },
766
- ],
767
- }));
828
+ ];
829
+ return { tools: tools.map(t => ({ ...t, annotations: TOOL_ANNOTATIONS[t.name] ?? {} })) };
830
+ });
768
831
  // ─── Shared Scan Utility ──────────────────────────────────────────────────────
769
832
  async function runTripleScan(diff, context) {
770
833
  const [reviewResult, secResult, secretsResult] = await Promise.all([
@@ -787,6 +850,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
787
850
  case 'veto_status': {
788
851
  const statusTokenCount = typeof args?.token_count === 'number' ? args.token_count : null;
789
852
  const statusPlatform = args?.platform ? String(args.platform) : 'claude';
853
+ if (statusTokenCount !== null && statusTokenCount > 0) {
854
+ trackTokens(statusPlatform, statusTokenCount);
855
+ }
790
856
  const autoSaveResult = statusTokenCount !== null ? maybeAutoSave(statusTokenCount, statusPlatform) : null;
791
857
  return {
792
858
  content: [
@@ -980,8 +1046,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
980
1046
  project_dir: args?.project_dir ? String(args.project_dir) : undefined,
981
1047
  });
982
1048
  const debateDuration = Date.now() - debateStart;
1049
+ const sessionId = args?.session_id ? String(args.session_id) : undefined;
983
1050
  const outcomeId = saveCouncilOutcome({
984
- session_id: args?.session_id ? String(args.session_id) : undefined,
1051
+ session_id: sessionId,
985
1052
  task,
986
1053
  verdict: result.final_verdict,
987
1054
  lead_dev: JSON.stringify(result.votes.lead_dev),
@@ -994,29 +1061,37 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
994
1061
  recommended: result.recommended,
995
1062
  duration_ms: debateDuration,
996
1063
  });
1064
+ const responsePayload = {
1065
+ outcome_id: outcomeId,
1066
+ final_verdict: result.final_verdict,
1067
+ block_reasons: result.block_reasons,
1068
+ warnings: result.warnings,
1069
+ recommended: result.recommended,
1070
+ debated_at: result.debated_at,
1071
+ votes: {
1072
+ lead_dev: result.votes.lead_dev.verdict,
1073
+ pm: result.votes.pm.verdict,
1074
+ architect: result.votes.architect.verdict,
1075
+ ux: result.votes.ux.verdict,
1076
+ devil: result.votes.devil.verdict,
1077
+ legal: result.votes.legal.verdict,
1078
+ security: result.votes.security.verdict,
1079
+ },
1080
+ };
1081
+ const fullText = result.formatted_output + '\n\n' + JSON.stringify(responsePayload, null, 2);
1082
+ if (typeof args?.max_tokens === 'number') {
1083
+ const { exceeded, estimated_tokens } = logUsage({
1084
+ tool_name: 'veto_council_debate',
1085
+ session_id: sessionId,
1086
+ max_tokens: args.max_tokens,
1087
+ output: fullText,
1088
+ });
1089
+ if (exceeded) {
1090
+ responsePayload.budget_warning = `Estimated output tokens (${estimated_tokens}) exceeded max_tokens budget (${args.max_tokens}).`;
1091
+ }
1092
+ }
997
1093
  return {
998
- content: [
999
- {
1000
- type: 'text',
1001
- text: result.formatted_output + '\n\n' + JSON.stringify({
1002
- outcome_id: outcomeId,
1003
- final_verdict: result.final_verdict,
1004
- block_reasons: result.block_reasons,
1005
- warnings: result.warnings,
1006
- recommended: result.recommended,
1007
- debated_at: result.debated_at,
1008
- votes: {
1009
- lead_dev: result.votes.lead_dev.verdict,
1010
- pm: result.votes.pm.verdict,
1011
- architect: result.votes.architect.verdict,
1012
- ux: result.votes.ux.verdict,
1013
- devil: result.votes.devil.verdict,
1014
- legal: result.votes.legal.verdict,
1015
- security: result.votes.security.verdict,
1016
- },
1017
- }, null, 2),
1018
- },
1019
- ],
1094
+ content: [{ type: 'text', text: fullText }],
1020
1095
  };
1021
1096
  }
1022
1097
  case 'veto_agent_plan': {
@@ -1158,21 +1233,30 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1158
1233
  project_dir: t.project_dir ? String(t.project_dir) : parallelProjectDir,
1159
1234
  }));
1160
1235
  const results = await executeParallel(tasks);
1236
+ const parallelPayload = {
1237
+ count: results.length,
1238
+ total_duration_ms: results.reduce((s, r) => s + r.duration_ms, 0),
1239
+ results: results.map(r => ({
1240
+ id: r.id,
1241
+ agent: r.agent,
1242
+ duration_ms: r.duration_ms,
1243
+ error: r.error,
1244
+ output: { ...(r.plan ?? r.analysis), structured: r.output },
1245
+ })),
1246
+ };
1247
+ if (typeof args?.max_tokens === 'number') {
1248
+ const outputText = JSON.stringify(parallelPayload, null, 2);
1249
+ const { exceeded, estimated_tokens } = logUsage({
1250
+ tool_name: 'veto_execute_parallel',
1251
+ max_tokens: args.max_tokens,
1252
+ output: outputText,
1253
+ });
1254
+ if (exceeded) {
1255
+ parallelPayload.budget_warning = `Estimated output tokens (${estimated_tokens}) exceeded max_tokens budget (${args.max_tokens}).`;
1256
+ }
1257
+ }
1161
1258
  return {
1162
- content: [{
1163
- type: 'text',
1164
- text: JSON.stringify({
1165
- count: results.length,
1166
- total_duration_ms: results.reduce((s, r) => s + r.duration_ms, 0),
1167
- results: results.map(r => ({
1168
- id: r.id,
1169
- agent: r.agent,
1170
- duration_ms: r.duration_ms,
1171
- error: r.error,
1172
- output: { ...(r.plan ?? r.analysis), structured: r.output },
1173
- })),
1174
- }, null, 2),
1175
- }],
1259
+ content: [{ type: 'text', text: JSON.stringify(parallelPayload, null, 2) }],
1176
1260
  };
1177
1261
  }
1178
1262
  case 'veto_memory_store': {
@@ -1600,8 +1684,48 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1600
1684
  }
1601
1685
  // ── Phase 14: Observability & Safety ──────────────────────────────────────
1602
1686
  case 'veto_usage_status': {
1687
+ if (args?.set_budget && typeof args.set_budget === 'object') {
1688
+ const b = args.set_budget;
1689
+ const current = getConfig().dailyTokenBudget;
1690
+ setConfig({
1691
+ dailyTokenBudget: {
1692
+ claude: typeof b.claude === 'number' ? b.claude : current.claude,
1693
+ gemini: typeof b.gemini === 'number' ? b.gemini : current.gemini,
1694
+ codex: typeof b.codex === 'number' ? b.codex : current.codex,
1695
+ },
1696
+ });
1697
+ }
1603
1698
  const status = getUsageStatus();
1604
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...status }, null, 2) }] };
1699
+ const { dailyTokenBudget } = getConfig();
1700
+ const rateStatus = getRateStatus();
1701
+ const recentBudgetLog = getUsageLogs({ limit: 10 });
1702
+ return {
1703
+ content: [{
1704
+ type: 'text',
1705
+ text: JSON.stringify({
1706
+ success: true,
1707
+ ...status,
1708
+ daily_token_budget: dailyTokenBudget,
1709
+ tokens_today: {
1710
+ claude: rateStatus.claude.tokens_today,
1711
+ gemini: rateStatus.gemini.tokens_today,
1712
+ codex: rateStatus.codex.tokens_today,
1713
+ },
1714
+ budget_used_pct: {
1715
+ claude: rateStatus.claude.used_percent,
1716
+ gemini: rateStatus.gemini.used_percent,
1717
+ codex: rateStatus.codex.used_percent,
1718
+ },
1719
+ operation_budget_log: recentBudgetLog.map(e => ({
1720
+ tool: e.tool_name,
1721
+ max_tokens: e.max_tokens,
1722
+ estimated_tokens: e.estimated_tokens,
1723
+ exceeded: e.exceeded === 1,
1724
+ at: e.created_at,
1725
+ })),
1726
+ }, null, 2),
1727
+ }],
1728
+ };
1605
1729
  }
1606
1730
  case 'veto_audit_log': {
1607
1731
  const events = getAuditLog({