@jigyasudham/veto 1.0.0 → 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 CHANGED
@@ -73,7 +73,7 @@ VS Code uses `"servers"` with `"type": "stdio"`:
73
73
 
74
74
  **50 Agents** — Domain experts for every task type. Each agent knows when it is the right tool and when to defer.
75
75
 
76
- **Memory** — Sessions, decisions, knowledge, and coding patterns persist across every conversation and every platform.
76
+ **Memory** — Sessions, decisions, knowledge, and coding patterns persist across every conversation and every platform. Memory is automatically scoped to the active session's project directory — two instances working on different projects stay isolated without any extra configuration.
77
77
 
78
78
  **Diff review** — `veto_diff_review` runs code review, security scan, and secrets scan in parallel across a git diff. Returns a pass/warn/fail verdict with per-file findings — ready for CI and pre-commit hooks.
79
79
 
@@ -303,10 +303,16 @@ Veto loads it on start. Use it in `veto_agent_plan { agent: "my-agent" }` or `ve
303
303
  **Rate limit mid-task:**
304
304
  ```
305
305
  Claude at 90% → veto_handoff { summary, context }
306
- Open Gemini → veto_continue
306
+ Open Gemini → veto_continue { resuming_as: "gemini" }
307
307
  Full context restored. Continue exactly where you stopped.
308
308
  ```
309
309
 
310
+ Every session tracks two fields:
311
+ - `created_by` — which AI originally saved the session
312
+ - `active_client` — which AI last resumed it (updated on every `veto_continue` or `veto_session_restore`)
313
+
314
+ **Multiple AIs on different projects simultaneously:** Each MCP server process is independent. Sessions are always separate. Memory is automatically scoped to each process's active project — no cross-contamination.
315
+
310
316
  **Switch machines:**
311
317
  ```
312
318
  Machine A → veto_memory_export → veto-export.json
@@ -37,10 +37,10 @@ export function handoff(options) {
37
37
  };
38
38
  }
39
39
  // ─── Continue ─────────────────────────────────────────────────────────────────
40
- export function continueSession(sessionId) {
40
+ export function continueSession(sessionId, active_client) {
41
41
  const now = new Date().toISOString();
42
42
  if (sessionId) {
43
- const result = restoreSession(sessionId);
43
+ const result = restoreSession(sessionId, active_client);
44
44
  if (!result.found || !result.session) {
45
45
  return { found: false, message: `No session found with ID: ${sessionId}`, restored_at: now };
46
46
  }
@@ -55,7 +55,7 @@ export function continueSession(sessionId) {
55
55
  restored_at: now,
56
56
  };
57
57
  }
58
- const result = restoreSession(sessions[0].id);
58
+ const result = restoreSession(sessions[0].id, active_client);
59
59
  if (!result.found || !result.session) {
60
60
  return { found: false, message: 'Could not restore the most recent session.', restored_at: now };
61
61
  }
@@ -98,6 +98,7 @@ function buildContinueResult(session, now) {
98
98
  found: true,
99
99
  session_id: session.id,
100
100
  platform: session.platform,
101
+ active_client: session.active_client ?? undefined,
101
102
  summary: session.summary ?? undefined,
102
103
  context,
103
104
  task_state,
@@ -19,8 +19,18 @@ export function getDb() {
19
19
  _db.exec(CREATE_TABLES);
20
20
  migrateCouncilOutcomes(_db);
21
21
  migrateCouncilColumns(_db);
22
+ migrateSessionColumns(_db);
22
23
  return _db;
23
24
  }
25
+ // Adds active_client and last_resumed_at columns if they don't exist
26
+ function migrateSessionColumns(db) {
27
+ const cols = db.prepare('PRAGMA table_info(sessions)').all();
28
+ const names = new Set(cols.map(c => c.name));
29
+ if (!names.has('active_client'))
30
+ db.exec('ALTER TABLE sessions ADD COLUMN active_client TEXT');
31
+ if (!names.has('last_resumed_at'))
32
+ db.exec('ALTER TABLE sessions ADD COLUMN last_resumed_at TEXT');
33
+ }
24
34
  // Adds legal and security columns if they don't exist (Phase 3 → Phase 3.1 migration)
25
35
  function migrateCouncilColumns(db) {
26
36
  const cols = db.prepare('PRAGMA table_info(council_outcomes)').all();
@@ -58,11 +68,18 @@ export function saveSession(input) {
58
68
  stmt.run(id, now, input.platform ?? 'claude', input.project_dir ?? null, input.summary ?? null, input.context ? JSON.stringify(input.context) : null, input.task_state ? JSON.stringify(input.task_state) : null, input.token_count ?? 0);
59
69
  return { session_id: id, saved_at: now };
60
70
  }
61
- export function restoreSession(session_id) {
71
+ export function restoreSession(session_id, active_client) {
62
72
  const db = getDb();
63
73
  const row = db.prepare('SELECT * FROM sessions WHERE id = ?').get(session_id);
64
74
  if (!row)
65
75
  return { found: false };
76
+ if (active_client) {
77
+ const now = new Date().toISOString();
78
+ db.prepare('UPDATE sessions SET active_client = ?, last_resumed_at = ? WHERE id = ?')
79
+ .run(active_client, now, session_id);
80
+ row.active_client = active_client;
81
+ row.last_resumed_at = now;
82
+ }
66
83
  return { found: true, session: row };
67
84
  }
68
85
  export function listSessions(limit = 10) {
@@ -2,16 +2,18 @@
2
2
  // All tables created on first run — zero setup required
3
3
  export const CREATE_TABLES = `
4
4
  CREATE TABLE IF NOT EXISTS sessions (
5
- id TEXT PRIMARY KEY,
6
- started_at TEXT NOT NULL,
7
- ended_at TEXT,
8
- platform TEXT NOT NULL DEFAULT 'claude',
9
- project_dir TEXT,
10
- summary TEXT,
11
- context TEXT,
12
- task_state TEXT,
13
- token_count INTEGER DEFAULT 0,
14
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
5
+ id TEXT PRIMARY KEY,
6
+ started_at TEXT NOT NULL,
7
+ ended_at TEXT,
8
+ platform TEXT NOT NULL DEFAULT 'claude',
9
+ active_client TEXT,
10
+ last_resumed_at TEXT,
11
+ project_dir TEXT,
12
+ summary TEXT,
13
+ context TEXT,
14
+ task_state TEXT,
15
+ token_count INTEGER DEFAULT 0,
16
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
15
17
  );
16
18
 
17
19
  CREATE TABLE IF NOT EXISTS decisions (
package/dist/server.js CHANGED
@@ -18,7 +18,11 @@ import { runPipeline } from './workflow/pipeline.js';
18
18
  import { loadPlugins, listPlugins } from './plugins/loader.js';
19
19
  import { readFileSync } from 'node:fs';
20
20
  import { extname, basename } from 'node:path';
21
- const VERSION = '1.0.0';
21
+ const VERSION = '1.1.0';
22
+ // Tracks the project_dir of the most recently active session in this process.
23
+ // Used as a fallback when memory_store/memory_search are called without an explicit project_dir,
24
+ // so memories are automatically scoped to the current project.
25
+ let activeProjectDir = null;
22
26
  const server = new Server({ name: 'veto', version: VERSION }, {
23
27
  capabilities: {
24
28
  tools: {},
@@ -83,6 +87,11 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
83
87
  type: 'string',
84
88
  description: 'UUID of the session to restore.',
85
89
  },
90
+ resuming_as: {
91
+ type: 'string',
92
+ description: 'The AI client resuming this session (e.g. "claude", "gemini", "codex"). Recorded as active_client.',
93
+ enum: ['claude', 'gemini', 'codex'],
94
+ },
86
95
  },
87
96
  required: ['session_id'],
88
97
  },
@@ -470,6 +479,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
470
479
  type: 'object',
471
480
  properties: {
472
481
  session_id: { type: 'string', description: 'Optional. Session ID from veto_handoff. If omitted, the most recent saved session is restored.' },
482
+ resuming_as: { type: 'string', description: 'The AI client resuming this session (e.g. "gemini"). Recorded as active_client so you can track which tool is currently working on it.', enum: ['claude', 'gemini', 'codex'] },
473
483
  },
474
484
  required: [],
475
485
  },
@@ -594,12 +604,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
594
604
  };
595
605
  }
596
606
  case 'veto_session_save': {
607
+ const sessionProjectDir = args?.project_dir ? String(args.project_dir) : undefined;
608
+ if (sessionProjectDir)
609
+ activeProjectDir = sessionProjectDir;
597
610
  const result = saveSession({
598
611
  summary: String(args?.summary ?? ''),
599
612
  context: String(args?.context ?? ''),
600
613
  task_state: args?.task_state ? String(args.task_state) : undefined,
601
614
  platform: args?.platform ? String(args.platform) : 'claude',
602
- project_dir: args?.project_dir ? String(args.project_dir) : undefined,
615
+ project_dir: sessionProjectDir,
603
616
  token_count: typeof args?.token_count === 'number' ? args.token_count : 0,
604
617
  });
605
618
  return {
@@ -617,7 +630,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
617
630
  }
618
631
  case 'veto_session_restore': {
619
632
  const session_id = String(args?.session_id ?? '');
620
- const result = restoreSession(session_id);
633
+ const resuming_as = args?.resuming_as ? String(args.resuming_as) : undefined;
634
+ const result = restoreSession(session_id, resuming_as);
621
635
  if (!result.found) {
622
636
  return {
623
637
  content: [
@@ -630,6 +644,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
630
644
  };
631
645
  }
632
646
  const s = result.session;
647
+ if (s.project_dir)
648
+ activeProjectDir = s.project_dir;
633
649
  return {
634
650
  content: [
635
651
  {
@@ -637,7 +653,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
637
653
  text: JSON.stringify({
638
654
  success: true,
639
655
  session_id: s.id,
640
- platform: s.platform,
656
+ created_by: s.platform,
657
+ active_client: s.active_client ?? s.platform,
658
+ last_resumed_at: s.last_resumed_at,
641
659
  started_at: s.started_at,
642
660
  ended_at: s.ended_at,
643
661
  project_dir: s.project_dir,
@@ -932,7 +950,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
932
950
  content,
933
951
  type: args?.type ? String(args.type) : 'solution',
934
952
  tags: Array.isArray(args?.tags) ? args.tags.map(String) : undefined,
935
- project_dir: args?.project_dir ? String(args.project_dir) : undefined,
953
+ project_dir: args?.project_dir ? String(args.project_dir) : (activeProjectDir ?? undefined),
936
954
  session_id: args?.session_id ? String(args.session_id) : undefined,
937
955
  relevance: typeof args?.relevance === 'number' ? args.relevance : 1.0,
938
956
  });
@@ -942,7 +960,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
942
960
  const results = searchKnowledge({
943
961
  query: args?.query ? String(args.query) : undefined,
944
962
  type: args?.type ? String(args.type) : undefined,
945
- project_dir: args?.project_dir ? String(args.project_dir) : undefined,
963
+ project_dir: args?.project_dir ? String(args.project_dir) : (activeProjectDir ?? undefined),
946
964
  limit: typeof args?.limit === 'number' ? args.limit : 10,
947
965
  });
948
966
  return {
@@ -1061,16 +1079,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1061
1079
  return { content: [{ type: 'text', text: result.instructions + '\n\n' + JSON.stringify({ session_id: result.session_id, to_platform: result.to_platform, saved_at: result.saved_at, reason: result.reason }, null, 2) }] };
1062
1080
  }
1063
1081
  case 'veto_continue': {
1064
- const result = continueSession(args?.session_id ? String(args.session_id) : undefined);
1082
+ const resuming_as = args?.resuming_as ? String(args.resuming_as) : undefined;
1083
+ const result = continueSession(args?.session_id ? String(args.session_id) : undefined, resuming_as);
1065
1084
  if (!result.found) {
1066
1085
  return { content: [{ type: 'text', text: JSON.stringify({ success: false, message: result.message }, null, 2) }], isError: true };
1067
1086
  }
1087
+ if (result.project_dir)
1088
+ activeProjectDir = result.project_dir;
1068
1089
  return {
1069
1090
  content: [{
1070
1091
  type: 'text',
1071
1092
  text: result.message + '\n\n' + JSON.stringify({
1072
1093
  session_id: result.session_id,
1073
- platform: result.platform,
1094
+ created_by: result.platform,
1095
+ active_client: result.active_client ?? result.platform,
1074
1096
  summary: result.summary,
1075
1097
  context: result.context,
1076
1098
  task_state: result.task_state,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jigyasudham/veto",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "50 agents. 34 tools. 3 AIs. Self-learning. Zero extra cost.",
5
5
  "keywords": [
6
6
  "mcp",