@algosuite/vo-mcp 0.1.0 → 0.2.0-beta.1
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/bin/vo-mcp +2 -5
- package/dist/cli.js +1087 -319
- package/dist/cli.js.map +4 -4
- package/dist/index.js +1083 -92
- package/dist/index.js.map +4 -4
- package/dist/install-cli.js +12 -7
- package/dist/install-cli.js.map +2 -2
- package/dist/login-cli.js +9 -5
- package/dist/login-cli.js.map +2 -2
- package/dist/runner-cli.js +1940 -0
- package/dist/runner-cli.js.map +7 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,291 @@
|
|
|
1
1
|
import { createRequire as __cr } from 'module'; const require = __cr(import.meta.url);
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res, err) => function __init() {
|
|
5
|
+
if (err) throw err[0];
|
|
6
|
+
try {
|
|
7
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
8
|
+
} catch (e) {
|
|
9
|
+
throw err = [e], e;
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/cloud/auth-token-source.ts
|
|
18
|
+
var auth_token_source_exports = {};
|
|
19
|
+
__export(auth_token_source_exports, {
|
|
20
|
+
FIREBASE_SECURETOKEN_URL: () => FIREBASE_SECURETOKEN_URL,
|
|
21
|
+
FIREBASE_TOKEN_REFERER: () => FIREBASE_TOKEN_REFERER,
|
|
22
|
+
createAuthTokenSourceFromEnv: () => createAuthTokenSourceFromEnv,
|
|
23
|
+
createFirebaseRefreshTokenSource: () => createFirebaseRefreshTokenSource,
|
|
24
|
+
createStaticTokenSource: () => createStaticTokenSource
|
|
25
|
+
});
|
|
26
|
+
function createStaticTokenSource(token, kind = "admin-token") {
|
|
27
|
+
const value = token.trim();
|
|
28
|
+
return { kind, getToken: async () => value.length > 0 ? value : null };
|
|
29
|
+
}
|
|
30
|
+
function createFirebaseRefreshTokenSource(opts) {
|
|
31
|
+
const refreshToken = opts.refreshToken.trim();
|
|
32
|
+
const apiKey = opts.apiKey.trim();
|
|
33
|
+
const now = opts.now ?? (() => Date.now());
|
|
34
|
+
const fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
35
|
+
let cachedToken = null;
|
|
36
|
+
let expiresAtMs = 0;
|
|
37
|
+
let inFlight = null;
|
|
38
|
+
async function refresh() {
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetchFn(`${FIREBASE_SECURETOKEN_URL}?key=${encodeURIComponent(apiKey)}`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: {
|
|
43
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
44
|
+
referer: FIREBASE_TOKEN_REFERER
|
|
45
|
+
},
|
|
46
|
+
body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`
|
|
47
|
+
});
|
|
48
|
+
const text = await res.text();
|
|
49
|
+
if (res.status < 200 || res.status >= 300) {
|
|
50
|
+
cachedToken = null;
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const parsed = JSON.parse(text);
|
|
54
|
+
const idToken = typeof parsed.id_token === "string" ? parsed.id_token : "";
|
|
55
|
+
if (!idToken) {
|
|
56
|
+
cachedToken = null;
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const expiresInSec = Number(parsed.expires_in);
|
|
60
|
+
const ttlMs = Number.isFinite(expiresInSec) && expiresInSec > 0 ? expiresInSec * 1e3 : 36e5;
|
|
61
|
+
cachedToken = idToken;
|
|
62
|
+
expiresAtMs = now() + ttlMs;
|
|
63
|
+
return idToken;
|
|
64
|
+
} catch {
|
|
65
|
+
cachedToken = null;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
kind: "firebase-refresh",
|
|
71
|
+
async getToken() {
|
|
72
|
+
if (cachedToken && now() < expiresAtMs - REFRESH_SKEW_MS) return cachedToken;
|
|
73
|
+
if (!inFlight) {
|
|
74
|
+
inFlight = refresh().finally(() => {
|
|
75
|
+
inFlight = null;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return inFlight;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function createAuthTokenSourceFromEnv(env = process.env, fetchFn, readStoredCred = () => null) {
|
|
83
|
+
const refreshToken = env["VO_USER_REFRESH_TOKEN"]?.trim();
|
|
84
|
+
const apiKey = env["VO_FIREBASE_API_KEY"]?.trim();
|
|
85
|
+
const idToken = env["VO_USER_ID_TOKEN"]?.trim();
|
|
86
|
+
const adminToken = env["VO_CONTROL_PLANE_ADMIN_TOKEN"]?.trim();
|
|
87
|
+
if (refreshToken || apiKey) {
|
|
88
|
+
if (!refreshToken || !apiKey) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
"Per-user refresh auth requires BOTH VO_USER_REFRESH_TOKEN and VO_FIREBASE_API_KEY"
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return createFirebaseRefreshTokenSource({
|
|
94
|
+
refreshToken,
|
|
95
|
+
apiKey,
|
|
96
|
+
...fetchFn ? { fetchFn } : {}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
if (idToken) return createStaticTokenSource(idToken, "firebase-id-token");
|
|
100
|
+
const stored = readStoredCred();
|
|
101
|
+
if (stored?.vo_credential && stored.vo_credential.trim()) {
|
|
102
|
+
return createStaticTokenSource(stored.vo_credential.trim(), "vo-credential");
|
|
103
|
+
}
|
|
104
|
+
if (stored && stored.refresh_token?.trim() && stored.api_key?.trim()) {
|
|
105
|
+
return createFirebaseRefreshTokenSource({
|
|
106
|
+
refreshToken: stored.refresh_token.trim(),
|
|
107
|
+
apiKey: stored.api_key.trim(),
|
|
108
|
+
...fetchFn ? { fetchFn } : {}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (adminToken) return createStaticTokenSource(adminToken, "admin-token");
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
var FIREBASE_SECURETOKEN_URL, FIREBASE_TOKEN_REFERER, REFRESH_SKEW_MS;
|
|
115
|
+
var init_auth_token_source = __esm({
|
|
116
|
+
"src/cloud/auth-token-source.ts"() {
|
|
117
|
+
"use strict";
|
|
118
|
+
FIREBASE_SECURETOKEN_URL = "https://securetoken.googleapis.com/v1/token";
|
|
119
|
+
FIREBASE_TOKEN_REFERER = "https://algosuite.ai/";
|
|
120
|
+
REFRESH_SKEW_MS = 6e4;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// src/cloud/keychain.ts
|
|
125
|
+
import { createRequire } from "node:module";
|
|
126
|
+
function loadKeyring() {
|
|
127
|
+
if (cached !== void 0) return cached;
|
|
128
|
+
try {
|
|
129
|
+
const req = createRequire(import.meta.url);
|
|
130
|
+
const mod = req("@napi-rs/keyring");
|
|
131
|
+
cached = mod && typeof mod.Entry === "function" ? mod : null;
|
|
132
|
+
} catch {
|
|
133
|
+
cached = null;
|
|
134
|
+
}
|
|
135
|
+
return cached;
|
|
136
|
+
}
|
|
137
|
+
function keychainAvailable() {
|
|
138
|
+
return loadKeyring() !== null;
|
|
139
|
+
}
|
|
140
|
+
function keychainGet() {
|
|
141
|
+
const k = loadKeyring();
|
|
142
|
+
if (!k) return null;
|
|
143
|
+
try {
|
|
144
|
+
return new k.Entry(SERVICE, ACCOUNT).getPassword();
|
|
145
|
+
} catch {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function keychainSet(secret) {
|
|
150
|
+
const k = loadKeyring();
|
|
151
|
+
if (!k) return false;
|
|
152
|
+
try {
|
|
153
|
+
new k.Entry(SERVICE, ACCOUNT).setPassword(secret);
|
|
154
|
+
return true;
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function keychainDelete() {
|
|
160
|
+
const k = loadKeyring();
|
|
161
|
+
if (!k) return false;
|
|
162
|
+
try {
|
|
163
|
+
return new k.Entry(SERVICE, ACCOUNT).deletePassword();
|
|
164
|
+
} catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
var SERVICE, ACCOUNT, cached;
|
|
169
|
+
var init_keychain = __esm({
|
|
170
|
+
"src/cloud/keychain.ts"() {
|
|
171
|
+
"use strict";
|
|
172
|
+
SERVICE = "vo-mcp";
|
|
173
|
+
ACCOUNT = "refresh-credential";
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// src/cloud/credential-store.ts
|
|
178
|
+
var credential_store_exports = {};
|
|
179
|
+
__export(credential_store_exports, {
|
|
180
|
+
KEYCHAIN_LOCATION: () => KEYCHAIN_LOCATION,
|
|
181
|
+
credentialPath: () => credentialPath,
|
|
182
|
+
readStoredCredential: () => readStoredCredential,
|
|
183
|
+
writeStoredCredential: () => writeStoredCredential
|
|
184
|
+
});
|
|
185
|
+
import { homedir as homedir3 } from "node:os";
|
|
186
|
+
import { join as join5, dirname as dirname4 } from "node:path";
|
|
187
|
+
import {
|
|
188
|
+
existsSync as existsSync3,
|
|
189
|
+
mkdirSync as mkdirSync2,
|
|
190
|
+
readFileSync as readFileSync5,
|
|
191
|
+
writeFileSync as writeFileSync2,
|
|
192
|
+
chmodSync as chmodSync2,
|
|
193
|
+
rmSync
|
|
194
|
+
} from "node:fs";
|
|
195
|
+
function credentialPath(env = process.env) {
|
|
196
|
+
const override = env["VO_MCP_CREDENTIALS_PATH"]?.trim();
|
|
197
|
+
if (override) return override;
|
|
198
|
+
return join5(homedir3(), ".config", "vo-mcp", "credentials.json");
|
|
199
|
+
}
|
|
200
|
+
function keychainEnabled(env, keychain) {
|
|
201
|
+
const disabled = (env["VO_MCP_DISABLE_KEYCHAIN"] ?? "").trim().toLowerCase();
|
|
202
|
+
if (disabled === "1" || disabled === "true" || disabled === "yes") return false;
|
|
203
|
+
return keychain.available();
|
|
204
|
+
}
|
|
205
|
+
function deserialize(raw) {
|
|
206
|
+
try {
|
|
207
|
+
const parsed = JSON.parse(raw);
|
|
208
|
+
const refresh = typeof parsed.refresh_token === "string" ? parsed.refresh_token.trim() : "";
|
|
209
|
+
const apiKey = typeof parsed.api_key === "string" ? parsed.api_key.trim() : "";
|
|
210
|
+
const voCred = typeof parsed.vo_credential === "string" ? parsed.vo_credential.trim() : "";
|
|
211
|
+
if (!voCred && (!refresh || !apiKey)) return null;
|
|
212
|
+
return {
|
|
213
|
+
...refresh ? { refresh_token: refresh } : {},
|
|
214
|
+
...apiKey ? { api_key: apiKey } : {},
|
|
215
|
+
...voCred ? { vo_credential: voCred } : {},
|
|
216
|
+
...typeof parsed.vo_credential_expires_at === "string" ? { vo_credential_expires_at: parsed.vo_credential_expires_at } : {},
|
|
217
|
+
...typeof parsed.email === "string" ? { email: parsed.email } : {},
|
|
218
|
+
...typeof parsed.stored_at === "string" ? { stored_at: parsed.stored_at } : {}
|
|
219
|
+
};
|
|
220
|
+
} catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function readFromFile(env) {
|
|
225
|
+
try {
|
|
226
|
+
const p = credentialPath(env);
|
|
227
|
+
if (!existsSync3(p)) return null;
|
|
228
|
+
return deserialize(readFileSync5(p, "utf8"));
|
|
229
|
+
} catch {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function readStoredCredential(env = process.env, keychain = realKeychain) {
|
|
234
|
+
if (keychainEnabled(env, keychain)) {
|
|
235
|
+
const raw = keychain.get();
|
|
236
|
+
const fromKeychain = raw ? deserialize(raw) : null;
|
|
237
|
+
if (fromKeychain) return fromKeychain;
|
|
238
|
+
}
|
|
239
|
+
return readFromFile(env);
|
|
240
|
+
}
|
|
241
|
+
function deleteFile(env) {
|
|
242
|
+
try {
|
|
243
|
+
rmSync(credentialPath(env), { force: true });
|
|
244
|
+
} catch {
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function writeToFile(payload, env) {
|
|
248
|
+
const p = credentialPath(env);
|
|
249
|
+
mkdirSync2(dirname4(p), { recursive: true });
|
|
250
|
+
writeFileSync2(p, `${JSON.stringify(payload, null, 2)}
|
|
251
|
+
`, { mode: 384 });
|
|
252
|
+
try {
|
|
253
|
+
chmodSync2(p, 384);
|
|
254
|
+
} catch {
|
|
255
|
+
}
|
|
256
|
+
return p;
|
|
257
|
+
}
|
|
258
|
+
function writeStoredCredential(cred, storedAt, env = process.env, keychain = realKeychain) {
|
|
259
|
+
const payload = {
|
|
260
|
+
...cred.refresh_token ? { refresh_token: cred.refresh_token } : {},
|
|
261
|
+
...cred.api_key ? { api_key: cred.api_key } : {},
|
|
262
|
+
...cred.vo_credential ? { vo_credential: cred.vo_credential } : {},
|
|
263
|
+
...cred.vo_credential_expires_at ? { vo_credential_expires_at: cred.vo_credential_expires_at } : {},
|
|
264
|
+
...cred.email ? { email: cred.email } : {},
|
|
265
|
+
stored_at: cred.stored_at ?? storedAt
|
|
266
|
+
};
|
|
267
|
+
if (keychainEnabled(env, keychain) && keychain.set(JSON.stringify(payload))) {
|
|
268
|
+
deleteFile(env);
|
|
269
|
+
return KEYCHAIN_LOCATION;
|
|
270
|
+
}
|
|
271
|
+
const p = writeToFile(payload, env);
|
|
272
|
+
if (keychainEnabled(env, keychain)) keychain.delete();
|
|
273
|
+
return p;
|
|
274
|
+
}
|
|
275
|
+
var realKeychain, KEYCHAIN_LOCATION;
|
|
276
|
+
var init_credential_store = __esm({
|
|
277
|
+
"src/cloud/credential-store.ts"() {
|
|
278
|
+
"use strict";
|
|
279
|
+
init_keychain();
|
|
280
|
+
realKeychain = {
|
|
281
|
+
available: keychainAvailable,
|
|
282
|
+
get: keychainGet,
|
|
283
|
+
set: keychainSet,
|
|
284
|
+
delete: keychainDelete
|
|
285
|
+
};
|
|
286
|
+
KEYCHAIN_LOCATION = 'OS keychain (service "vo-mcp")';
|
|
287
|
+
}
|
|
288
|
+
});
|
|
2
289
|
|
|
3
290
|
// src/server.ts
|
|
4
291
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
@@ -987,10 +1274,10 @@ async function handleCheckAssertionStrength(deps, rawInput, _signal) {
|
|
|
987
1274
|
const key = deps.cache.keyFor(TOOL_NAME, rawInput);
|
|
988
1275
|
const excerpt = rawInput.source.slice(0, 300);
|
|
989
1276
|
const inputSizeBytes = bytesOf(rawInput.source);
|
|
990
|
-
const
|
|
991
|
-
if (
|
|
1277
|
+
const cached2 = deps.cache.get(key);
|
|
1278
|
+
if (cached2) {
|
|
992
1279
|
const envelope2 = {
|
|
993
|
-
...
|
|
1280
|
+
...cached2.value,
|
|
994
1281
|
cache: { hit: true, key }
|
|
995
1282
|
};
|
|
996
1283
|
deps.events.append({
|
|
@@ -1210,8 +1497,8 @@ async function handleCheckHollowTest(deps, rawInput, signal) {
|
|
|
1210
1497
|
const key = deps.cache.keyFor(TOOL_NAME2, rawInput);
|
|
1211
1498
|
const excerpt = rawInput.source.slice(0, 300);
|
|
1212
1499
|
const inputSizeBytes = bytesOf(rawInput.source);
|
|
1213
|
-
const
|
|
1214
|
-
if (
|
|
1500
|
+
const cached2 = deps.cache.get(key);
|
|
1501
|
+
if (cached2 !== null) {
|
|
1215
1502
|
const replayEvent = {
|
|
1216
1503
|
...buildBaseEvent({
|
|
1217
1504
|
tool: TOOL_NAME2,
|
|
@@ -1222,14 +1509,14 @@ async function handleCheckHollowTest(deps, rawInput, signal) {
|
|
|
1222
1509
|
session: deps.session,
|
|
1223
1510
|
now: deps.now()
|
|
1224
1511
|
}),
|
|
1225
|
-
per_model_verdicts:
|
|
1226
|
-
synthesized_verdict:
|
|
1227
|
-
consensus_confidence:
|
|
1228
|
-
consensus_engine_version:
|
|
1512
|
+
per_model_verdicts: cached2.value.payload.per_model_verdicts,
|
|
1513
|
+
synthesized_verdict: cached2.value.payload.synthesized_verdict ?? null,
|
|
1514
|
+
consensus_confidence: cached2.value.payload.synthesized_verdict?.confidence ?? null,
|
|
1515
|
+
consensus_engine_version: cached2.value.payload.engine_version ?? null,
|
|
1229
1516
|
cache_hit: true
|
|
1230
1517
|
};
|
|
1231
1518
|
deps.events.append(replayEvent);
|
|
1232
|
-
return jsonContent({ ...
|
|
1519
|
+
return jsonContent({ ...cached2.value, cache: { hit: true, key } });
|
|
1233
1520
|
}
|
|
1234
1521
|
const kbResult = findRulesByMetadata({ category: "testing" });
|
|
1235
1522
|
const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);
|
|
@@ -1394,8 +1681,8 @@ async function handleVerifyAnswer(deps, rawInput, signal) {
|
|
|
1394
1681
|
const isDeep = gateType === DEEP_GATE;
|
|
1395
1682
|
const kbResult = isDeep ? findRulesByMetadata({ category: "security" }) : { rules: [], error: null };
|
|
1396
1683
|
const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);
|
|
1397
|
-
const
|
|
1398
|
-
if (
|
|
1684
|
+
const cached2 = deps.cache.get(key);
|
|
1685
|
+
if (cached2 !== null) {
|
|
1399
1686
|
const replayEvent = {
|
|
1400
1687
|
...buildBaseEvent({
|
|
1401
1688
|
tool: TOOL_NAME3,
|
|
@@ -1406,14 +1693,14 @@ async function handleVerifyAnswer(deps, rawInput, signal) {
|
|
|
1406
1693
|
session: deps.session,
|
|
1407
1694
|
now: deps.now()
|
|
1408
1695
|
}),
|
|
1409
|
-
per_model_verdicts:
|
|
1410
|
-
synthesized_verdict:
|
|
1411
|
-
consensus_confidence:
|
|
1412
|
-
consensus_engine_version:
|
|
1696
|
+
per_model_verdicts: cached2.value.payload.per_model_verdicts,
|
|
1697
|
+
synthesized_verdict: cached2.value.payload.synthesized_verdict ?? null,
|
|
1698
|
+
consensus_confidence: cached2.value.payload.synthesized_verdict?.confidence ?? null,
|
|
1699
|
+
consensus_engine_version: cached2.value.payload.engine_version ?? null,
|
|
1413
1700
|
cache_hit: true
|
|
1414
1701
|
};
|
|
1415
1702
|
deps.events.append(replayEvent);
|
|
1416
|
-
return jsonContent({ ...
|
|
1703
|
+
return jsonContent({ ...cached2.value, cache: { hit: true, key } });
|
|
1417
1704
|
}
|
|
1418
1705
|
const baseEvent = buildBaseEvent({
|
|
1419
1706
|
tool: TOOL_NAME3,
|
|
@@ -1519,6 +1806,45 @@ var ALL_RECOGNIZED_GATE_TYPES = [
|
|
|
1519
1806
|
...KNOWN_GATE_TYPES
|
|
1520
1807
|
];
|
|
1521
1808
|
|
|
1809
|
+
// src/tools/consensus-judgment-source-grounded.ts
|
|
1810
|
+
function isStringArray(v) {
|
|
1811
|
+
return Array.isArray(v) && v.every((e) => typeof e === "string");
|
|
1812
|
+
}
|
|
1813
|
+
function isToolSourceGrounded(v) {
|
|
1814
|
+
if (typeof v !== "object" || v === null) return false;
|
|
1815
|
+
const o = v;
|
|
1816
|
+
if (o["enable_citation_grading"] !== void 0 && typeof o["enable_citation_grading"] !== "boolean") {
|
|
1817
|
+
return false;
|
|
1818
|
+
}
|
|
1819
|
+
if (o["escalate_below"] !== void 0 && typeof o["escalate_below"] !== "number") return false;
|
|
1820
|
+
return true;
|
|
1821
|
+
}
|
|
1822
|
+
function normalizeSourceGrounded(input) {
|
|
1823
|
+
const sourceUrls = input.source_urls !== void 0 ? input.source_urls.filter((u) => u.trim().length > 0) : [];
|
|
1824
|
+
const active = sourceUrls.length > 0;
|
|
1825
|
+
const gradingEnabled = active && input.source_grounded?.enable_citation_grading === true;
|
|
1826
|
+
const escalateBelow = input.source_grounded?.escalate_below;
|
|
1827
|
+
const config = gradingEnabled ? {
|
|
1828
|
+
citation_grader: {
|
|
1829
|
+
enabled: true,
|
|
1830
|
+
...escalateBelow !== void 0 ? { escalate_below: escalateBelow } : {}
|
|
1831
|
+
}
|
|
1832
|
+
} : void 0;
|
|
1833
|
+
const cacheFragments = {};
|
|
1834
|
+
if (active) cacheFragments["source_urls"] = sourceUrls;
|
|
1835
|
+
if (gradingEnabled) {
|
|
1836
|
+
cacheFragments["citation_grading"] = true;
|
|
1837
|
+
if (escalateBelow !== void 0) cacheFragments["escalate_below"] = escalateBelow;
|
|
1838
|
+
}
|
|
1839
|
+
return {
|
|
1840
|
+
active,
|
|
1841
|
+
sourceUrls,
|
|
1842
|
+
gradingEnabled,
|
|
1843
|
+
...config !== void 0 ? { config } : {},
|
|
1844
|
+
cacheFragments
|
|
1845
|
+
};
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1522
1848
|
// src/tools/consensus-judgment.ts
|
|
1523
1849
|
var TOOL_NAME4 = "vo_consensus_judgment";
|
|
1524
1850
|
var MAX_PROMPT_BYTES = 64 * 1024;
|
|
@@ -1541,6 +1867,26 @@ var inputSchema4 = {
|
|
|
1541
1867
|
type: "object",
|
|
1542
1868
|
description: "Tool-specific context (e.g. source file, diff, observed value).",
|
|
1543
1869
|
additionalProperties: true
|
|
1870
|
+
},
|
|
1871
|
+
source_urls: {
|
|
1872
|
+
type: "array",
|
|
1873
|
+
items: { type: "string" },
|
|
1874
|
+
description: "Authoritative source URLs to ground the judgment against (Tier-4). When NON-EMPTY, the engine routes to its source-grounded path: it fetches the sources, flags low-confidence retrievals (surfaced as low_confidence_sources), and \u2014 when source_grounded.enable_citation_grading is true \u2014 grades each claim against the cited source text. When absent/empty the call uses the unchanged sourceless consensus path."
|
|
1875
|
+
},
|
|
1876
|
+
source_grounded: {
|
|
1877
|
+
type: "object",
|
|
1878
|
+
description: "Optional source-grounded behaviour toggles. Only meaningful when source_urls is non-empty.",
|
|
1879
|
+
properties: {
|
|
1880
|
+
enable_citation_grading: {
|
|
1881
|
+
type: "boolean",
|
|
1882
|
+
description: "Opt IN to deterministic citation grading (default OFF). A failing grade raises an escalation signal, so this is behaviour-changing. When true, a real claim classifier (a single cheap model call) grades each claim against the cited sources."
|
|
1883
|
+
},
|
|
1884
|
+
escalate_below: {
|
|
1885
|
+
type: "number",
|
|
1886
|
+
description: "Uncertain-claim escalation floor [0..1]; an uncertain claim with confidence below this triggers escalation. Engine default 0.85 when omitted."
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
additionalProperties: false
|
|
1544
1890
|
}
|
|
1545
1891
|
},
|
|
1546
1892
|
required: ["prompt"],
|
|
@@ -1552,6 +1898,8 @@ function isToolInput4(v) {
|
|
|
1552
1898
|
const o = v;
|
|
1553
1899
|
if (typeof o["prompt"] !== "string") return false;
|
|
1554
1900
|
if (o["gate_type"] !== void 0 && typeof o["gate_type"] !== "string") return false;
|
|
1901
|
+
if (o["source_urls"] !== void 0 && !isStringArray(o["source_urls"])) return false;
|
|
1902
|
+
if (o["source_grounded"] !== void 0 && !isToolSourceGrounded(o["source_grounded"])) return false;
|
|
1555
1903
|
return true;
|
|
1556
1904
|
}
|
|
1557
1905
|
function isAcceptedGateType(v) {
|
|
@@ -1577,15 +1925,20 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
|
|
|
1577
1925
|
const rawGate = rawInput.gate_type ?? DEFAULT_GATE_TYPE;
|
|
1578
1926
|
const gateType = isAcceptedGateType(rawGate) ? rawGate : DEFAULT_GATE_TYPE;
|
|
1579
1927
|
const inputSizeBytes = bytesOf(rawInput.prompt) + bytesOf(contextStrForSize);
|
|
1928
|
+
const sg = normalizeSourceGrounded({
|
|
1929
|
+
...rawInput.source_urls !== void 0 ? { source_urls: rawInput.source_urls } : {},
|
|
1930
|
+
...rawInput.source_grounded !== void 0 ? { source_grounded: rawInput.source_grounded } : {}
|
|
1931
|
+
});
|
|
1580
1932
|
const cacheInput = {
|
|
1581
1933
|
prompt: rawInput.prompt,
|
|
1582
1934
|
gate_type: gateType,
|
|
1583
|
-
...rawInput.context !== void 0 ? { context: rawInput.context } : {}
|
|
1935
|
+
...rawInput.context !== void 0 ? { context: rawInput.context } : {},
|
|
1936
|
+
...sg.cacheFragments
|
|
1584
1937
|
};
|
|
1585
1938
|
const key = deps.cache.keyFor(TOOL_NAME4, cacheInput);
|
|
1586
1939
|
const excerpt = rawInput.prompt.slice(0, 300);
|
|
1587
|
-
const
|
|
1588
|
-
if (
|
|
1940
|
+
const cached2 = deps.cache.get(key);
|
|
1941
|
+
if (cached2 !== null) {
|
|
1589
1942
|
const replayEvent = buildBaseEvent({
|
|
1590
1943
|
tool: TOOL_NAME4,
|
|
1591
1944
|
gateType,
|
|
@@ -1597,15 +1950,15 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
|
|
|
1597
1950
|
});
|
|
1598
1951
|
const enrichedReplay = {
|
|
1599
1952
|
...replayEvent,
|
|
1600
|
-
per_model_verdicts:
|
|
1601
|
-
synthesized_verdict:
|
|
1602
|
-
consensus_confidence:
|
|
1603
|
-
consensus_engine_version:
|
|
1953
|
+
per_model_verdicts: cached2.value.payload.per_model_verdicts,
|
|
1954
|
+
synthesized_verdict: cached2.value.payload.synthesized_verdict ?? null,
|
|
1955
|
+
consensus_confidence: cached2.value.payload.synthesized_verdict?.confidence ?? null,
|
|
1956
|
+
consensus_engine_version: cached2.value.payload.engine_version ?? null,
|
|
1604
1957
|
cache_hit: true
|
|
1605
1958
|
};
|
|
1606
1959
|
deps.events.append(enrichedReplay);
|
|
1607
1960
|
const replayEnvelope = {
|
|
1608
|
-
...
|
|
1961
|
+
...cached2.value,
|
|
1609
1962
|
cache: { hit: true, key }
|
|
1610
1963
|
};
|
|
1611
1964
|
return jsonContent(replayEnvelope);
|
|
@@ -1626,7 +1979,9 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
|
|
|
1626
1979
|
gate_type: gateType,
|
|
1627
1980
|
prompt: rawInput.prompt,
|
|
1628
1981
|
...rawInput.context !== void 0 ? { caller_context: rawInput.context } : {},
|
|
1629
|
-
...signal !== void 0 ? { signal } : {}
|
|
1982
|
+
...signal !== void 0 ? { signal } : {},
|
|
1983
|
+
...sg.active ? { source_urls: sg.sourceUrls } : {},
|
|
1984
|
+
...sg.config !== void 0 ? { source_grounded: sg.config } : {}
|
|
1630
1985
|
});
|
|
1631
1986
|
} catch (err) {
|
|
1632
1987
|
engineThrew = true;
|
|
@@ -1679,7 +2034,23 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
|
|
|
1679
2034
|
synthesized_verdict: synthForEvent,
|
|
1680
2035
|
engine_version: engineResult.engine_version,
|
|
1681
2036
|
degraded: engineResult.degraded,
|
|
1682
|
-
gate_type: gateType
|
|
2037
|
+
gate_type: gateType,
|
|
2038
|
+
// ─── Consensus-engine feature outputs (additive; 2026-06-13) ─────────────
|
|
2039
|
+
// Feature 2 (calibrated-confidence) — ON by default; the engine attaches
|
|
2040
|
+
// calibrated_confidence + confidence_badge to the synthesized verdict on the
|
|
2041
|
+
// plain runConsensus path. Surface them at the top level so callers don't
|
|
2042
|
+
// have to know the engine's internal SynthesizedVerdict shape.
|
|
2043
|
+
...engineResult.synthesized_verdict.calibrated_confidence !== void 0 ? { calibrated_confidence: engineResult.synthesized_verdict.calibrated_confidence } : {},
|
|
2044
|
+
...engineResult.synthesized_verdict.confidence_badge !== void 0 ? { confidence_badge: engineResult.synthesized_verdict.confidence_badge } : {},
|
|
2045
|
+
// Feature 1 (agreement-gate) — fan-out diagnostics (present iff the gate ran).
|
|
2046
|
+
...engineResult.fan_out_diagnostics !== void 0 ? { fan_out_diagnostics: engineResult.fan_out_diagnostics } : {},
|
|
2047
|
+
// Source-grounded Tier-4 outputs (present iff the call was source-grounded).
|
|
2048
|
+
...engineResult.source_grounded === true ? { source_grounded: true } : {},
|
|
2049
|
+
...engineResult.citation_grade !== void 0 ? { citation_grade: engineResult.citation_grade } : {},
|
|
2050
|
+
...engineResult.low_confidence_sources !== void 0 ? { low_confidence_sources: engineResult.low_confidence_sources } : {},
|
|
2051
|
+
// Escalation (from citation grade or human-tiebreak synthesizer).
|
|
2052
|
+
...engineResult.escalation_required !== void 0 ? { escalation_required: engineResult.escalation_required } : {},
|
|
2053
|
+
...engineResult.escalation_reason !== void 0 ? { escalation_reason: engineResult.escalation_reason } : {}
|
|
1683
2054
|
};
|
|
1684
2055
|
const envelope = {
|
|
1685
2056
|
tool: TOOL_NAME4,
|
|
@@ -1771,8 +2142,8 @@ async function handleArchitectureReview(deps, rawInput, signal) {
|
|
|
1771
2142
|
const key = deps.cache.keyFor(TOOL_NAME5, cacheInput);
|
|
1772
2143
|
const excerpt = rawInput.diff.slice(0, 300);
|
|
1773
2144
|
const inputSizeBytes = bytesOf(rawInput.diff) + (rawInput.summary !== void 0 ? bytesOf(rawInput.summary) : 0);
|
|
1774
|
-
const
|
|
1775
|
-
if (
|
|
2145
|
+
const cached2 = deps.cache.get(key);
|
|
2146
|
+
if (cached2 !== null) {
|
|
1776
2147
|
const replayEvent = {
|
|
1777
2148
|
...buildBaseEvent({
|
|
1778
2149
|
tool: TOOL_NAME5,
|
|
@@ -1783,14 +2154,14 @@ async function handleArchitectureReview(deps, rawInput, signal) {
|
|
|
1783
2154
|
session: deps.session,
|
|
1784
2155
|
now: deps.now()
|
|
1785
2156
|
}),
|
|
1786
|
-
per_model_verdicts:
|
|
1787
|
-
synthesized_verdict:
|
|
1788
|
-
consensus_confidence:
|
|
1789
|
-
consensus_engine_version:
|
|
2157
|
+
per_model_verdicts: cached2.value.payload.per_model_verdicts,
|
|
2158
|
+
synthesized_verdict: cached2.value.payload.synthesized_verdict ?? null,
|
|
2159
|
+
consensus_confidence: cached2.value.payload.synthesized_verdict?.confidence ?? null,
|
|
2160
|
+
consensus_engine_version: cached2.value.payload.engine_version ?? null,
|
|
1790
2161
|
cache_hit: true
|
|
1791
2162
|
};
|
|
1792
2163
|
deps.events.append(replayEvent);
|
|
1793
|
-
return jsonContent({ ...
|
|
2164
|
+
return jsonContent({ ...cached2.value, cache: { hit: true, key } });
|
|
1794
2165
|
}
|
|
1795
2166
|
const kbResult = findRulesForDiff(rawInput.diff);
|
|
1796
2167
|
const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);
|
|
@@ -1902,8 +2273,11 @@ var ratchetIdSchema = z3.enum([
|
|
|
1902
2273
|
]);
|
|
1903
2274
|
var thresholdSchema = z3.enum(["strict", "medium", "permissive"]);
|
|
1904
2275
|
var userConfigSchema = z3.object({
|
|
1905
|
-
|
|
1906
|
-
|
|
2276
|
+
// zod v4: z.record with ENUM keys validates exhaustively (every key
|
|
2277
|
+
// required); these are user OVERRIDE maps merged over DEFAULT_CONFIG,
|
|
2278
|
+
// so partial-by-design — z.partialRecord restores the v3 semantics.
|
|
2279
|
+
enabled: z3.partialRecord(ratchetIdSchema, z3.boolean()).optional(),
|
|
2280
|
+
thresholds: z3.partialRecord(ratchetIdSchema, thresholdSchema).optional(),
|
|
1907
2281
|
allowlist: z3.object({
|
|
1908
2282
|
paths: z3.array(z3.string()).optional(),
|
|
1909
2283
|
rules: z3.array(z3.string()).optional()
|
|
@@ -3205,8 +3579,8 @@ async function handleDecomposeDispatch(deps, rawInput, signal) {
|
|
|
3205
3579
|
const key = deps.cache.keyFor(TOOL_NAME7, cacheInput);
|
|
3206
3580
|
const excerpt = rawInput.customer_goal.slice(0, 300);
|
|
3207
3581
|
const inputSizeBytes = bytesOf(rawInput.customer_goal) + bytesOf(contextStrForSize);
|
|
3208
|
-
const
|
|
3209
|
-
if (
|
|
3582
|
+
const cached2 = deps.cache.get(key);
|
|
3583
|
+
if (cached2 !== null) {
|
|
3210
3584
|
const replayEvent = {
|
|
3211
3585
|
...buildBaseEvent({
|
|
3212
3586
|
tool: TOOL_NAME7,
|
|
@@ -3217,7 +3591,7 @@ async function handleDecomposeDispatch(deps, rawInput, signal) {
|
|
|
3217
3591
|
session: deps.session,
|
|
3218
3592
|
now: deps.now()
|
|
3219
3593
|
}),
|
|
3220
|
-
per_model_verdicts:
|
|
3594
|
+
per_model_verdicts: cached2.value.payload.per_model_plans.map((p) => ({
|
|
3221
3595
|
model: p.model_id,
|
|
3222
3596
|
model_id: p.model_id,
|
|
3223
3597
|
provider: "cache-replay",
|
|
@@ -3230,12 +3604,12 @@ async function handleDecomposeDispatch(deps, rawInput, signal) {
|
|
|
3230
3604
|
cited_sources: [],
|
|
3231
3605
|
error: null
|
|
3232
3606
|
})),
|
|
3233
|
-
consensus_engine_version:
|
|
3607
|
+
consensus_engine_version: cached2.value.payload.engine_version ?? null,
|
|
3234
3608
|
cache_hit: true
|
|
3235
3609
|
};
|
|
3236
3610
|
deps.events.append(replayEvent);
|
|
3237
3611
|
const replayEnvelope = {
|
|
3238
|
-
...
|
|
3612
|
+
...cached2.value,
|
|
3239
3613
|
cache: { hit: true, key }
|
|
3240
3614
|
};
|
|
3241
3615
|
return jsonContent(replayEnvelope);
|
|
@@ -3343,22 +3717,9 @@ Produce the JSON dispatch plan now.`;
|
|
|
3343
3717
|
return jsonContent(envelope);
|
|
3344
3718
|
}
|
|
3345
3719
|
|
|
3346
|
-
// src/cloud/credential-store.ts
|
|
3347
|
-
import { homedir as homedir3 } from "node:os";
|
|
3348
|
-
import { join as join5, dirname as dirname4 } from "node:path";
|
|
3349
|
-
import {
|
|
3350
|
-
existsSync as existsSync3,
|
|
3351
|
-
mkdirSync as mkdirSync2,
|
|
3352
|
-
readFileSync as readFileSync5,
|
|
3353
|
-
writeFileSync as writeFileSync2,
|
|
3354
|
-
chmodSync as chmodSync2,
|
|
3355
|
-
rmSync
|
|
3356
|
-
} from "node:fs";
|
|
3357
|
-
|
|
3358
|
-
// src/cloud/keychain.ts
|
|
3359
|
-
import { createRequire } from "node:module";
|
|
3360
|
-
|
|
3361
3720
|
// src/cloud/admin-callable-client.ts
|
|
3721
|
+
init_auth_token_source();
|
|
3722
|
+
init_credential_store();
|
|
3362
3723
|
var AdminCallableError = class extends Error {
|
|
3363
3724
|
constructor(status, path3, message) {
|
|
3364
3725
|
super(message);
|
|
@@ -4178,7 +4539,7 @@ var inputSchema19 = {
|
|
|
4178
4539
|
additionalProperties: false
|
|
4179
4540
|
};
|
|
4180
4541
|
var description19 = "Reports per-session context-window utilization to VO and returns a directive: 'continue' (under 70%), 'prepare_handoff' (70-84%), or 'execute_handoff_now' (\u226585%). Implements V1 launch gate #9 (fleet context lifecycle management) per the official VO roadmap. V1 backend is stub-local \u2014 computes the directive purely from `context_used_pct` against the documented thresholds without a network call. Phase 3 wires this to the deployed vo-control-plane HTTP API; the response shape stays stable across the cutover (`backend_mode` field in the payload tells the caller which mode produced the verdict).";
|
|
4181
|
-
function
|
|
4542
|
+
function isStringArray2(v, maxItems) {
|
|
4182
4543
|
if (!Array.isArray(v)) return false;
|
|
4183
4544
|
if (v.length > maxItems) return false;
|
|
4184
4545
|
return v.every((item) => typeof item === "string");
|
|
@@ -4194,14 +4555,64 @@ function isToolInput19(v) {
|
|
|
4194
4555
|
if (!Number.isFinite(o["context_used_pct"])) return false;
|
|
4195
4556
|
if (o["context_used_pct"] < 0 || o["context_used_pct"] > 100) return false;
|
|
4196
4557
|
if (o["current_goal"] !== void 0 && typeof o["current_goal"] !== "string") return false;
|
|
4197
|
-
if (o["recent_files_touched"] !== void 0 && !
|
|
4558
|
+
if (o["recent_files_touched"] !== void 0 && !isStringArray2(o["recent_files_touched"], MAX_RECENT_FILES)) {
|
|
4198
4559
|
return false;
|
|
4199
4560
|
}
|
|
4200
|
-
if (o["recent_tool_uses"] !== void 0 && !
|
|
4561
|
+
if (o["recent_tool_uses"] !== void 0 && !isStringArray2(o["recent_tool_uses"], MAX_RECENT_TOOLS)) {
|
|
4201
4562
|
return false;
|
|
4202
4563
|
}
|
|
4203
4564
|
return true;
|
|
4204
4565
|
}
|
|
4566
|
+
function getCloudConfig() {
|
|
4567
|
+
const url = process.env["VO_CONTROL_PLANE_URL"];
|
|
4568
|
+
const token = process.env["VO_CONTROL_PLANE_ADMIN_TOKEN"];
|
|
4569
|
+
if (!url || !token) return null;
|
|
4570
|
+
return { url, token };
|
|
4571
|
+
}
|
|
4572
|
+
async function tryCloudReportState(cloud, input) {
|
|
4573
|
+
try {
|
|
4574
|
+
const body = {
|
|
4575
|
+
context_used_pct: input.context_used_pct
|
|
4576
|
+
};
|
|
4577
|
+
if (input.current_goal !== void 0) body["current_goal"] = input.current_goal;
|
|
4578
|
+
if (input.recent_files_touched !== void 0) {
|
|
4579
|
+
body["recent_files_touched"] = input.recent_files_touched;
|
|
4580
|
+
}
|
|
4581
|
+
if (input.recent_tool_uses !== void 0) {
|
|
4582
|
+
body["recent_tool_uses"] = input.recent_tool_uses;
|
|
4583
|
+
}
|
|
4584
|
+
const url = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;
|
|
4585
|
+
const response = await fetch(url, {
|
|
4586
|
+
method: "POST",
|
|
4587
|
+
headers: {
|
|
4588
|
+
"Content-Type": "application/json",
|
|
4589
|
+
"Authorization": `Bearer ${cloud.token}`
|
|
4590
|
+
},
|
|
4591
|
+
body: JSON.stringify(body)
|
|
4592
|
+
});
|
|
4593
|
+
if (!response.ok) {
|
|
4594
|
+
return null;
|
|
4595
|
+
}
|
|
4596
|
+
const data = await response.json();
|
|
4597
|
+
if (!data.ok || !data.session || !data.directive) {
|
|
4598
|
+
return null;
|
|
4599
|
+
}
|
|
4600
|
+
return {
|
|
4601
|
+
directive: data.directive.action,
|
|
4602
|
+
message: data.directive.message,
|
|
4603
|
+
session_id: data.session.session_id,
|
|
4604
|
+
operator_id: data.session.operator_id,
|
|
4605
|
+
agent_type: input.agent_type,
|
|
4606
|
+
context_used_pct: data.session.context_used_pct,
|
|
4607
|
+
backend_mode: "cloud-control-plane",
|
|
4608
|
+
ts: data.session.last_seen_at,
|
|
4609
|
+
schema_version: 1,
|
|
4610
|
+
...data.directive.action === "execute_handoff_now" ? { handoff_path: suggestedHandoffPath(data.session.session_id, data.session.last_seen_at) } : {}
|
|
4611
|
+
};
|
|
4612
|
+
} catch {
|
|
4613
|
+
return null;
|
|
4614
|
+
}
|
|
4615
|
+
}
|
|
4205
4616
|
async function handleReportSessionState(deps, rawInput, _signal) {
|
|
4206
4617
|
if (!isToolInput19(rawInput)) {
|
|
4207
4618
|
throw invalidParams(
|
|
@@ -4209,6 +4620,13 @@ async function handleReportSessionState(deps, rawInput, _signal) {
|
|
|
4209
4620
|
`invalid input. Required fields: operator_id (non-empty string), session_id (non-empty string), agent_type (one of: ${VALID_AGENT_TYPES.join(" | ")}), context_used_pct (number 0-100). Optional: current_goal (string \u2264${MAX_GOAL_CHARS} chars), recent_files_touched (string[] \u2264${MAX_RECENT_FILES}), recent_tool_uses (string[] \u2264${MAX_RECENT_TOOLS}).`
|
|
4210
4621
|
);
|
|
4211
4622
|
}
|
|
4623
|
+
const cloud = getCloudConfig();
|
|
4624
|
+
if (cloud !== null) {
|
|
4625
|
+
const cloudPayload = await tryCloudReportState(cloud, rawInput);
|
|
4626
|
+
if (cloudPayload !== null) {
|
|
4627
|
+
return jsonContent(cloudPayload);
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4212
4630
|
const directive = computeDirective(rawInput.context_used_pct);
|
|
4213
4631
|
const ts = deps.now().toISOString();
|
|
4214
4632
|
const payload = {
|
|
@@ -4226,6 +4644,141 @@ async function handleReportSessionState(deps, rawInput, _signal) {
|
|
|
4226
4644
|
return jsonContent(payload);
|
|
4227
4645
|
}
|
|
4228
4646
|
|
|
4647
|
+
// src/tools/session/spawn-successor.ts
|
|
4648
|
+
import { spawn } from "node:child_process";
|
|
4649
|
+
import { homedir as homedir4 } from "node:os";
|
|
4650
|
+
import { join as join6 } from "node:path";
|
|
4651
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync6, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
4652
|
+
var TOOL_NAME20 = "vo_spawn_successor";
|
|
4653
|
+
var MAX_HANDOFF_BYTES = 64e3;
|
|
4654
|
+
var inputSchema20 = {
|
|
4655
|
+
type: "object",
|
|
4656
|
+
properties: {
|
|
4657
|
+
handoff_path: {
|
|
4658
|
+
type: "string",
|
|
4659
|
+
description: "Path to the handoff doc to pre-inject. Default: the newest .md in ~/.vo/handoffs/."
|
|
4660
|
+
},
|
|
4661
|
+
goal: {
|
|
4662
|
+
type: "string",
|
|
4663
|
+
description: "Optional one-line goal override appended after the handoff."
|
|
4664
|
+
},
|
|
4665
|
+
cwd: {
|
|
4666
|
+
type: "string",
|
|
4667
|
+
description: "Working directory for the successor (default: the repo the handoff names, else process cwd)."
|
|
4668
|
+
},
|
|
4669
|
+
max_turns: {
|
|
4670
|
+
type: "number",
|
|
4671
|
+
description: "Optional --max-turns bound for the successor."
|
|
4672
|
+
}
|
|
4673
|
+
},
|
|
4674
|
+
required: [],
|
|
4675
|
+
additionalProperties: false
|
|
4676
|
+
};
|
|
4677
|
+
var description20 = "Mode B auto-handoff (roadmap \xA73.4): spawn a DETACHED headless `claude -p` successor with a handoff doc pre-injected into its prompt. Defaults to the newest handoff in ~/.vo/handoffs/. Returns {spawned, pid, log_path, handoff_path}. The successor works under the same gates as any session (ADR-001: verify-before-act, human merge approval) \u2014 this tool never fires autonomously.";
|
|
4678
|
+
function isToolInput20(v) {
|
|
4679
|
+
if (typeof v !== "object" || v === null) return false;
|
|
4680
|
+
const o = v;
|
|
4681
|
+
if (o["handoff_path"] !== void 0 && typeof o["handoff_path"] !== "string") return false;
|
|
4682
|
+
if (o["goal"] !== void 0 && typeof o["goal"] !== "string") return false;
|
|
4683
|
+
if (o["cwd"] !== void 0 && typeof o["cwd"] !== "string") return false;
|
|
4684
|
+
if (o["max_turns"] !== void 0 && typeof o["max_turns"] !== "number") return false;
|
|
4685
|
+
return true;
|
|
4686
|
+
}
|
|
4687
|
+
function newestHandoff(dir = join6(homedir4(), ".vo", "handoffs")) {
|
|
4688
|
+
try {
|
|
4689
|
+
const entries = readdirSync3(dir).filter((f) => f.endsWith(".md")).map((f) => ({ f, m: statSync3(join6(dir, f)).mtimeMs })).sort((a, b) => b.m - a.m);
|
|
4690
|
+
return entries.length > 0 && entries[0] ? join6(dir, entries[0].f) : null;
|
|
4691
|
+
} catch {
|
|
4692
|
+
return null;
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
var MANDATORY_READS = [
|
|
4696
|
+
"CLAUDE.md + AGENTS.md + README.md (repo root)",
|
|
4697
|
+
"docs/current/virtual-office-agent-charter.md, -operating-model.md, -test-architect.md",
|
|
4698
|
+
"docs/current/evidence-grounded-consensus-testing.md",
|
|
4699
|
+
"docs/vo/ADR-001-* (verify + sign, human approves merge; no autonomous bot-merge / headless triggers) + docs/vo/vo-adr-002-two-plane-moat.md",
|
|
4700
|
+
"docs/vo/vo-roadmap-2026-05-26.md (read the Change log tail for current state)",
|
|
4701
|
+
"the operator memory index ~/.claude/projects/C--Users-greyl/memory/MEMORY.md"
|
|
4702
|
+
];
|
|
4703
|
+
function buildSuccessorPrompt(handoffMarkdown, goal) {
|
|
4704
|
+
const reads = MANDATORY_READS.map((r, i) => ` ${i + 1}. ${r}`).join("\n");
|
|
4705
|
+
const lines = [
|
|
4706
|
+
"You are the SUCCESSOR agent for a Virtual Office lane. The previous session",
|
|
4707
|
+
"exhausted its context and wrote the handoff below. Read it fully, verify its",
|
|
4708
|
+
'"verification needed" items against live state (a handoff is a claim, not',
|
|
4709
|
+
"evidence \u2014 verify via `git show origin/main:<path>`), then continue the lane.",
|
|
4710
|
+
"",
|
|
4711
|
+
"MANDATORY READS before writing any code (NOT all auto-loaded \u2014 open them):",
|
|
4712
|
+
reads,
|
|
4713
|
+
"",
|
|
4714
|
+
"NON-NEGOTIABLES: multi-model consensus verification is the core; test honesty",
|
|
4715
|
+
"(verified-answer-only, no fake green); verify-before-act + human merge approval;",
|
|
4716
|
+
"never a full functions-shared deploy; Gen2 only; work in a worktree on your own",
|
|
4717
|
+
"branch; finish line is MERGED + DEPLOYED + LIVE-VERIFIED, and VO changes update",
|
|
4718
|
+
"the roadmap in the same PR.",
|
|
4719
|
+
"",
|
|
4720
|
+
"--- HANDOFF ---",
|
|
4721
|
+
handoffMarkdown,
|
|
4722
|
+
"--- END HANDOFF ---"
|
|
4723
|
+
];
|
|
4724
|
+
if (goal && goal.trim().length > 0) lines.push("", `OPERATOR GOAL OVERRIDE: ${goal.trim()}`);
|
|
4725
|
+
return lines.join("\n");
|
|
4726
|
+
}
|
|
4727
|
+
function buildSuccessorArgs(maxTurns) {
|
|
4728
|
+
const args = ["-p", "--permission-mode", "acceptEdits"];
|
|
4729
|
+
if (Number.isInteger(maxTurns) && maxTurns > 0) {
|
|
4730
|
+
args.push("--max-turns", String(maxTurns));
|
|
4731
|
+
}
|
|
4732
|
+
return args;
|
|
4733
|
+
}
|
|
4734
|
+
async function handleSpawnSuccessor(_deps, rawInput, _signal, spawnImpl = spawn) {
|
|
4735
|
+
if (!isToolInput20(rawInput)) {
|
|
4736
|
+
throw invalidParams(TOOL_NAME20, "invalid input. Optional: { handoff_path, goal, cwd, max_turns }.");
|
|
4737
|
+
}
|
|
4738
|
+
const handoffPath = rawInput.handoff_path?.trim() || newestHandoff();
|
|
4739
|
+
if (!handoffPath || !existsSync4(handoffPath)) {
|
|
4740
|
+
return jsonContent({
|
|
4741
|
+
tool: TOOL_NAME20,
|
|
4742
|
+
schema_version: 1,
|
|
4743
|
+
payload: {
|
|
4744
|
+
spawned: false,
|
|
4745
|
+
reason: rawInput.handoff_path ? `handoff not found: ${rawInput.handoff_path}` : "no handoff docs in ~/.vo/handoffs \u2014 write one first (the 85% directive does this)"
|
|
4746
|
+
}
|
|
4747
|
+
});
|
|
4748
|
+
}
|
|
4749
|
+
const handoff = readFileSync6(handoffPath, "utf8").slice(0, MAX_HANDOFF_BYTES);
|
|
4750
|
+
const prompt = buildSuccessorPrompt(handoff, rawInput.goal);
|
|
4751
|
+
const logDir = process.env["VO_MCP_SUCCESSOR_LOG_DIR"]?.trim() || join6(homedir4(), ".vo", "successors");
|
|
4752
|
+
mkdirSync3(logDir, { recursive: true });
|
|
4753
|
+
const logPath = join6(logDir, `successor-${Date.now()}.log`);
|
|
4754
|
+
const logFd = openSync(logPath, "a");
|
|
4755
|
+
const child = spawnImpl("claude", buildSuccessorArgs(rawInput.max_turns), {
|
|
4756
|
+
cwd: rawInput.cwd?.trim() || process.cwd(),
|
|
4757
|
+
detached: true,
|
|
4758
|
+
stdio: ["pipe", logFd, logFd],
|
|
4759
|
+
// Windows: `claude` is a .cmd shim — needs a shell to resolve. The prompt
|
|
4760
|
+
// goes via STDIN below, never argv, so the shell never sees it.
|
|
4761
|
+
shell: process.platform === "win32",
|
|
4762
|
+
windowsHide: true
|
|
4763
|
+
});
|
|
4764
|
+
let spawnError = null;
|
|
4765
|
+
child.on("error", (e) => {
|
|
4766
|
+
spawnError = e.message;
|
|
4767
|
+
});
|
|
4768
|
+
try {
|
|
4769
|
+
child.stdin.write(prompt);
|
|
4770
|
+
child.stdin.end();
|
|
4771
|
+
} catch {
|
|
4772
|
+
}
|
|
4773
|
+
child.unref();
|
|
4774
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
4775
|
+
return jsonContent({
|
|
4776
|
+
tool: TOOL_NAME20,
|
|
4777
|
+
schema_version: 1,
|
|
4778
|
+
payload: spawnError ? { spawned: false, reason: `spawn failed: ${spawnError}`, handoff_path: handoffPath } : { spawned: true, pid: child.pid ?? null, log_path: logPath, handoff_path: handoffPath }
|
|
4779
|
+
});
|
|
4780
|
+
}
|
|
4781
|
+
|
|
4229
4782
|
// src/tools/concierge/common-concierge.ts
|
|
4230
4783
|
var CONCIERGE_STUB_REASON = "cloud mode not active in this MCP runtime \u2014 set VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN to enable. The server-side /api/v1/admin/concierge/dispatch endpoint IS built + deployed (vo-control-plane #5724/#5734); in cloud mode this tool returns the routed pack README + file index.";
|
|
4231
4784
|
var CONCIERGE_GATE_TYPE = "concierge-dispatch";
|
|
@@ -4244,10 +4797,10 @@ function isKnownConciergePack(value) {
|
|
|
4244
4797
|
}
|
|
4245
4798
|
|
|
4246
4799
|
// src/tools/concierge/dispatch.ts
|
|
4247
|
-
var
|
|
4800
|
+
var TOOL_NAME21 = "vo_concierge_dispatch";
|
|
4248
4801
|
var CALLABLE_NAME10 = "voConciergeDispatch";
|
|
4249
4802
|
var ADMIN_PATH10 = "/api/v1/admin/concierge/dispatch";
|
|
4250
|
-
var
|
|
4803
|
+
var inputSchema21 = {
|
|
4251
4804
|
type: "object",
|
|
4252
4805
|
properties: {
|
|
4253
4806
|
pack: {
|
|
@@ -4262,8 +4815,8 @@ var inputSchema20 = {
|
|
|
4262
4815
|
},
|
|
4263
4816
|
additionalProperties: false
|
|
4264
4817
|
};
|
|
4265
|
-
var
|
|
4266
|
-
function
|
|
4818
|
+
var description21 = "Dispatches a provider-scoped knowledge pack (gcp | firebase | aws | cloudflare | vercel | netlify | tax | hybrid). Cross-vendor MCP equivalent of the /vo-concierge Claude-Code slash command. Route explicitly via `pack`, or via tenant.cloud_provider by passing `tenant_id`. Returns the pack's README (`readme_markdown`) + file index. In cloud mode, dispatches via vo-control-plane and returns `verdict: 'pass'` with the pack/directory data; without cloud config, returns `verdict: 'unimplemented'`.";
|
|
4819
|
+
function isToolInput21(v) {
|
|
4267
4820
|
if (typeof v !== "object" || v === null) return false;
|
|
4268
4821
|
const obj = v;
|
|
4269
4822
|
if (obj.pack !== void 0 && typeof obj.pack !== "string") return false;
|
|
@@ -4271,15 +4824,15 @@ function isToolInput20(v) {
|
|
|
4271
4824
|
return true;
|
|
4272
4825
|
}
|
|
4273
4826
|
async function handleConciergeDispatch(deps, rawInput, _signal) {
|
|
4274
|
-
if (!
|
|
4827
|
+
if (!isToolInput21(rawInput)) {
|
|
4275
4828
|
throw invalidParams(
|
|
4276
|
-
|
|
4829
|
+
TOOL_NAME21,
|
|
4277
4830
|
"invalid input. Expected { pack?: string, tenant_id?: string }."
|
|
4278
4831
|
);
|
|
4279
4832
|
}
|
|
4280
4833
|
if (rawInput.pack !== void 0 && rawInput.pack !== "" && !isKnownConciergePack(rawInput.pack)) {
|
|
4281
4834
|
throw invalidParams(
|
|
4282
|
-
|
|
4835
|
+
TOOL_NAME21,
|
|
4283
4836
|
`unknown pack: ${JSON.stringify(rawInput.pack)}. Known packs: ${KNOWN_CONCIERGE_PACKS.join(", ")}.`
|
|
4284
4837
|
);
|
|
4285
4838
|
}
|
|
@@ -4290,7 +4843,7 @@ async function handleConciergeDispatch(deps, rawInput, _signal) {
|
|
|
4290
4843
|
if (rawInput.pack) cloudBody.pack = rawInput.pack;
|
|
4291
4844
|
if (rawInput.tenant_id) cloudBody.tenantId = rawInput.tenant_id;
|
|
4292
4845
|
return buildCloudOrStubResponse({
|
|
4293
|
-
toolName:
|
|
4846
|
+
toolName: TOOL_NAME21,
|
|
4294
4847
|
callableName: CALLABLE_NAME10,
|
|
4295
4848
|
adminPath: ADMIN_PATH10,
|
|
4296
4849
|
normalizedInput,
|
|
@@ -4307,6 +4860,254 @@ async function handleConciergeDispatch(deps, rawInput, _signal) {
|
|
|
4307
4860
|
});
|
|
4308
4861
|
}
|
|
4309
4862
|
|
|
4863
|
+
// src/tools/memory/sync-config.ts
|
|
4864
|
+
import { homedir as homedir5 } from "node:os";
|
|
4865
|
+
import { join as join7 } from "node:path";
|
|
4866
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, readdirSync as readdirSync4 } from "node:fs";
|
|
4867
|
+
var TOOL_NAME22 = "vo_sync_config";
|
|
4868
|
+
var inputSchema22 = {
|
|
4869
|
+
type: "object",
|
|
4870
|
+
properties: {
|
|
4871
|
+
action: {
|
|
4872
|
+
type: "string",
|
|
4873
|
+
enum: ["pull", "push"],
|
|
4874
|
+
description: "pull: download cloud memory to local files. push: upload local files to cloud."
|
|
4875
|
+
},
|
|
4876
|
+
cwd: {
|
|
4877
|
+
type: "string",
|
|
4878
|
+
description: "Working directory to derive project slug from (default: process.cwd())."
|
|
4879
|
+
}
|
|
4880
|
+
},
|
|
4881
|
+
required: ["action"],
|
|
4882
|
+
additionalProperties: false
|
|
4883
|
+
};
|
|
4884
|
+
var description22 = "Syncs memory entries between local ~/.claude/projects/<slug>/memory/ and cloud control-plane /api/v1/agent-config/memory/me. Requires operator auth (vo-mcp login). Actions: pull (cloud\u2192local), push (local\u2192cloud). Idempotent; push creates/updates as needed.";
|
|
4885
|
+
function isToolInput22(v) {
|
|
4886
|
+
if (typeof v !== "object" || v === null) return false;
|
|
4887
|
+
const o = v;
|
|
4888
|
+
if (o["action"] !== "pull" && o["action"] !== "push") return false;
|
|
4889
|
+
if (o["cwd"] !== void 0 && typeof o["cwd"] !== "string") return false;
|
|
4890
|
+
return true;
|
|
4891
|
+
}
|
|
4892
|
+
function deriveProjectSlug(cwd) {
|
|
4893
|
+
const normalized = cwd.replace(/\\/g, "/");
|
|
4894
|
+
return normalized.replace(/^([A-Z]):/i, (_, drive) => `${drive.toUpperCase()}-`).replace(/\/$/g, "").split("/").join("--").replace(/\s+/g, "-");
|
|
4895
|
+
}
|
|
4896
|
+
function getMemoryDir(cwd) {
|
|
4897
|
+
const slug = deriveProjectSlug(cwd);
|
|
4898
|
+
return join7(homedir5(), ".claude", "projects", slug, "memory");
|
|
4899
|
+
}
|
|
4900
|
+
async function pullMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
|
|
4901
|
+
const url = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
4902
|
+
const response = await fetchFn(url, {
|
|
4903
|
+
method: "GET",
|
|
4904
|
+
headers: {
|
|
4905
|
+
authorization: `Bearer ${token}`
|
|
4906
|
+
}
|
|
4907
|
+
});
|
|
4908
|
+
if (response.status !== 200) {
|
|
4909
|
+
const text = await response.text();
|
|
4910
|
+
throw new Error(`GET /api/v1/agent-config/memory/me returned HTTP ${response.status}: ${text.slice(0, 200)}`);
|
|
4911
|
+
}
|
|
4912
|
+
const data = JSON.parse(await response.text());
|
|
4913
|
+
if (!data.ok || !Array.isArray(data.entries)) {
|
|
4914
|
+
throw new Error("GET /api/v1/agent-config/memory/me response missing ok=true or entries array");
|
|
4915
|
+
}
|
|
4916
|
+
mkdirSync4(memoryDir, { recursive: true });
|
|
4917
|
+
const files = [];
|
|
4918
|
+
for (const entry of data.entries) {
|
|
4919
|
+
const filePath = join7(memoryDir, entry.file_name);
|
|
4920
|
+
writeFileSync3(filePath, entry.content, "utf8");
|
|
4921
|
+
files.push(entry.file_name);
|
|
4922
|
+
}
|
|
4923
|
+
return { pulled: data.entries.length, files };
|
|
4924
|
+
}
|
|
4925
|
+
async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
|
|
4926
|
+
if (!existsSync5(memoryDir)) {
|
|
4927
|
+
return { pushed: 0, created: 0, updated: 0 };
|
|
4928
|
+
}
|
|
4929
|
+
const localFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
|
|
4930
|
+
file_name: f,
|
|
4931
|
+
content: readFileSync7(join7(memoryDir, f), "utf8"),
|
|
4932
|
+
entry_type: f === "MEMORY.md" ? "index" : "topic"
|
|
4933
|
+
}));
|
|
4934
|
+
if (localFiles.length === 0) {
|
|
4935
|
+
return { pushed: 0, created: 0, updated: 0 };
|
|
4936
|
+
}
|
|
4937
|
+
const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
4938
|
+
const getResponse = await fetchFn(getUrl, {
|
|
4939
|
+
method: "GET",
|
|
4940
|
+
headers: {
|
|
4941
|
+
authorization: `Bearer ${token}`
|
|
4942
|
+
}
|
|
4943
|
+
});
|
|
4944
|
+
const existingMap = /* @__PURE__ */ new Map();
|
|
4945
|
+
if (getResponse.status === 200) {
|
|
4946
|
+
const getData = JSON.parse(await getResponse.text());
|
|
4947
|
+
if (getData.ok && Array.isArray(getData.entries)) {
|
|
4948
|
+
for (const entry of getData.entries) {
|
|
4949
|
+
existingMap.set(entry.file_name, entry.memory_id);
|
|
4950
|
+
}
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
let created = 0;
|
|
4954
|
+
let updated = 0;
|
|
4955
|
+
for (const localFile of localFiles) {
|
|
4956
|
+
const memoryId = existingMap.get(localFile.file_name);
|
|
4957
|
+
if (memoryId) {
|
|
4958
|
+
const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${memoryId}`;
|
|
4959
|
+
const updateBody = {
|
|
4960
|
+
content: localFile.content,
|
|
4961
|
+
session_id: sessionId
|
|
4962
|
+
};
|
|
4963
|
+
const updateResponse = await fetchFn(updateUrl, {
|
|
4964
|
+
method: "PUT",
|
|
4965
|
+
headers: {
|
|
4966
|
+
authorization: `Bearer ${token}`,
|
|
4967
|
+
"content-type": "application/json"
|
|
4968
|
+
},
|
|
4969
|
+
body: JSON.stringify(updateBody)
|
|
4970
|
+
});
|
|
4971
|
+
if (updateResponse.status !== 200) {
|
|
4972
|
+
const text = await updateResponse.text();
|
|
4973
|
+
throw new Error(
|
|
4974
|
+
`PUT /api/v1/agent-config/memory/${memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`
|
|
4975
|
+
);
|
|
4976
|
+
}
|
|
4977
|
+
const updateData = JSON.parse(await updateResponse.text());
|
|
4978
|
+
if (!updateData.ok) {
|
|
4979
|
+
throw new Error(`PUT /api/v1/agent-config/memory/${memoryId} returned ok=false`);
|
|
4980
|
+
}
|
|
4981
|
+
updated++;
|
|
4982
|
+
} else {
|
|
4983
|
+
const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
|
|
4984
|
+
const createBody = {
|
|
4985
|
+
entry_type: localFile.entry_type,
|
|
4986
|
+
file_name: localFile.file_name,
|
|
4987
|
+
content: localFile.content,
|
|
4988
|
+
session_id: sessionId
|
|
4989
|
+
};
|
|
4990
|
+
const createResponse = await fetchFn(createUrl, {
|
|
4991
|
+
method: "POST",
|
|
4992
|
+
headers: {
|
|
4993
|
+
authorization: `Bearer ${token}`,
|
|
4994
|
+
"content-type": "application/json"
|
|
4995
|
+
},
|
|
4996
|
+
body: JSON.stringify(createBody)
|
|
4997
|
+
});
|
|
4998
|
+
if (createResponse.status !== 200 && createResponse.status !== 201) {
|
|
4999
|
+
const text = await createResponse.text();
|
|
5000
|
+
throw new Error(
|
|
5001
|
+
`POST /api/v1/agent-config/memory/me returned HTTP ${createResponse.status}: ${text.slice(0, 200)}`
|
|
5002
|
+
);
|
|
5003
|
+
}
|
|
5004
|
+
const createData = JSON.parse(await createResponse.text());
|
|
5005
|
+
if (!createData.ok) {
|
|
5006
|
+
throw new Error("POST /api/v1/agent-config/memory/me returned ok=false");
|
|
5007
|
+
}
|
|
5008
|
+
created++;
|
|
5009
|
+
}
|
|
5010
|
+
}
|
|
5011
|
+
return { pushed: localFiles.length, created, updated };
|
|
5012
|
+
}
|
|
5013
|
+
async function handleSyncConfig(deps, rawInput, _signal, fetchFn = globalThis.fetch) {
|
|
5014
|
+
if (!isToolInput22(rawInput)) {
|
|
5015
|
+
throw invalidParams(
|
|
5016
|
+
TOOL_NAME22,
|
|
5017
|
+
'invalid input. Required: { action: "pull" | "push" }. Optional: { cwd: "<path>" }.'
|
|
5018
|
+
);
|
|
5019
|
+
}
|
|
5020
|
+
const controlPlaneUrl = process.env["VO_CONTROL_PLANE_URL"];
|
|
5021
|
+
if (!controlPlaneUrl) {
|
|
5022
|
+
return jsonContent({
|
|
5023
|
+
tool: TOOL_NAME22,
|
|
5024
|
+
schema_version: 1,
|
|
5025
|
+
payload: {
|
|
5026
|
+
synced: false,
|
|
5027
|
+
reason: "VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled"
|
|
5028
|
+
}
|
|
5029
|
+
});
|
|
5030
|
+
}
|
|
5031
|
+
const { createAuthTokenSourceFromEnv: createAuthTokenSourceFromEnv2 } = await Promise.resolve().then(() => (init_auth_token_source(), auth_token_source_exports));
|
|
5032
|
+
const { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
|
|
5033
|
+
const tokenSource = createAuthTokenSourceFromEnv2(process.env, fetchFn, () => readStoredCredential2(process.env));
|
|
5034
|
+
if (!tokenSource) {
|
|
5035
|
+
return jsonContent({
|
|
5036
|
+
tool: TOOL_NAME22,
|
|
5037
|
+
schema_version: 1,
|
|
5038
|
+
payload: {
|
|
5039
|
+
synced: false,
|
|
5040
|
+
reason: "No auth configured. Run `vo-mcp login` to authenticate as an operator."
|
|
5041
|
+
}
|
|
5042
|
+
});
|
|
5043
|
+
}
|
|
5044
|
+
const token = await tokenSource.getToken();
|
|
5045
|
+
if (!token) {
|
|
5046
|
+
return jsonContent({
|
|
5047
|
+
tool: TOOL_NAME22,
|
|
5048
|
+
schema_version: 1,
|
|
5049
|
+
payload: {
|
|
5050
|
+
synced: false,
|
|
5051
|
+
reason: "Failed to obtain auth token. Run `vo-mcp login` to re-authenticate."
|
|
5052
|
+
}
|
|
5053
|
+
});
|
|
5054
|
+
}
|
|
5055
|
+
const cwd = rawInput.cwd?.trim() || process.cwd();
|
|
5056
|
+
const memoryDir = getMemoryDir(cwd);
|
|
5057
|
+
try {
|
|
5058
|
+
if (rawInput.action === "pull") {
|
|
5059
|
+
const result = await pullMemory(
|
|
5060
|
+
controlPlaneUrl.replace(/\/+$/, ""),
|
|
5061
|
+
token,
|
|
5062
|
+
memoryDir,
|
|
5063
|
+
deps.sessionId,
|
|
5064
|
+
fetchFn
|
|
5065
|
+
);
|
|
5066
|
+
return jsonContent({
|
|
5067
|
+
tool: TOOL_NAME22,
|
|
5068
|
+
schema_version: 1,
|
|
5069
|
+
payload: {
|
|
5070
|
+
synced: true,
|
|
5071
|
+
action: "pull",
|
|
5072
|
+
pulled: result.pulled,
|
|
5073
|
+
files: result.files,
|
|
5074
|
+
memory_dir: memoryDir
|
|
5075
|
+
}
|
|
5076
|
+
});
|
|
5077
|
+
} else {
|
|
5078
|
+
const result = await pushMemory(
|
|
5079
|
+
controlPlaneUrl.replace(/\/+$/, ""),
|
|
5080
|
+
token,
|
|
5081
|
+
memoryDir,
|
|
5082
|
+
deps.sessionId,
|
|
5083
|
+
fetchFn
|
|
5084
|
+
);
|
|
5085
|
+
return jsonContent({
|
|
5086
|
+
tool: TOOL_NAME22,
|
|
5087
|
+
schema_version: 1,
|
|
5088
|
+
payload: {
|
|
5089
|
+
synced: true,
|
|
5090
|
+
action: "push",
|
|
5091
|
+
pushed: result.pushed,
|
|
5092
|
+
created: result.created,
|
|
5093
|
+
updated: result.updated,
|
|
5094
|
+
memory_dir: memoryDir
|
|
5095
|
+
}
|
|
5096
|
+
});
|
|
5097
|
+
}
|
|
5098
|
+
} catch (err) {
|
|
5099
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
5100
|
+
return jsonContent({
|
|
5101
|
+
tool: TOOL_NAME22,
|
|
5102
|
+
schema_version: 1,
|
|
5103
|
+
payload: {
|
|
5104
|
+
synced: false,
|
|
5105
|
+
reason: `Sync failed: ${message}`
|
|
5106
|
+
}
|
|
5107
|
+
});
|
|
5108
|
+
}
|
|
5109
|
+
}
|
|
5110
|
+
|
|
4310
5111
|
// src/server.ts
|
|
4311
5112
|
function buildToolRegistry() {
|
|
4312
5113
|
return {
|
|
@@ -4468,7 +5269,23 @@ function buildToolRegistry() {
|
|
|
4468
5269
|
description: description20,
|
|
4469
5270
|
inputSchema: inputSchema20
|
|
4470
5271
|
},
|
|
5272
|
+
handler: handleSpawnSuccessor
|
|
5273
|
+
},
|
|
5274
|
+
[TOOL_NAME21]: {
|
|
5275
|
+
definition: {
|
|
5276
|
+
name: TOOL_NAME21,
|
|
5277
|
+
description: description21,
|
|
5278
|
+
inputSchema: inputSchema21
|
|
5279
|
+
},
|
|
4471
5280
|
handler: handleConciergeDispatch
|
|
5281
|
+
},
|
|
5282
|
+
[TOOL_NAME22]: {
|
|
5283
|
+
definition: {
|
|
5284
|
+
name: TOOL_NAME22,
|
|
5285
|
+
description: description22,
|
|
5286
|
+
inputSchema: inputSchema22
|
|
5287
|
+
},
|
|
5288
|
+
handler: handleSyncConfig
|
|
4472
5289
|
}
|
|
4473
5290
|
};
|
|
4474
5291
|
}
|
|
@@ -4526,7 +5343,7 @@ function listToolNames() {
|
|
|
4526
5343
|
|
|
4527
5344
|
// src/cache/sqlite-cache.ts
|
|
4528
5345
|
import { createHash as createHash3 } from "node:crypto";
|
|
4529
|
-
import { chmodSync as chmodSync3, mkdirSync as
|
|
5346
|
+
import { chmodSync as chmodSync3, mkdirSync as mkdirSync5 } from "node:fs";
|
|
4530
5347
|
import { dirname as dirname5 } from "node:path";
|
|
4531
5348
|
import { DatabaseSync } from "node:sqlite";
|
|
4532
5349
|
|
|
@@ -4572,7 +5389,7 @@ function normalizeString(s) {
|
|
|
4572
5389
|
function createSqliteCache(options) {
|
|
4573
5390
|
const fileBacked = options.dbPath !== ":memory:";
|
|
4574
5391
|
if (fileBacked) {
|
|
4575
|
-
|
|
5392
|
+
mkdirSync5(dirname5(options.dbPath), { recursive: true, mode: 448 });
|
|
4576
5393
|
}
|
|
4577
5394
|
const versionNamespace = options.cacheVersionNamespace ?? "";
|
|
4578
5395
|
const db = new DatabaseSync(options.dbPath);
|
|
@@ -4757,6 +5574,143 @@ function createNullConsensusEngineClient(reason = NULL_CLIENT_DEFAULT_REASON) {
|
|
|
4757
5574
|
|
|
4758
5575
|
// src/consensus/engine-client.ts
|
|
4759
5576
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
5577
|
+
|
|
5578
|
+
// src/consensus/engine-options.ts
|
|
5579
|
+
var AGREEMENT_GATE_ENV_VAR = "VO_CONSENSUS_AGREEMENT_GATE";
|
|
5580
|
+
function isTruthyFlag(raw) {
|
|
5581
|
+
if (raw === void 0) return false;
|
|
5582
|
+
const v = raw.trim().toLowerCase();
|
|
5583
|
+
return v === "1" || v === "true" || v === "yes" || v === "on";
|
|
5584
|
+
}
|
|
5585
|
+
function resolveAgreementGate(args) {
|
|
5586
|
+
const env = args.env ?? process.env;
|
|
5587
|
+
const enabled = args.configEnabled === true || args.configEnabled === void 0 && isTruthyFlag(env[AGREEMENT_GATE_ENV_VAR]);
|
|
5588
|
+
if (!enabled) return void 0;
|
|
5589
|
+
return {
|
|
5590
|
+
enabled: true,
|
|
5591
|
+
...args.probeSize !== void 0 ? { probe_size: args.probeSize } : {},
|
|
5592
|
+
...args.modalAgreementThreshold !== void 0 ? { modal_agreement_threshold: args.modalAgreementThreshold } : {},
|
|
5593
|
+
...args.minConfidence !== void 0 ? { min_confidence: args.minConfidence } : {}
|
|
5594
|
+
};
|
|
5595
|
+
}
|
|
5596
|
+
function buildSourceGroundedFields(config, defaultClassifier) {
|
|
5597
|
+
if (config === void 0) return {};
|
|
5598
|
+
const out = {};
|
|
5599
|
+
const grader = config.citation_grader;
|
|
5600
|
+
if (grader !== void 0 && grader.enabled === true) {
|
|
5601
|
+
const classifier = grader.classifier ?? defaultClassifier;
|
|
5602
|
+
if (classifier !== void 0) {
|
|
5603
|
+
out.citation_grader = {
|
|
5604
|
+
enabled: true,
|
|
5605
|
+
classifier,
|
|
5606
|
+
...grader.escalate_below !== void 0 ? { escalate_below: grader.escalate_below } : {}
|
|
5607
|
+
};
|
|
5608
|
+
}
|
|
5609
|
+
}
|
|
5610
|
+
if (config.allow_url !== void 0) {
|
|
5611
|
+
out.allow_url = config.allow_url;
|
|
5612
|
+
}
|
|
5613
|
+
return out;
|
|
5614
|
+
}
|
|
5615
|
+
function mapFanOutDiagnostics(fd) {
|
|
5616
|
+
if (fd === void 0) return void 0;
|
|
5617
|
+
return {
|
|
5618
|
+
models_called: fd.models_called,
|
|
5619
|
+
panel_size: fd.panel_size,
|
|
5620
|
+
early_exit: fd.early_exit,
|
|
5621
|
+
refused: fd.refused
|
|
5622
|
+
};
|
|
5623
|
+
}
|
|
5624
|
+
function mapCitationGrade(cg) {
|
|
5625
|
+
if (cg === void 0) return void 0;
|
|
5626
|
+
return {
|
|
5627
|
+
ok: cg.ok,
|
|
5628
|
+
unsupported_count: cg.unsupported_count,
|
|
5629
|
+
uncertain_count: cg.uncertain_count,
|
|
5630
|
+
graded_claims: cg.graded_claims.map((g) => ({
|
|
5631
|
+
claim: g.claim,
|
|
5632
|
+
label: g.label,
|
|
5633
|
+
confidence: g.confidence,
|
|
5634
|
+
cited_ids: [...g.cited_ids]
|
|
5635
|
+
}))
|
|
5636
|
+
};
|
|
5637
|
+
}
|
|
5638
|
+
|
|
5639
|
+
// src/consensus/claim-classifier.ts
|
|
5640
|
+
var CLASSIFIER_SYSTEM_PROMPT = [
|
|
5641
|
+
"You are a citation-grading judge. You are given a CLAIM and the full text of the SOURCES it cites.",
|
|
5642
|
+
"Decide whether the SOURCES directly support the CLAIM. Judge ONLY against the supplied sources \u2014 do not use outside knowledge.",
|
|
5643
|
+
"pass = the sources clearly support the claim; fail = the sources contradict or do not support it; uncertain = the sources are insufficient or ambiguous.",
|
|
5644
|
+
"After a one-sentence justification, conclude with EXACTLY one of these lines:",
|
|
5645
|
+
" VERDICT: pass",
|
|
5646
|
+
" VERDICT: fail",
|
|
5647
|
+
" VERDICT: uncertain",
|
|
5648
|
+
"Then on a new line, exactly:",
|
|
5649
|
+
" CONFIDENCE: <0..1>",
|
|
5650
|
+
"Do not include any other VERDICT or CONFIDENCE lines."
|
|
5651
|
+
].join("\n");
|
|
5652
|
+
function verdictToLabel(verdict) {
|
|
5653
|
+
switch (verdict) {
|
|
5654
|
+
case "pass":
|
|
5655
|
+
return "supported";
|
|
5656
|
+
case "fail":
|
|
5657
|
+
return "unsupported";
|
|
5658
|
+
case "uncertain":
|
|
5659
|
+
return "uncertain";
|
|
5660
|
+
default:
|
|
5661
|
+
return "uncertain";
|
|
5662
|
+
}
|
|
5663
|
+
}
|
|
5664
|
+
function clampConfidence(raw) {
|
|
5665
|
+
if (!Number.isFinite(raw)) return 0;
|
|
5666
|
+
if (raw < 0) return 0;
|
|
5667
|
+
if (raw > 1) return 1;
|
|
5668
|
+
return raw;
|
|
5669
|
+
}
|
|
5670
|
+
function buildClassifyPrompt(claim, sources) {
|
|
5671
|
+
return [
|
|
5672
|
+
"CLAIM:",
|
|
5673
|
+
claim,
|
|
5674
|
+
"",
|
|
5675
|
+
"SOURCES:",
|
|
5676
|
+
sources,
|
|
5677
|
+
"",
|
|
5678
|
+
"Does the cited SOURCES text support the CLAIM? Reply with your verdict (pass = supported, fail = unsupported, uncertain = insufficient)."
|
|
5679
|
+
].join("\n");
|
|
5680
|
+
}
|
|
5681
|
+
function createClaimClassifier(model, options = {}) {
|
|
5682
|
+
return {
|
|
5683
|
+
async classify(args) {
|
|
5684
|
+
try {
|
|
5685
|
+
const result = await model.call({
|
|
5686
|
+
system_prompt: CLASSIFIER_SYSTEM_PROMPT,
|
|
5687
|
+
user_prompt: buildClassifyPrompt(args.claim, args.sources),
|
|
5688
|
+
...options.signal !== void 0 ? { abort_signal: options.signal } : {}
|
|
5689
|
+
});
|
|
5690
|
+
if (result.verdict === "error") {
|
|
5691
|
+
return { label: "uncertain", confidence: 0 };
|
|
5692
|
+
}
|
|
5693
|
+
return {
|
|
5694
|
+
label: verdictToLabel(result.verdict),
|
|
5695
|
+
confidence: clampConfidence(result.confidence)
|
|
5696
|
+
};
|
|
5697
|
+
} catch {
|
|
5698
|
+
return { label: "uncertain", confidence: 0 };
|
|
5699
|
+
}
|
|
5700
|
+
}
|
|
5701
|
+
};
|
|
5702
|
+
}
|
|
5703
|
+
|
|
5704
|
+
// src/consensus/engine-client.ts
|
|
5705
|
+
function raceAbort(signal) {
|
|
5706
|
+
return new Promise((_, reject) => {
|
|
5707
|
+
const onAbort = () => {
|
|
5708
|
+
reject(new Error("__VO_MCP_CANCELLED__"));
|
|
5709
|
+
};
|
|
5710
|
+
if (signal.aborted) onAbort();
|
|
5711
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
5712
|
+
});
|
|
5713
|
+
}
|
|
4760
5714
|
async function loadEngineModule() {
|
|
4761
5715
|
try {
|
|
4762
5716
|
const moduleName = ["@algosuite", "consensus-engine"].join("/");
|
|
@@ -4800,36 +5754,73 @@ function createEngineConsensusClient(options) {
|
|
|
4800
5754
|
...adapter,
|
|
4801
5755
|
call: (args) => adapter.call({ ...args, abort_signal: signal })
|
|
4802
5756
|
}));
|
|
5757
|
+
const agreementGate = resolveAgreementGate({
|
|
5758
|
+
...options.agreement_gate_enabled !== void 0 ? { configEnabled: options.agreement_gate_enabled } : {},
|
|
5759
|
+
...options.env !== void 0 ? { env: options.env } : {}
|
|
5760
|
+
});
|
|
4803
5761
|
const engineOptions = {
|
|
4804
5762
|
panel,
|
|
4805
|
-
...options.per_model_timeout_ms !== void 0 ? { per_model_timeout_ms: options.per_model_timeout_ms } : {}
|
|
5763
|
+
...options.per_model_timeout_ms !== void 0 ? { per_model_timeout_ms: options.per_model_timeout_ms } : {},
|
|
5764
|
+
...agreementGate !== void 0 ? { agreement_gate: agreementGate } : {}
|
|
4806
5765
|
};
|
|
4807
|
-
const
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
5766
|
+
const sources = request.source_urls;
|
|
5767
|
+
const useSourceGrounded = sources !== void 0 && sources.length > 0 && typeof engine.runSourceGroundedConsensus === "function";
|
|
5768
|
+
let response;
|
|
5769
|
+
let sourceExtras;
|
|
5770
|
+
if (useSourceGrounded) {
|
|
5771
|
+
const [firstAdapter] = panel;
|
|
5772
|
+
const classifierOptions = signal === void 0 ? {} : { signal };
|
|
5773
|
+
const defaultClassifier = firstAdapter === void 0 ? void 0 : createClaimClassifier(firstAdapter, classifierOptions);
|
|
5774
|
+
const sgFields = buildSourceGroundedFields(request.source_grounded, defaultClassifier);
|
|
5775
|
+
const sgOptions = {
|
|
5776
|
+
...engineOptions,
|
|
5777
|
+
source_urls: sources,
|
|
5778
|
+
...sgFields.allow_url !== void 0 ? { allow_url: sgFields.allow_url } : {},
|
|
5779
|
+
...sgFields.citation_grader !== void 0 ? { citation_grader: sgFields.citation_grader } : {}
|
|
5780
|
+
};
|
|
5781
|
+
if (typeof engine.runSourceGroundedConsensus !== "function") {
|
|
5782
|
+
return { ok: false, reason: "source-grounded engine missing runSourceGroundedConsensus" };
|
|
5783
|
+
}
|
|
5784
|
+
const sgCall = engine.runSourceGroundedConsensus(engineRequest, sgOptions);
|
|
5785
|
+
const sgResult = signal === void 0 ? await sgCall : await Promise.race([sgCall, raceAbort(signal)]);
|
|
5786
|
+
if (!sgResult.ok) {
|
|
5787
|
+
return { ok: false, reason: sgResult.reason };
|
|
5788
|
+
}
|
|
5789
|
+
response = sgResult.engine.response;
|
|
5790
|
+
sourceExtras = {
|
|
5791
|
+
...sgResult.citation_grade !== void 0 ? { citation_grade: mapCitationGrade(sgResult.citation_grade) } : {},
|
|
5792
|
+
...sgResult.low_confidence_sources !== void 0 ? { low_confidence_sources: sgResult.low_confidence_sources } : {},
|
|
5793
|
+
...sgResult.escalation_required !== void 0 ? { escalation_required: sgResult.escalation_required } : {},
|
|
5794
|
+
...sgResult.escalation_reason !== void 0 ? { escalation_reason: sgResult.escalation_reason } : {}
|
|
4821
5795
|
};
|
|
5796
|
+
} else {
|
|
5797
|
+
const result = signal === void 0 ? await engine.runConsensus(engineRequest, engineOptions) : await Promise.race([engine.runConsensus(engineRequest, engineOptions), raceAbort(signal)]);
|
|
5798
|
+
if (!result.ok || result.response === void 0) {
|
|
5799
|
+
return {
|
|
5800
|
+
ok: false,
|
|
5801
|
+
reason: result.error?.reason ?? "engine-returned-not-ok"
|
|
5802
|
+
};
|
|
5803
|
+
}
|
|
5804
|
+
response = result.response;
|
|
4822
5805
|
}
|
|
4823
5806
|
return {
|
|
4824
5807
|
ok: true,
|
|
4825
|
-
synthesized_verdict:
|
|
4826
|
-
per_model_verdicts:
|
|
4827
|
-
degraded:
|
|
4828
|
-
duration_ms:
|
|
4829
|
-
engine_version:
|
|
4830
|
-
// Phase 2 Lane D-1 — forward escalation signal when present.
|
|
4831
|
-
|
|
4832
|
-
|
|
5808
|
+
synthesized_verdict: response.synthesized_verdict,
|
|
5809
|
+
per_model_verdicts: response.per_model_verdicts,
|
|
5810
|
+
degraded: response.degraded,
|
|
5811
|
+
duration_ms: response.duration_ms,
|
|
5812
|
+
engine_version: response.engine_version,
|
|
5813
|
+
// Phase 2 Lane D-1 — forward escalation signal when present. The
|
|
5814
|
+
// source-grounded layer's own escalation (from the citation grade)
|
|
5815
|
+
// takes precedence when set, else the synthesizer's.
|
|
5816
|
+
...sourceExtras?.escalation_required !== void 0 ? { escalation_required: sourceExtras.escalation_required } : response.escalation_required !== void 0 ? { escalation_required: response.escalation_required } : {},
|
|
5817
|
+
...sourceExtras?.escalation_reason !== void 0 ? { escalation_reason: sourceExtras.escalation_reason } : response.escalation_reason !== void 0 ? { escalation_reason: response.escalation_reason } : {},
|
|
5818
|
+
// Feature 1 (agreement-gate) — fan-out diagnostics (additive telemetry).
|
|
5819
|
+
...mapFanOutDiagnostics(response.fan_out_diagnostics) !== void 0 ? { fan_out_diagnostics: mapFanOutDiagnostics(response.fan_out_diagnostics) } : {},
|
|
5820
|
+
// Source-grounded additive outputs (Tier-4 features).
|
|
5821
|
+
...useSourceGrounded ? { source_grounded: true } : {},
|
|
5822
|
+
...sourceExtras?.citation_grade !== void 0 ? { citation_grade: sourceExtras.citation_grade } : {},
|
|
5823
|
+
...sourceExtras?.low_confidence_sources !== void 0 ? { low_confidence_sources: sourceExtras.low_confidence_sources } : {}
|
|
4833
5824
|
};
|
|
4834
5825
|
} catch (err) {
|
|
4835
5826
|
const message = err instanceof Error ? err.message : String(err);
|