@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 +8 -2
- package/dist/adapters/index.js +4 -3
- package/dist/memory/local.js +18 -1
- package/dist/memory/schema.js +12 -10
- package/dist/server.js +30 -8
- package/package.json +1 -1
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
|
package/dist/adapters/index.js
CHANGED
|
@@ -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,
|
package/dist/memory/local.js
CHANGED
|
@@ -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) {
|
package/dist/memory/schema.js
CHANGED
|
@@ -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
|
|
6
|
-
started_at
|
|
7
|
-
ended_at
|
|
8
|
-
platform
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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.
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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,
|