@jigyasudham/veto 1.2.12 → 1.2.15

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, 19 phases complete, auto-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([
@@ -778,8 +841,18 @@ async function runTripleScan(diff, context) {
778
841
  const hasWarnings = (reviewResult.analysis?.high_count ?? 0) > 0
779
842
  || (secResult.analysis?.high_count ?? 0) > 0;
780
843
  const verdict = hasBlocking ? 'fail' : hasWarnings ? 'warn' : 'pass';
844
+ // auto-record per agent so learning accumulates from every scan (diff_review, ci_gate, pr_review)
845
+ autoRecord('scan', 'reviewer', reviewResult.analysis?.score ?? Math.round(reviewResult.output.confidence * 100));
846
+ autoRecord('scan', 'security-scanner', secResult.analysis?.score ?? Math.round(secResult.output.confidence * 100));
847
+ autoRecord('scan', 'secrets', (secretsResult.analysis?.findings?.length ?? 0) === 0 ? 100 : secretsResult.analysis?.score ?? Math.round(secretsResult.output.confidence * 100));
781
848
  return { reviewResult, secResult, secretsResult, verdict };
782
849
  }
850
+ // Auto-learning helper — records a learning_data row from any agent result.
851
+ // Keeps call sites to one line rather than repeating the tier/quality logic.
852
+ function autoRecord(taskType, agent, quality, complexity = 50) {
853
+ const tier = quality >= 80 ? 1 : quality >= 40 ? 2 : 3;
854
+ recordOutcome(taskType.slice(0, 50), complexity, tier, agent, quality);
855
+ }
783
856
  // ─── Tool Handlers ────────────────────────────────────────────────────────────
784
857
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
785
858
  const { name, arguments: args } = request.params;
@@ -787,6 +860,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
787
860
  case 'veto_status': {
788
861
  const statusTokenCount = typeof args?.token_count === 'number' ? args.token_count : null;
789
862
  const statusPlatform = args?.platform ? String(args.platform) : 'claude';
863
+ if (statusTokenCount !== null && statusTokenCount > 0) {
864
+ trackTokens(statusPlatform, statusTokenCount);
865
+ }
790
866
  const autoSaveResult = statusTokenCount !== null ? maybeAutoSave(statusTokenCount, statusPlatform) : null;
791
867
  return {
792
868
  content: [
@@ -948,6 +1024,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
948
1024
  preferredPlatform: args?.preferred_platform ? String(args.preferred_platform) : 'claude',
949
1025
  });
950
1026
  const recommended_agent = getRecommendedAgent(routeTaskStr, fileExt);
1027
+ // #41: auto-record every routing so tier distribution stats are always populated
1028
+ recordOutcome(routeTaskStr.slice(0, 50), result.complexity.score, result.model.tier, 'router', 70);
951
1029
  return {
952
1030
  content: [{
953
1031
  type: 'text',
@@ -980,8 +1058,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
980
1058
  project_dir: args?.project_dir ? String(args.project_dir) : undefined,
981
1059
  });
982
1060
  const debateDuration = Date.now() - debateStart;
1061
+ const sessionId = args?.session_id ? String(args.session_id) : undefined;
983
1062
  const outcomeId = saveCouncilOutcome({
984
- session_id: args?.session_id ? String(args.session_id) : undefined,
1063
+ session_id: sessionId,
985
1064
  task,
986
1065
  verdict: result.final_verdict,
987
1066
  lead_dev: JSON.stringify(result.votes.lead_dev),
@@ -994,29 +1073,43 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
994
1073
  recommended: result.recommended,
995
1074
  duration_ms: debateDuration,
996
1075
  });
1076
+ // #38: auto-record learning outcome from verdict — no manual veto_record_outcome needed
1077
+ {
1078
+ const qMap = { GREEN: 90, YELLOW: 60, RED: 20, DEADLOCK: 50 };
1079
+ const tMap = { GREEN: 1, YELLOW: 2, RED: 3, DEADLOCK: 2 };
1080
+ recordOutcome(task.slice(0, 50), 50, tMap[result.final_verdict] ?? 2, 'council', qMap[result.final_verdict] ?? 50);
1081
+ }
1082
+ const responsePayload = {
1083
+ outcome_id: outcomeId,
1084
+ final_verdict: result.final_verdict,
1085
+ block_reasons: result.block_reasons,
1086
+ warnings: result.warnings,
1087
+ recommended: result.recommended,
1088
+ debated_at: result.debated_at,
1089
+ votes: {
1090
+ lead_dev: result.votes.lead_dev.verdict,
1091
+ pm: result.votes.pm.verdict,
1092
+ architect: result.votes.architect.verdict,
1093
+ ux: result.votes.ux.verdict,
1094
+ devil: result.votes.devil.verdict,
1095
+ legal: result.votes.legal.verdict,
1096
+ security: result.votes.security.verdict,
1097
+ },
1098
+ };
1099
+ const fullText = result.formatted_output + '\n\n' + JSON.stringify(responsePayload, null, 2);
1100
+ if (typeof args?.max_tokens === 'number') {
1101
+ const { exceeded, estimated_tokens } = logUsage({
1102
+ tool_name: 'veto_council_debate',
1103
+ session_id: sessionId,
1104
+ max_tokens: args.max_tokens,
1105
+ output: fullText,
1106
+ });
1107
+ if (exceeded) {
1108
+ responsePayload.budget_warning = `Estimated output tokens (${estimated_tokens}) exceeded max_tokens budget (${args.max_tokens}).`;
1109
+ }
1110
+ }
997
1111
  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
- ],
1112
+ content: [{ type: 'text', text: fullText }],
1020
1113
  };
1021
1114
  }
1022
1115
  case 'veto_agent_plan': {
@@ -1035,6 +1128,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1035
1128
  if (result.error) {
1036
1129
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error }) }], isError: true };
1037
1130
  }
1131
+ autoRecord(task, agentType, Math.round(result.output.confidence * 100));
1038
1132
  return { content: [{ type: 'text', text: JSON.stringify({ ...(result.plan ?? result.analysis), output: result.output }, null, 2) }] };
1039
1133
  }
1040
1134
  case 'veto_code_review': {
@@ -1046,6 +1140,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1046
1140
  if (result.error) {
1047
1141
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error }) }], isError: true };
1048
1142
  }
1143
+ autoRecord('code review', 'reviewer', result.analysis?.score ?? Math.round(result.output.confidence * 100));
1049
1144
  return { content: [{ type: 'text', text: JSON.stringify(result.analysis, null, 2) }] };
1050
1145
  }
1051
1146
  case 'veto_diff_review': {
@@ -1130,6 +1225,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1130
1225
  if (result.error) {
1131
1226
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error }) }], isError: true };
1132
1227
  }
1228
+ autoRecord('security scan', 'security-scanner', result.analysis?.score ?? Math.round(result.output.confidence * 100));
1133
1229
  return { content: [{ type: 'text', text: JSON.stringify(result.analysis, null, 2) }] };
1134
1230
  }
1135
1231
  case 'veto_secrets_scan': {
@@ -1141,6 +1237,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1141
1237
  if (result.error) {
1142
1238
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, error: result.error }) }], isError: true };
1143
1239
  }
1240
+ autoRecord('secrets scan', 'secrets', (result.analysis?.findings?.length ?? 0) === 0 ? 100 : result.analysis?.score ?? Math.round(result.output.confidence * 100));
1144
1241
  return { content: [{ type: 'text', text: JSON.stringify(result.analysis, null, 2) }] };
1145
1242
  }
1146
1243
  case 'veto_execute_parallel': {
@@ -1158,21 +1255,39 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1158
1255
  project_dir: t.project_dir ? String(t.project_dir) : parallelProjectDir,
1159
1256
  }));
1160
1257
  const results = await executeParallel(tasks);
1258
+ // #40: auto-record learning outcome per completed parallel task
1259
+ for (let i = 0; i < results.length; i++) {
1260
+ const r = results[i];
1261
+ if (r.error)
1262
+ continue;
1263
+ const quality = Math.round(r.output.confidence * 100);
1264
+ const tier = quality >= 80 ? 1 : quality >= 40 ? 2 : 3;
1265
+ recordOutcome(tasks[i]?.task.slice(0, 50) ?? r.agent, 50, tier, r.agent, quality);
1266
+ }
1267
+ const parallelPayload = {
1268
+ count: results.length,
1269
+ total_duration_ms: results.reduce((s, r) => s + r.duration_ms, 0),
1270
+ results: results.map(r => ({
1271
+ id: r.id,
1272
+ agent: r.agent,
1273
+ duration_ms: r.duration_ms,
1274
+ error: r.error,
1275
+ output: { ...(r.plan ?? r.analysis), structured: r.output },
1276
+ })),
1277
+ };
1278
+ if (typeof args?.max_tokens === 'number') {
1279
+ const outputText = JSON.stringify(parallelPayload, null, 2);
1280
+ const { exceeded, estimated_tokens } = logUsage({
1281
+ tool_name: 'veto_execute_parallel',
1282
+ max_tokens: args.max_tokens,
1283
+ output: outputText,
1284
+ });
1285
+ if (exceeded) {
1286
+ parallelPayload.budget_warning = `Estimated output tokens (${estimated_tokens}) exceeded max_tokens budget (${args.max_tokens}).`;
1287
+ }
1288
+ }
1161
1289
  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
- }],
1290
+ content: [{ type: 'text', text: JSON.stringify(parallelPayload, null, 2) }],
1176
1291
  };
1177
1292
  }
1178
1293
  case 'veto_memory_store': {
@@ -1454,6 +1569,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1454
1569
  gate: typeof s.gate === 'number' ? s.gate : undefined,
1455
1570
  }));
1456
1571
  const result = await runPipeline(steps, args?.project_dir ? String(args.project_dir) : undefined);
1572
+ // #39: auto-record learning outcome per executed workflow step
1573
+ for (const step of result.results) {
1574
+ if (step.status === 'skipped')
1575
+ continue;
1576
+ const quality = step.error ? 0 : step.confidence;
1577
+ const tier = quality >= 80 ? 1 : quality >= 40 ? 2 : 3;
1578
+ const taskStr = steps.find(s => s.id === step.id)?.task.slice(0, 50) ?? step.id;
1579
+ recordOutcome(taskStr, 50, tier, step.agent, quality);
1580
+ }
1457
1581
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
1458
1582
  }
1459
1583
  case 'veto_explain': {
@@ -1489,6 +1613,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1489
1613
  const userContext = args?.context ? String(args.context) : undefined;
1490
1614
  const task = `Explain this ${ext} file at ${depth} depth. File: ${basename(filePath)}${userContext ? `. Focus: ${userContext}` : ''}`;
1491
1615
  const result = await executeOne({ id: 'explain-1', agent, task, code: fileContent, project_dir: undefined });
1616
+ autoRecord(`explain ${basename(filePath)}`, agent, Math.round(result.output.confidence * 100));
1492
1617
  return {
1493
1618
  content: [{ type: 'text', text: JSON.stringify({
1494
1619
  file: filePath, agent_used: agent, depth,
@@ -1594,14 +1719,55 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1594
1719
  tasks,
1595
1720
  duration_estimate: planResult.plan?.duration_estimate ?? 'unknown',
1596
1721
  };
1722
+ autoRecord(description, 'task-planner', Math.round(planResult.output.confidence * 100));
1597
1723
  const hash = createHash('sha256').update(description).digest('hex').slice(0, 16);
1598
1724
  const plan_id = saveTaskPlan(JSON.stringify(plan), hash, project_dir);
1599
1725
  return { content: [{ type: 'text', text: JSON.stringify({ success: true, plan_id, ...plan }, null, 2) }] };
1600
1726
  }
1601
1727
  // ── Phase 14: Observability & Safety ──────────────────────────────────────
1602
1728
  case 'veto_usage_status': {
1729
+ if (args?.set_budget && typeof args.set_budget === 'object') {
1730
+ const b = args.set_budget;
1731
+ const current = getConfig().dailyTokenBudget;
1732
+ setConfig({
1733
+ dailyTokenBudget: {
1734
+ claude: typeof b.claude === 'number' ? b.claude : current.claude,
1735
+ gemini: typeof b.gemini === 'number' ? b.gemini : current.gemini,
1736
+ codex: typeof b.codex === 'number' ? b.codex : current.codex,
1737
+ },
1738
+ });
1739
+ }
1603
1740
  const status = getUsageStatus();
1604
- return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...status }, null, 2) }] };
1741
+ const { dailyTokenBudget } = getConfig();
1742
+ const rateStatus = getRateStatus();
1743
+ const recentBudgetLog = getUsageLogs({ limit: 10 });
1744
+ return {
1745
+ content: [{
1746
+ type: 'text',
1747
+ text: JSON.stringify({
1748
+ success: true,
1749
+ ...status,
1750
+ daily_token_budget: dailyTokenBudget,
1751
+ tokens_today: {
1752
+ claude: rateStatus.claude.tokens_today,
1753
+ gemini: rateStatus.gemini.tokens_today,
1754
+ codex: rateStatus.codex.tokens_today,
1755
+ },
1756
+ budget_used_pct: {
1757
+ claude: rateStatus.claude.used_percent,
1758
+ gemini: rateStatus.gemini.used_percent,
1759
+ codex: rateStatus.codex.used_percent,
1760
+ },
1761
+ operation_budget_log: recentBudgetLog.map(e => ({
1762
+ tool: e.tool_name,
1763
+ max_tokens: e.max_tokens,
1764
+ estimated_tokens: e.estimated_tokens,
1765
+ exceeded: e.exceeded === 1,
1766
+ at: e.created_at,
1767
+ })),
1768
+ }, null, 2),
1769
+ }],
1770
+ };
1605
1771
  }
1606
1772
  case 'veto_audit_log': {
1607
1773
  const events = getAuditLog({
@@ -1846,6 +2012,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1846
2012
  const depthNote = sumFormat === 'detailed' ? 'Write paragraph-level prose.' : 'Return 4–6 bullet points only.';
1847
2013
  const task = `Summarize this file concisely for a developer who has never seen it.${focusNote} ${depthNote} File: ${basename(sumFilePath)}`;
1848
2014
  const r = await executeOne({ id: 'sum-file', agent, task, code: fileContent.slice(0, 8000) });
2015
+ autoRecord(`summarize ${basename(sumFilePath)}`, agent, Math.round(r.output.confidence * 100));
1849
2016
  return { content: [{ type: 'text', text: JSON.stringify({
1850
2017
  success: true, subject: 'file', path: sumFilePath, format: sumFormat,
1851
2018
  summary: r.plan ?? r.analysis ?? r.output,
@@ -1867,6 +2034,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1867
2034
  const depthNote = sumFormat === 'detailed' ? 'Write paragraph-level prose with sections.' : 'Return 5–7 bullet points that capture the essence.';
1868
2035
  const task = `You are a senior engineer briefing a colleague on this codebase.${focusNote} ${depthNote} Be concise and precise — no filler.`;
1869
2036
  const r = await executeOne({ id: 'sum-proj', agent: 'project-mapper', task, context: ctx });
2037
+ autoRecord('summarize project', 'project-mapper', Math.round(r.output.confidence * 100));
1870
2038
  return { content: [{ type: 'text', text: JSON.stringify({
1871
2039
  success: true, subject: 'project', path: sumProjectDir, format: sumFormat,
1872
2040
  tech_stack: discResult.tech_stack,