@algosuite/vo-mcp 0.2.0-beta.2 → 0.2.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -15,500 +15,222 @@ var __export = (target, all) => {
15
15
  __defProp(target, name, { get: all[name], enumerable: true });
16
16
  };
17
17
 
18
- // src/cloud/auth-token-source.ts
19
- var auth_token_source_exports = {};
20
- __export(auth_token_source_exports, {
21
- FIREBASE_SECURETOKEN_URL: () => FIREBASE_SECURETOKEN_URL,
22
- FIREBASE_TOKEN_REFERER: () => FIREBASE_TOKEN_REFERER,
23
- createAuthTokenSourceFromEnv: () => createAuthTokenSourceFromEnv,
24
- createFirebaseRefreshTokenSource: () => createFirebaseRefreshTokenSource,
25
- createStaticTokenSource: () => createStaticTokenSource
18
+ // ../vo-arch-defaults/src/schema/rule-v1.ts
19
+ import { z } from "zod";
20
+ function parseRule(input) {
21
+ return ArchitecturalDefaultRuleSchema.parse(input);
22
+ }
23
+ var EvidenceMatcherKind, EvidenceMatcher, ChangeType, AppliesWhen, Reference, IsoDate, RuleId, ArchitecturalDefaultRuleSchema;
24
+ var init_rule_v1 = __esm({
25
+ "../vo-arch-defaults/src/schema/rule-v1.ts"() {
26
+ "use strict";
27
+ EvidenceMatcherKind = z.enum([
28
+ "regex",
29
+ "import-detector",
30
+ "package-json-field",
31
+ "file-size",
32
+ "ast-pattern",
33
+ "custom"
34
+ ]);
35
+ EvidenceMatcher = z.object({
36
+ kind: EvidenceMatcherKind,
37
+ pattern: z.string().optional(),
38
+ config: z.record(z.string(), z.unknown()).optional(),
39
+ description: z.string().min(1)
40
+ }).strict().refine(
41
+ (m) => m.kind !== "regex" || typeof m.pattern === "string" && m.pattern.length > 0,
42
+ { message: "regex matcher requires non-empty pattern" }
43
+ );
44
+ ChangeType = z.enum(["new-file", "edit", "delete", "rename", "any"]);
45
+ AppliesWhen = z.object({
46
+ stack: z.array(z.string().min(1)).optional(),
47
+ change_types: z.array(ChangeType).optional(),
48
+ file_globs: z.array(z.string().min(1)).optional(),
49
+ not_file_globs: z.array(z.string().min(1)).optional()
50
+ }).strict();
51
+ Reference = z.object({
52
+ type: z.enum(["framework-doc", "internal-doc", "pr", "incident", "external"]),
53
+ url: z.string().min(1),
54
+ description: z.string().min(1)
55
+ }).strict();
56
+ IsoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "last_verified must be ISO date YYYY-MM-DD").refine((s) => {
57
+ const d = /* @__PURE__ */ new Date(s + "T00:00:00Z");
58
+ return !Number.isNaN(d.getTime()) && d.toISOString().startsWith(s);
59
+ }, "last_verified must be a real calendar date");
60
+ RuleId = z.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/, {
61
+ message: "rule_id must be `<category>/<kebab-name>`"
62
+ });
63
+ ArchitecturalDefaultRuleSchema = z.object({
64
+ schema_version: z.literal(1),
65
+ rule_id: RuleId,
66
+ rule_version: z.number().int().positive(),
67
+ category: z.string().min(1),
68
+ severity: z.enum(["blocker", "warning", "info"]),
69
+ title: z.string().min(1),
70
+ rationale: z.string().min(1),
71
+ applies_when: AppliesWhen,
72
+ evidence_of_violation: z.array(EvidenceMatcher).min(1, {
73
+ message: "rule must declare at least one evidence matcher"
74
+ }),
75
+ remediation: z.string().min(1),
76
+ references: z.array(Reference),
77
+ last_verified: IsoDate,
78
+ tags: z.array(z.string().min(1))
79
+ }).strict();
80
+ }
26
81
  });
27
- function createStaticTokenSource(token, kind = "admin-token") {
28
- const value = token.trim();
29
- return { kind, getToken: async () => value.length > 0 ? value : null };
82
+
83
+ // ../vo-arch-defaults/src/schema/override-v1.ts
84
+ import { z as z2 } from "zod";
85
+ function parseOverride(input) {
86
+ return TenantOverrideSchema.parse(input);
30
87
  }
31
- function createFirebaseRefreshTokenSource(opts) {
32
- const refreshToken = opts.refreshToken.trim();
33
- const apiKey = opts.apiKey.trim();
34
- const now = opts.now ?? (() => Date.now());
35
- const fetchFn = opts.fetchFn ?? globalThis.fetch;
36
- let cachedToken = null;
37
- let expiresAtMs = 0;
38
- let inFlight = null;
39
- async function refresh() {
40
- try {
41
- const res = await fetchFn(`${FIREBASE_SECURETOKEN_URL}?key=${encodeURIComponent(apiKey)}`, {
42
- method: "POST",
43
- headers: {
44
- "content-type": "application/x-www-form-urlencoded",
45
- referer: FIREBASE_TOKEN_REFERER
46
- },
47
- body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`
48
- });
49
- const text = await res.text();
50
- if (res.status < 200 || res.status >= 300) {
51
- cachedToken = null;
52
- return null;
53
- }
54
- const parsed = JSON.parse(text);
55
- const idToken = typeof parsed.id_token === "string" ? parsed.id_token : "";
56
- if (!idToken) {
57
- cachedToken = null;
58
- return null;
59
- }
60
- const expiresInSec = Number(parsed.expires_in);
61
- const ttlMs = Number.isFinite(expiresInSec) && expiresInSec > 0 ? expiresInSec * 1e3 : 36e5;
62
- cachedToken = idToken;
63
- expiresAtMs = now() + ttlMs;
64
- return idToken;
65
- } catch {
66
- cachedToken = null;
67
- return null;
68
- }
88
+ var PartialRuleSchema, TenantOverrideSchema;
89
+ var init_override_v1 = __esm({
90
+ "../vo-arch-defaults/src/schema/override-v1.ts"() {
91
+ "use strict";
92
+ init_rule_v1();
93
+ PartialRuleSchema = ArchitecturalDefaultRuleSchema.partial();
94
+ TenantOverrideSchema = z2.object({
95
+ schema_version: z2.literal(1),
96
+ suppressed_rule_ids: z2.array(z2.string().min(1)),
97
+ modified_rules: z2.record(z2.string().min(1), PartialRuleSchema),
98
+ added_rules: z2.array(ArchitecturalDefaultRuleSchema),
99
+ /**
100
+ * Per-tenant stack override (added 2026-05-24 — audit HIGH
101
+ * "DEFAULT_STACK hardcoded for Nexus repo"). When set, downstream
102
+ * consumers (vo-mcp KB pre-filter, vo-arch-check CLI) use this list
103
+ * instead of their built-in default. Lets external tenants whose
104
+ * repo isn't `firebase+react+pnpm` get useful KB rule matches by
105
+ * dropping a `~/.claude/vo-arch-defaults.local.json` with their own
106
+ * stack identifiers no code change required.
107
+ *
108
+ * Open string union — values are not constrained beyond non-empty
109
+ * strings so out-of-tree tenants can declare their own
110
+ * (e.g. `vercel-edge`, `next-15`, `drizzle-postgres`).
111
+ *
112
+ * Optional. Undefined / omitted preserves pre-2026-05-24 behavior
113
+ * (consumer falls back to its hardcoded default stack).
114
+ */
115
+ tenant_stack: z2.array(z2.string().min(1)).optional()
116
+ }).strict();
69
117
  }
70
- return {
71
- kind: "firebase-refresh",
72
- async getToken() {
73
- if (cachedToken && now() < expiresAtMs - REFRESH_SKEW_MS) return cachedToken;
74
- if (!inFlight) {
75
- inFlight = refresh().finally(() => {
76
- inFlight = null;
77
- });
78
- }
79
- return inFlight;
80
- }
81
- };
118
+ });
119
+
120
+ // ../vo-arch-defaults/src/schema/index.ts
121
+ var init_schema = __esm({
122
+ "../vo-arch-defaults/src/schema/index.ts"() {
123
+ "use strict";
124
+ init_rule_v1();
125
+ init_override_v1();
126
+ }
127
+ });
128
+
129
+ // ../vo-arch-defaults/src/storage/load-bundled.ts
130
+ import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
131
+ import { dirname, join } from "node:path";
132
+ import { fileURLToPath } from "node:url";
133
+ function resolveBundledCorpusDir() {
134
+ const here = dirname(fileURLToPath(import.meta.url));
135
+ const candidates = [
136
+ join(here, "..", "corpus"),
137
+ // dist/corpus next to dist/storage
138
+ join(here, "..", "..", "corpus")
139
+ // src/corpus next to src/storage (under vitest)
140
+ ];
141
+ for (const c of candidates) {
142
+ if (existsSync(c) && statSync(c).isDirectory()) return c;
143
+ }
144
+ throw new Error(
145
+ `vo-arch-defaults: could not locate bundled corpus directory. Tried: ${candidates.join(", ")}`
146
+ );
82
147
  }
83
- function createAuthTokenSourceFromEnv(env = process.env, fetchFn, readStoredCred = () => null) {
84
- const refreshToken = env["VO_USER_REFRESH_TOKEN"]?.trim();
85
- const apiKey = env["VO_FIREBASE_API_KEY"]?.trim();
86
- const idToken = env["VO_USER_ID_TOKEN"]?.trim();
87
- const adminToken = env["VO_CONTROL_PLANE_ADMIN_TOKEN"]?.trim();
88
- if (refreshToken || apiKey) {
89
- if (!refreshToken || !apiKey) {
90
- throw new Error(
91
- "Per-user refresh auth requires BOTH VO_USER_REFRESH_TOKEN and VO_FIREBASE_API_KEY"
92
- );
148
+ function walkJson(dir, out) {
149
+ for (const entry of readdirSync(dir)) {
150
+ const full = join(dir, entry);
151
+ const st = statSync(full);
152
+ if (st.isDirectory()) {
153
+ walkJson(full, out);
154
+ } else if (entry.endsWith(".json")) {
155
+ out.push(full);
93
156
  }
94
- return createFirebaseRefreshTokenSource({
95
- refreshToken,
96
- apiKey,
97
- ...fetchFn ? { fetchFn } : {}
98
- });
99
- }
100
- if (idToken) return createStaticTokenSource(idToken, "firebase-id-token");
101
- const stored = readStoredCred();
102
- if (stored?.vo_credential && stored.vo_credential.trim()) {
103
- return createStaticTokenSource(stored.vo_credential.trim(), "vo-credential");
104
157
  }
105
- if (stored && stored.refresh_token?.trim() && stored.api_key?.trim()) {
106
- return createFirebaseRefreshTokenSource({
107
- refreshToken: stored.refresh_token.trim(),
108
- apiKey: stored.api_key.trim(),
109
- ...fetchFn ? { fetchFn } : {}
110
- });
158
+ }
159
+ function loadBundledCorpus(opts = {}) {
160
+ const dir = opts.corpusDir ?? resolveBundledCorpusDir();
161
+ const files = [];
162
+ walkJson(dir, files);
163
+ files.sort();
164
+ const rules = [];
165
+ const seenIds = /* @__PURE__ */ new Set();
166
+ for (const file of files) {
167
+ const raw = readFileSync(file, "utf8");
168
+ let parsed;
169
+ try {
170
+ parsed = JSON.parse(raw);
171
+ } catch (err) {
172
+ const m = err instanceof Error ? err.message : String(err);
173
+ throw new Error(`vo-arch-defaults: invalid JSON in ${file}: ${m}`, { cause: err });
174
+ }
175
+ try {
176
+ const rule = parseRule(parsed);
177
+ if (seenIds.has(rule.rule_id)) {
178
+ throw new Error(
179
+ `vo-arch-defaults: duplicate rule_id '${rule.rule_id}' (second occurrence in ${file})`
180
+ );
181
+ }
182
+ seenIds.add(rule.rule_id);
183
+ rules.push(rule);
184
+ } catch (err) {
185
+ const m = err instanceof Error ? err.message : String(err);
186
+ throw new Error(`vo-arch-defaults: schema validation failed for ${file}: ${m}`, { cause: err });
187
+ }
111
188
  }
112
- if (adminToken) return createStaticTokenSource(adminToken, "admin-token");
113
- return null;
189
+ return { rules, source_paths: files };
114
190
  }
115
- var FIREBASE_SECURETOKEN_URL, FIREBASE_TOKEN_REFERER, REFRESH_SKEW_MS;
116
- var init_auth_token_source = __esm({
117
- "src/cloud/auth-token-source.ts"() {
191
+ var init_load_bundled = __esm({
192
+ "../vo-arch-defaults/src/storage/load-bundled.ts"() {
118
193
  "use strict";
119
- FIREBASE_SECURETOKEN_URL = "https://securetoken.googleapis.com/v1/token";
120
- FIREBASE_TOKEN_REFERER = "https://algosuite.ai/";
121
- REFRESH_SKEW_MS = 6e4;
194
+ init_schema();
122
195
  }
123
196
  });
124
197
 
125
- // src/cloud/keychain.ts
126
- import { createRequire } from "node:module";
127
- function loadKeyring() {
128
- if (cached !== void 0) return cached;
129
- try {
130
- const req = createRequire(import.meta.url);
131
- const mod = req("@napi-rs/keyring");
132
- cached = mod && typeof mod.Entry === "function" ? mod : null;
133
- } catch {
134
- cached = null;
135
- }
136
- return cached;
137
- }
138
- function keychainAvailable() {
139
- return loadKeyring() !== null;
198
+ // ../vo-arch-defaults/src/storage/load-override.ts
199
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
200
+ import { homedir } from "node:os";
201
+ import { join as join2 } from "node:path";
202
+ function defaultOverridePath() {
203
+ return join2(homedir(), ".claude", "vo-arch-defaults.local.json");
140
204
  }
141
- function keychainGet() {
142
- const k = loadKeyring();
143
- if (!k) return null;
144
- try {
145
- return new k.Entry(SERVICE, ACCOUNT).getPassword();
146
- } catch {
147
- return null;
205
+ function loadTenantOverride(opts = {}) {
206
+ const path3 = opts.path ?? defaultOverridePath();
207
+ if (!existsSync2(path3)) {
208
+ return { override: null, source_path: null };
148
209
  }
149
- }
150
- function keychainSet(secret) {
151
- const k = loadKeyring();
152
- if (!k) return false;
210
+ const raw = readFileSync2(path3, "utf8");
211
+ let parsed;
153
212
  try {
154
- new k.Entry(SERVICE, ACCOUNT).setPassword(secret);
155
- return true;
156
- } catch {
157
- return false;
213
+ parsed = JSON.parse(raw);
214
+ } catch (err) {
215
+ const m = err instanceof Error ? err.message : String(err);
216
+ throw new Error(`vo-arch-defaults: invalid JSON in override ${path3}: ${m}`, { cause: err });
158
217
  }
159
- }
160
- function keychainDelete() {
161
- const k = loadKeyring();
162
- if (!k) return false;
163
218
  try {
164
- return new k.Entry(SERVICE, ACCOUNT).deletePassword();
165
- } catch {
166
- return false;
219
+ const override = parseOverride(parsed);
220
+ return { override, source_path: path3 };
221
+ } catch (err) {
222
+ const m = err instanceof Error ? err.message : String(err);
223
+ throw new Error(`vo-arch-defaults: override schema validation failed for ${path3}: ${m}`, { cause: err });
167
224
  }
168
225
  }
169
- var SERVICE, ACCOUNT, cached;
170
- var init_keychain = __esm({
171
- "src/cloud/keychain.ts"() {
226
+ var init_load_override = __esm({
227
+ "../vo-arch-defaults/src/storage/load-override.ts"() {
172
228
  "use strict";
173
- SERVICE = "vo-mcp";
174
- ACCOUNT = "refresh-credential";
229
+ init_schema();
175
230
  }
176
231
  });
177
232
 
178
- // src/cloud/credential-store.ts
179
- var credential_store_exports = {};
180
- __export(credential_store_exports, {
181
- KEYCHAIN_LOCATION: () => KEYCHAIN_LOCATION,
182
- credentialPath: () => credentialPath,
183
- readStoredCredential: () => readStoredCredential,
184
- writeStoredCredential: () => writeStoredCredential
185
- });
186
- import { homedir as homedir3 } from "node:os";
187
- import { join as join5, dirname as dirname4 } from "node:path";
188
- import {
189
- existsSync as existsSync3,
190
- mkdirSync as mkdirSync2,
191
- readFileSync as readFileSync5,
192
- writeFileSync as writeFileSync2,
193
- chmodSync as chmodSync2,
194
- rmSync
195
- } from "node:fs";
196
- function credentialPath(env = process.env) {
197
- const override = env["VO_MCP_CREDENTIALS_PATH"]?.trim();
198
- if (override) return override;
199
- return join5(homedir3(), ".config", "vo-mcp", "credentials.json");
200
- }
201
- function keychainEnabled(env, keychain) {
202
- const disabled = (env["VO_MCP_DISABLE_KEYCHAIN"] ?? "").trim().toLowerCase();
203
- if (disabled === "1" || disabled === "true" || disabled === "yes") return false;
204
- return keychain.available();
205
- }
206
- function deserialize(raw) {
207
- try {
208
- const parsed = JSON.parse(raw);
209
- const refresh = typeof parsed.refresh_token === "string" ? parsed.refresh_token.trim() : "";
210
- const apiKey = typeof parsed.api_key === "string" ? parsed.api_key.trim() : "";
211
- const voCred = typeof parsed.vo_credential === "string" ? parsed.vo_credential.trim() : "";
212
- if (!voCred && (!refresh || !apiKey)) return null;
213
- return {
214
- ...refresh ? { refresh_token: refresh } : {},
215
- ...apiKey ? { api_key: apiKey } : {},
216
- ...voCred ? { vo_credential: voCred } : {},
217
- ...typeof parsed.vo_credential_expires_at === "string" ? { vo_credential_expires_at: parsed.vo_credential_expires_at } : {},
218
- ...typeof parsed.email === "string" ? { email: parsed.email } : {},
219
- ...typeof parsed.stored_at === "string" ? { stored_at: parsed.stored_at } : {}
220
- };
221
- } catch {
222
- return null;
223
- }
224
- }
225
- function readFromFile(env) {
226
- try {
227
- const p = credentialPath(env);
228
- if (!existsSync3(p)) return null;
229
- return deserialize(readFileSync5(p, "utf8"));
230
- } catch {
231
- return null;
232
- }
233
- }
234
- function readStoredCredential(env = process.env, keychain = realKeychain) {
235
- if (keychainEnabled(env, keychain)) {
236
- const raw = keychain.get();
237
- const fromKeychain = raw ? deserialize(raw) : null;
238
- if (fromKeychain) return fromKeychain;
239
- }
240
- return readFromFile(env);
241
- }
242
- function deleteFile(env) {
243
- try {
244
- rmSync(credentialPath(env), { force: true });
245
- } catch {
246
- }
247
- }
248
- function writeToFile(payload, env) {
249
- const p = credentialPath(env);
250
- mkdirSync2(dirname4(p), { recursive: true });
251
- writeFileSync2(p, `${JSON.stringify(payload, null, 2)}
252
- `, { mode: 384 });
253
- try {
254
- chmodSync2(p, 384);
255
- } catch {
256
- }
257
- return p;
258
- }
259
- function writeStoredCredential(cred, storedAt, env = process.env, keychain = realKeychain) {
260
- const payload = {
261
- ...cred.refresh_token ? { refresh_token: cred.refresh_token } : {},
262
- ...cred.api_key ? { api_key: cred.api_key } : {},
263
- ...cred.vo_credential ? { vo_credential: cred.vo_credential } : {},
264
- ...cred.vo_credential_expires_at ? { vo_credential_expires_at: cred.vo_credential_expires_at } : {},
265
- ...cred.email ? { email: cred.email } : {},
266
- stored_at: cred.stored_at ?? storedAt
267
- };
268
- if (keychainEnabled(env, keychain) && keychain.set(JSON.stringify(payload))) {
269
- deleteFile(env);
270
- return KEYCHAIN_LOCATION;
271
- }
272
- const p = writeToFile(payload, env);
273
- if (keychainEnabled(env, keychain)) keychain.delete();
274
- return p;
275
- }
276
- var realKeychain, KEYCHAIN_LOCATION;
277
- var init_credential_store = __esm({
278
- "src/cloud/credential-store.ts"() {
279
- "use strict";
280
- init_keychain();
281
- realKeychain = {
282
- available: keychainAvailable,
283
- get: keychainGet,
284
- set: keychainSet,
285
- delete: keychainDelete
286
- };
287
- KEYCHAIN_LOCATION = 'OS keychain (service "vo-mcp")';
288
- }
289
- });
290
-
291
- // src/cli.ts
292
- import { homedir as homedir6, hostname } from "node:os";
293
- import { join as join8 } from "node:path";
294
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
295
-
296
- // src/server.ts
297
- import { randomUUID as randomUUID2 } from "node:crypto";
298
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
299
- import {
300
- CallToolRequestSchema,
301
- ListToolsRequestSchema
302
- } from "@modelcontextprotocol/sdk/types.js";
303
-
304
- // src/tools/common.ts
305
- import { createHash as createHash2, randomUUID } from "node:crypto";
306
- import { readFileSync as readFileSync4 } from "node:fs";
307
- import { dirname as dirname3, join as join4 } from "node:path";
308
- import { fileURLToPath as fileURLToPath2 } from "node:url";
309
- import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
310
-
311
- // src/logging/events-writer.ts
312
- import {
313
- appendFileSync,
314
- chmodSync,
315
- mkdirSync,
316
- readdirSync as readdirSync2,
317
- readFileSync as readFileSync3,
318
- statSync as statSync2,
319
- unlinkSync,
320
- writeFileSync
321
- } from "node:fs";
322
- import { homedir as homedir2 } from "node:os";
323
- import { basename, dirname as dirname2, join as join3 } from "node:path";
324
- import { gzipSync } from "node:zlib";
325
-
326
- // ../vo-arch-defaults/src/schema/rule-v1.ts
327
- import { z } from "zod";
328
- var EvidenceMatcherKind = z.enum([
329
- "regex",
330
- "import-detector",
331
- "package-json-field",
332
- "file-size",
333
- "ast-pattern",
334
- "custom"
335
- ]);
336
- var EvidenceMatcher = z.object({
337
- kind: EvidenceMatcherKind,
338
- pattern: z.string().optional(),
339
- config: z.record(z.string(), z.unknown()).optional(),
340
- description: z.string().min(1)
341
- }).strict().refine(
342
- (m) => m.kind !== "regex" || typeof m.pattern === "string" && m.pattern.length > 0,
343
- { message: "regex matcher requires non-empty pattern" }
344
- );
345
- var ChangeType = z.enum(["new-file", "edit", "delete", "rename", "any"]);
346
- var AppliesWhen = z.object({
347
- stack: z.array(z.string().min(1)).optional(),
348
- change_types: z.array(ChangeType).optional(),
349
- file_globs: z.array(z.string().min(1)).optional(),
350
- not_file_globs: z.array(z.string().min(1)).optional()
351
- }).strict();
352
- var Reference = z.object({
353
- type: z.enum(["framework-doc", "internal-doc", "pr", "incident", "external"]),
354
- url: z.string().min(1),
355
- description: z.string().min(1)
356
- }).strict();
357
- var IsoDate = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "last_verified must be ISO date YYYY-MM-DD").refine((s) => {
358
- const d = /* @__PURE__ */ new Date(s + "T00:00:00Z");
359
- return !Number.isNaN(d.getTime()) && d.toISOString().startsWith(s);
360
- }, "last_verified must be a real calendar date");
361
- var RuleId = z.string().regex(/^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/, {
362
- message: "rule_id must be `<category>/<kebab-name>`"
363
- });
364
- var ArchitecturalDefaultRuleSchema = z.object({
365
- schema_version: z.literal(1),
366
- rule_id: RuleId,
367
- rule_version: z.number().int().positive(),
368
- category: z.string().min(1),
369
- severity: z.enum(["blocker", "warning", "info"]),
370
- title: z.string().min(1),
371
- rationale: z.string().min(1),
372
- applies_when: AppliesWhen,
373
- evidence_of_violation: z.array(EvidenceMatcher).min(1, {
374
- message: "rule must declare at least one evidence matcher"
375
- }),
376
- remediation: z.string().min(1),
377
- references: z.array(Reference),
378
- last_verified: IsoDate,
379
- tags: z.array(z.string().min(1))
380
- }).strict();
381
- function parseRule(input) {
382
- return ArchitecturalDefaultRuleSchema.parse(input);
383
- }
384
-
385
- // ../vo-arch-defaults/src/schema/override-v1.ts
386
- import { z as z2 } from "zod";
387
- var PartialRuleSchema = ArchitecturalDefaultRuleSchema.partial();
388
- var TenantOverrideSchema = z2.object({
389
- schema_version: z2.literal(1),
390
- suppressed_rule_ids: z2.array(z2.string().min(1)),
391
- modified_rules: z2.record(z2.string().min(1), PartialRuleSchema),
392
- added_rules: z2.array(ArchitecturalDefaultRuleSchema),
393
- /**
394
- * Per-tenant stack override (added 2026-05-24 — audit HIGH
395
- * "DEFAULT_STACK hardcoded for Nexus repo"). When set, downstream
396
- * consumers (vo-mcp KB pre-filter, vo-arch-check CLI) use this list
397
- * instead of their built-in default. Lets external tenants whose
398
- * repo isn't `firebase+react+pnpm` get useful KB rule matches by
399
- * dropping a `~/.claude/vo-arch-defaults.local.json` with their own
400
- * stack identifiers — no code change required.
401
- *
402
- * Open string union — values are not constrained beyond non-empty
403
- * strings so out-of-tree tenants can declare their own
404
- * (e.g. `vercel-edge`, `next-15`, `drizzle-postgres`).
405
- *
406
- * Optional. Undefined / omitted preserves pre-2026-05-24 behavior
407
- * (consumer falls back to its hardcoded default stack).
408
- */
409
- tenant_stack: z2.array(z2.string().min(1)).optional()
410
- }).strict();
411
- function parseOverride(input) {
412
- return TenantOverrideSchema.parse(input);
413
- }
414
-
415
- // ../vo-arch-defaults/src/storage/load-bundled.ts
416
- import { readdirSync, readFileSync, statSync, existsSync } from "node:fs";
417
- import { dirname, join } from "node:path";
418
- import { fileURLToPath } from "node:url";
419
- function resolveBundledCorpusDir() {
420
- const here = dirname(fileURLToPath(import.meta.url));
421
- const candidates = [
422
- join(here, "..", "corpus"),
423
- // dist/corpus next to dist/storage
424
- join(here, "..", "..", "corpus")
425
- // src/corpus next to src/storage (under vitest)
426
- ];
427
- for (const c of candidates) {
428
- if (existsSync(c) && statSync(c).isDirectory()) return c;
429
- }
430
- throw new Error(
431
- `vo-arch-defaults: could not locate bundled corpus directory. Tried: ${candidates.join(", ")}`
432
- );
433
- }
434
- function walkJson(dir, out) {
435
- for (const entry of readdirSync(dir)) {
436
- const full = join(dir, entry);
437
- const st = statSync(full);
438
- if (st.isDirectory()) {
439
- walkJson(full, out);
440
- } else if (entry.endsWith(".json")) {
441
- out.push(full);
442
- }
443
- }
444
- }
445
- function loadBundledCorpus(opts = {}) {
446
- const dir = opts.corpusDir ?? resolveBundledCorpusDir();
447
- const files = [];
448
- walkJson(dir, files);
449
- files.sort();
450
- const rules = [];
451
- const seenIds = /* @__PURE__ */ new Set();
452
- for (const file of files) {
453
- const raw = readFileSync(file, "utf8");
454
- let parsed;
455
- try {
456
- parsed = JSON.parse(raw);
457
- } catch (err) {
458
- const m = err instanceof Error ? err.message : String(err);
459
- throw new Error(`vo-arch-defaults: invalid JSON in ${file}: ${m}`, { cause: err });
460
- }
461
- try {
462
- const rule = parseRule(parsed);
463
- if (seenIds.has(rule.rule_id)) {
464
- throw new Error(
465
- `vo-arch-defaults: duplicate rule_id '${rule.rule_id}' (second occurrence in ${file})`
466
- );
467
- }
468
- seenIds.add(rule.rule_id);
469
- rules.push(rule);
470
- } catch (err) {
471
- const m = err instanceof Error ? err.message : String(err);
472
- throw new Error(`vo-arch-defaults: schema validation failed for ${file}: ${m}`, { cause: err });
473
- }
474
- }
475
- return { rules, source_paths: files };
476
- }
477
-
478
- // ../vo-arch-defaults/src/storage/load-override.ts
479
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
480
- import { homedir } from "node:os";
481
- import { join as join2 } from "node:path";
482
- function defaultOverridePath() {
483
- return join2(homedir(), ".claude", "vo-arch-defaults.local.json");
484
- }
485
- function loadTenantOverride(opts = {}) {
486
- const path3 = opts.path ?? defaultOverridePath();
487
- if (!existsSync2(path3)) {
488
- return { override: null, source_path: null };
489
- }
490
- const raw = readFileSync2(path3, "utf8");
491
- let parsed;
492
- try {
493
- parsed = JSON.parse(raw);
494
- } catch (err) {
495
- const m = err instanceof Error ? err.message : String(err);
496
- throw new Error(`vo-arch-defaults: invalid JSON in override ${path3}: ${m}`, { cause: err });
497
- }
498
- try {
499
- const override = parseOverride(parsed);
500
- return { override, source_path: path3 };
501
- } catch (err) {
502
- const m = err instanceof Error ? err.message : String(err);
503
- throw new Error(`vo-arch-defaults: override schema validation failed for ${path3}: ${m}`, { cause: err });
504
- }
505
- }
506
-
507
233
  // ../vo-arch-defaults/src/storage/merge.ts
508
- var IMMUTABLE_FIELDS = [
509
- "schema_version",
510
- "rule_id"
511
- ];
512
234
  function applyModification(base, patch) {
513
235
  const cleanPatch = { ...patch };
514
236
  for (const k of IMMUTABLE_FIELDS) {
@@ -560,6 +282,16 @@ function mergeCorpusWithOverride(bundled, override) {
560
282
  added_count: addedCount
561
283
  };
562
284
  }
285
+ var IMMUTABLE_FIELDS;
286
+ var init_merge = __esm({
287
+ "../vo-arch-defaults/src/storage/merge.ts"() {
288
+ "use strict";
289
+ IMMUTABLE_FIELDS = [
290
+ "schema_version",
291
+ "rule_id"
292
+ ];
293
+ }
294
+ });
563
295
 
564
296
  // ../vo-arch-defaults/src/query/applies-to-stack.ts
565
297
  function appliesToStack(rule, stacks) {
@@ -572,6 +304,11 @@ function appliesToStack(rule, stacks) {
572
304
  }
573
305
  return false;
574
306
  }
307
+ var init_applies_to_stack = __esm({
308
+ "../vo-arch-defaults/src/query/applies-to-stack.ts"() {
309
+ "use strict";
310
+ }
311
+ });
575
312
 
576
313
  // ../vo-arch-defaults/src/query/applies-to-change.ts
577
314
  function appliesToChangeType(rule, changeType) {
@@ -581,9 +318,13 @@ function appliesToChangeType(rule, changeType) {
581
318
  if (changeType === "any") return true;
582
319
  return required.includes(changeType);
583
320
  }
321
+ var init_applies_to_change = __esm({
322
+ "../vo-arch-defaults/src/query/applies-to-change.ts"() {
323
+ "use strict";
324
+ }
325
+ });
584
326
 
585
327
  // ../vo-arch-defaults/src/query/glob.ts
586
- var REGEX_META = /[.+^${}()|[\]\\]/g;
587
328
  function globToRegExp(glob) {
588
329
  let out = "";
589
330
  let i = 0;
@@ -636,6 +377,13 @@ function matchesAnyGlob(path3, globs) {
636
377
  }
637
378
  return false;
638
379
  }
380
+ var REGEX_META;
381
+ var init_glob = __esm({
382
+ "../vo-arch-defaults/src/query/glob.ts"() {
383
+ "use strict";
384
+ REGEX_META = /[.+^${}()|[\]\\]/g;
385
+ }
386
+ });
639
387
 
640
388
  // ../vo-arch-defaults/src/query/applies-to-files.ts
641
389
  function appliesToFiles(rule, filePaths) {
@@ -656,8 +404,14 @@ function appliesToFiles(rule, filePaths) {
656
404
  }
657
405
  return false;
658
406
  }
659
-
660
- // ../vo-arch-defaults/src/analyze/diff-parser.ts
407
+ var init_applies_to_files = __esm({
408
+ "../vo-arch-defaults/src/query/applies-to-files.ts"() {
409
+ "use strict";
410
+ init_glob();
411
+ }
412
+ });
413
+
414
+ // ../vo-arch-defaults/src/analyze/diff-parser.ts
661
415
  function stripPathPrefix(p) {
662
416
  if (p.startsWith("a/") || p.startsWith("b/")) return p.slice(2);
663
417
  return p;
@@ -727,9 +481,13 @@ function parseUnifiedDiff(text) {
727
481
  }
728
482
  return { files };
729
483
  }
484
+ var init_diff_parser = __esm({
485
+ "../vo-arch-defaults/src/analyze/diff-parser.ts"() {
486
+ "use strict";
487
+ }
488
+ });
730
489
 
731
490
  // ../vo-arch-defaults/src/analyze/evidence-matchers/regex-matcher.ts
732
- var MAX_EXCERPT = 200;
733
491
  function sanitizeLine(s) {
734
492
  let out = s.replace(/^\s+/, "");
735
493
  if (out.length > MAX_EXCERPT) out = out.slice(0, MAX_EXCERPT) + "\u2026";
@@ -757,12 +515,15 @@ function runRegexMatcher(matcher, file) {
757
515
  }
758
516
  return hits;
759
517
  }
518
+ var MAX_EXCERPT;
519
+ var init_regex_matcher = __esm({
520
+ "../vo-arch-defaults/src/analyze/evidence-matchers/regex-matcher.ts"() {
521
+ "use strict";
522
+ MAX_EXCERPT = 200;
523
+ }
524
+ });
760
525
 
761
526
  // ../vo-arch-defaults/src/analyze/evidence-matchers/import-detector.ts
762
- var STATIC_IMPORT = /import\s+(?:[^'"\n]{0,200}?from\s+)?(['"])([^'"]+)\1/;
763
- var DYNAMIC_IMPORT = /import\(\s*(['"])([^'"]+)\1\s*\)/;
764
- var REQUIRE_CALL = /require\(\s*(['"])([^'"]+)\1\s*\)/;
765
- var MAX_EXCERPT2 = 200;
766
527
  function sanitize(s) {
767
528
  let out = s.replace(/^\s+/, "");
768
529
  if (out.length > MAX_EXCERPT2) out = out.slice(0, MAX_EXCERPT2) + "\u2026";
@@ -807,6 +568,16 @@ function runImportDetector(matcher, file) {
807
568
  }
808
569
  return hits;
809
570
  }
571
+ var STATIC_IMPORT, DYNAMIC_IMPORT, REQUIRE_CALL, MAX_EXCERPT2;
572
+ var init_import_detector = __esm({
573
+ "../vo-arch-defaults/src/analyze/evidence-matchers/import-detector.ts"() {
574
+ "use strict";
575
+ STATIC_IMPORT = /import\s+(?:[^'"\n]{0,200}?from\s+)?(['"])([^'"]+)\1/;
576
+ DYNAMIC_IMPORT = /import\(\s*(['"])([^'"]+)\1\s*\)/;
577
+ REQUIRE_CALL = /require\(\s*(['"])([^'"]+)\1\s*\)/;
578
+ MAX_EXCERPT2 = 200;
579
+ }
580
+ });
810
581
 
811
582
  // ../vo-arch-defaults/src/analyze/evidence-matchers/file-size.ts
812
583
  function runFileSizeMatcher(matcher, file) {
@@ -827,6 +598,11 @@ function runFileSizeMatcher(matcher, file) {
827
598
  }
828
599
  ];
829
600
  }
601
+ var init_file_size = __esm({
602
+ "../vo-arch-defaults/src/analyze/evidence-matchers/file-size.ts"() {
603
+ "use strict";
604
+ }
605
+ });
830
606
 
831
607
  // ../vo-arch-defaults/src/analyze/evidence-matchers/package-json.ts
832
608
  function valueMatches(value, cfg) {
@@ -867,6 +643,11 @@ function runPackageJsonMatcher(matcher, file) {
867
643
  }
868
644
  return hits;
869
645
  }
646
+ var init_package_json = __esm({
647
+ "../vo-arch-defaults/src/analyze/evidence-matchers/package-json.ts"() {
648
+ "use strict";
649
+ }
650
+ });
870
651
 
871
652
  // ../vo-arch-defaults/src/analyze/evidence-matchers/index.ts
872
653
  function runEvidenceMatcher(matcher, file) {
@@ -886,9 +667,17 @@ function runEvidenceMatcher(matcher, file) {
886
667
  return [];
887
668
  }
888
669
  }
670
+ var init_evidence_matchers = __esm({
671
+ "../vo-arch-defaults/src/analyze/evidence-matchers/index.ts"() {
672
+ "use strict";
673
+ init_regex_matcher();
674
+ init_import_detector();
675
+ init_file_size();
676
+ init_package_json();
677
+ }
678
+ });
889
679
 
890
680
  // ../vo-arch-defaults/src/query/staleness.ts
891
- var DEFAULT_STALENESS_THRESHOLD_DAYS = 365;
892
681
  function computeStaleRules(rules, opts = {}) {
893
682
  const threshold = opts.thresholdDays ?? DEFAULT_STALENESS_THRESHOLD_DAYS;
894
683
  if (!Number.isFinite(threshold)) return [];
@@ -912,6 +701,13 @@ function computeStaleRules(rules, opts = {}) {
912
701
  out.sort((a, b) => b.days_stale - a.days_stale);
913
702
  return out;
914
703
  }
704
+ var DEFAULT_STALENESS_THRESHOLD_DAYS;
705
+ var init_staleness = __esm({
706
+ "../vo-arch-defaults/src/query/staleness.ts"() {
707
+ "use strict";
708
+ DEFAULT_STALENESS_THRESHOLD_DAYS = 365;
709
+ }
710
+ });
915
711
 
916
712
  // ../vo-arch-defaults/src/query/run-query.ts
917
713
  function findApplicableRules(input, opts = {}) {
@@ -970,9 +766,28 @@ function findApplicableRules(input, opts = {}) {
970
766
  stale_rules
971
767
  };
972
768
  }
769
+ var init_run_query = __esm({
770
+ "../vo-arch-defaults/src/query/run-query.ts"() {
771
+ "use strict";
772
+ init_load_bundled();
773
+ init_load_override();
774
+ init_merge();
775
+ init_applies_to_stack();
776
+ init_applies_to_change();
777
+ init_applies_to_files();
778
+ init_diff_parser();
779
+ init_evidence_matchers();
780
+ init_staleness();
781
+ }
782
+ });
973
783
 
974
784
  // ../vo-arch-defaults/src/query/corpus-version.ts
975
785
  import { createHash } from "node:crypto";
786
+ var init_corpus_version = __esm({
787
+ "../vo-arch-defaults/src/query/corpus-version.ts"() {
788
+ "use strict";
789
+ }
790
+ });
976
791
 
977
792
  // ../vo-arch-defaults/src/query/resolve-stack.ts
978
793
  function resolveStack(override, fallback) {
@@ -981,9 +796,13 @@ function resolveStack(override, fallback) {
981
796
  }
982
797
  return fallback;
983
798
  }
799
+ var init_resolve_stack = __esm({
800
+ "../vo-arch-defaults/src/query/resolve-stack.ts"() {
801
+ "use strict";
802
+ }
803
+ });
984
804
 
985
805
  // ../vo-arch-defaults/src/pii-sanitize.ts
986
- var SANITIZE_DEFAULT_MAX_LEN = 200;
987
806
  function sanitizeExcerpt(raw, maxLen = SANITIZE_DEFAULT_MAX_LEN) {
988
807
  let s = raw;
989
808
  s = s.replace(/eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{5,}/g, "[REDACTED_JWT]");
@@ -999,10 +818,49 @@ function sanitizeExcerpt(raw, maxLen = SANITIZE_DEFAULT_MAX_LEN) {
999
818
  if (s.length > maxLen) s = s.slice(0, maxLen) + "\u2026";
1000
819
  return s;
1001
820
  }
821
+ var SANITIZE_DEFAULT_MAX_LEN;
822
+ var init_pii_sanitize = __esm({
823
+ "../vo-arch-defaults/src/pii-sanitize.ts"() {
824
+ "use strict";
825
+ SANITIZE_DEFAULT_MAX_LEN = 200;
826
+ }
827
+ });
828
+
829
+ // ../vo-arch-defaults/src/index.ts
830
+ var init_src = __esm({
831
+ "../vo-arch-defaults/src/index.ts"() {
832
+ init_schema();
833
+ init_load_bundled();
834
+ init_load_override();
835
+ init_merge();
836
+ init_applies_to_stack();
837
+ init_applies_to_change();
838
+ init_applies_to_files();
839
+ init_run_query();
840
+ init_glob();
841
+ init_corpus_version();
842
+ init_resolve_stack();
843
+ init_staleness();
844
+ init_diff_parser();
845
+ init_evidence_matchers();
846
+ init_pii_sanitize();
847
+ }
848
+ });
1002
849
 
1003
850
  // src/logging/events-writer.ts
1004
- var DEFAULT_EVENTS_MAX_BYTES = 50 * 1024 * 1024;
1005
- var DEFAULT_EVENTS_KEEP_ROTATED = 10;
851
+ import {
852
+ appendFileSync,
853
+ chmodSync,
854
+ mkdirSync,
855
+ readdirSync as readdirSync2,
856
+ readFileSync as readFileSync3,
857
+ statSync as statSync2,
858
+ unlinkSync,
859
+ writeFileSync
860
+ } from "node:fs";
861
+ import { homedir as homedir2 } from "node:os";
862
+ import { basename, dirname as dirname2, join as join3 } from "node:path";
863
+ import { gzipSync } from "node:zlib";
1006
864
  function defaultEventsPath() {
1007
865
  const envPath = process.env["VO_MCP_EVENTS_PATH"];
1008
866
  if (envPath && envPath.length > 0) return envPath;
@@ -1109,19 +967,38 @@ function createFileEventsWriter(opts = {}) {
1109
967
  }
1110
968
  };
1111
969
  }
970
+ var DEFAULT_EVENTS_MAX_BYTES, DEFAULT_EVENTS_KEEP_ROTATED;
971
+ var init_events_writer = __esm({
972
+ "src/logging/events-writer.ts"() {
973
+ "use strict";
974
+ init_src();
975
+ DEFAULT_EVENTS_MAX_BYTES = 50 * 1024 * 1024;
976
+ DEFAULT_EVENTS_KEEP_ROTATED = 10;
977
+ }
978
+ });
1112
979
 
1113
980
  // src/tools/common.ts
981
+ import { createHash as createHash2, randomUUID } from "node:crypto";
982
+ import { readFileSync as readFileSync4 } from "node:fs";
983
+ import { dirname as dirname3, join as join4 } from "node:path";
984
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
985
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
1114
986
  function readVoMcpVersion() {
1115
987
  try {
1116
988
  const here = dirname3(fileURLToPath2(import.meta.url));
1117
- const pkgPath = join4(here, "..", "..", "package.json");
1118
- const pkg = JSON.parse(readFileSync4(pkgPath, "utf8"));
1119
- return typeof pkg.version === "string" ? pkg.version : "0.0.0-unknown";
989
+ for (const rel of ["..", ["..", ".."], ["..", "..", ".."]]) {
990
+ try {
991
+ const segs = Array.isArray(rel) ? rel : [rel];
992
+ const pkg = JSON.parse(readFileSync4(join4(here, ...segs, "package.json"), "utf8"));
993
+ if (pkg.name === "@algosuite/vo-mcp" && typeof pkg.version === "string") return pkg.version;
994
+ } catch {
995
+ }
996
+ }
997
+ return "0.0.0-unknown";
1120
998
  } catch {
1121
999
  return "0.0.0-unknown";
1122
1000
  }
1123
1001
  }
1124
- var VO_MCP_VERSION = readVoMcpVersion();
1125
1002
  function bytesOf(s) {
1126
1003
  return Buffer.byteLength(s, "utf8");
1127
1004
  }
@@ -1209,6 +1086,521 @@ function toEventSynthesizedVerdict(src) {
1209
1086
  reasoning_excerpt: sanitizeExcerpt(src.reasoning_excerpt)
1210
1087
  };
1211
1088
  }
1089
+ var VO_MCP_VERSION;
1090
+ var init_common = __esm({
1091
+ "src/tools/common.ts"() {
1092
+ "use strict";
1093
+ init_events_writer();
1094
+ VO_MCP_VERSION = readVoMcpVersion();
1095
+ }
1096
+ });
1097
+
1098
+ // src/cloud/auth-token-source.ts
1099
+ var auth_token_source_exports = {};
1100
+ __export(auth_token_source_exports, {
1101
+ FIREBASE_SECURETOKEN_URL: () => FIREBASE_SECURETOKEN_URL,
1102
+ FIREBASE_TOKEN_REFERER: () => FIREBASE_TOKEN_REFERER,
1103
+ createAuthTokenSourceFromEnv: () => createAuthTokenSourceFromEnv,
1104
+ createFirebaseRefreshTokenSource: () => createFirebaseRefreshTokenSource,
1105
+ createStaticTokenSource: () => createStaticTokenSource
1106
+ });
1107
+ function createStaticTokenSource(token, kind = "admin-token") {
1108
+ const value = token.trim();
1109
+ return { kind, getToken: async () => value.length > 0 ? value : null };
1110
+ }
1111
+ function createFirebaseRefreshTokenSource(opts) {
1112
+ const refreshToken = opts.refreshToken.trim();
1113
+ const apiKey = opts.apiKey.trim();
1114
+ const now = opts.now ?? (() => Date.now());
1115
+ const fetchFn = opts.fetchFn ?? globalThis.fetch;
1116
+ let cachedToken = null;
1117
+ let expiresAtMs = 0;
1118
+ let inFlight = null;
1119
+ async function refresh() {
1120
+ try {
1121
+ const res = await fetchFn(`${FIREBASE_SECURETOKEN_URL}?key=${encodeURIComponent(apiKey)}`, {
1122
+ method: "POST",
1123
+ headers: {
1124
+ "content-type": "application/x-www-form-urlencoded",
1125
+ referer: FIREBASE_TOKEN_REFERER
1126
+ },
1127
+ body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`
1128
+ });
1129
+ const text = await res.text();
1130
+ if (res.status < 200 || res.status >= 300) {
1131
+ cachedToken = null;
1132
+ return null;
1133
+ }
1134
+ const parsed = JSON.parse(text);
1135
+ const idToken = typeof parsed.id_token === "string" ? parsed.id_token : "";
1136
+ if (!idToken) {
1137
+ cachedToken = null;
1138
+ return null;
1139
+ }
1140
+ const expiresInSec = Number(parsed.expires_in);
1141
+ const ttlMs = Number.isFinite(expiresInSec) && expiresInSec > 0 ? expiresInSec * 1e3 : 36e5;
1142
+ cachedToken = idToken;
1143
+ expiresAtMs = now() + ttlMs;
1144
+ return idToken;
1145
+ } catch {
1146
+ cachedToken = null;
1147
+ return null;
1148
+ }
1149
+ }
1150
+ return {
1151
+ kind: "firebase-refresh",
1152
+ async getToken() {
1153
+ if (cachedToken && now() < expiresAtMs - REFRESH_SKEW_MS) return cachedToken;
1154
+ if (!inFlight) {
1155
+ inFlight = refresh().finally(() => {
1156
+ inFlight = null;
1157
+ });
1158
+ }
1159
+ return inFlight;
1160
+ }
1161
+ };
1162
+ }
1163
+ function createAuthTokenSourceFromEnv(env = process.env, fetchFn, readStoredCred = () => null) {
1164
+ const refreshToken = env["VO_USER_REFRESH_TOKEN"]?.trim();
1165
+ const apiKey = env["VO_FIREBASE_API_KEY"]?.trim();
1166
+ const idToken = env["VO_USER_ID_TOKEN"]?.trim();
1167
+ const adminToken = env["VO_CONTROL_PLANE_ADMIN_TOKEN"]?.trim();
1168
+ if (refreshToken || apiKey) {
1169
+ if (!refreshToken || !apiKey) {
1170
+ throw new Error(
1171
+ "Per-user refresh auth requires BOTH VO_USER_REFRESH_TOKEN and VO_FIREBASE_API_KEY"
1172
+ );
1173
+ }
1174
+ return createFirebaseRefreshTokenSource({
1175
+ refreshToken,
1176
+ apiKey,
1177
+ ...fetchFn ? { fetchFn } : {}
1178
+ });
1179
+ }
1180
+ if (idToken) return createStaticTokenSource(idToken, "firebase-id-token");
1181
+ const stored = readStoredCred();
1182
+ if (stored?.vo_credential && stored.vo_credential.trim()) {
1183
+ return createStaticTokenSource(stored.vo_credential.trim(), "vo-credential");
1184
+ }
1185
+ if (stored && stored.refresh_token?.trim() && stored.api_key?.trim()) {
1186
+ return createFirebaseRefreshTokenSource({
1187
+ refreshToken: stored.refresh_token.trim(),
1188
+ apiKey: stored.api_key.trim(),
1189
+ ...fetchFn ? { fetchFn } : {}
1190
+ });
1191
+ }
1192
+ if (adminToken) return createStaticTokenSource(adminToken, "admin-token");
1193
+ return null;
1194
+ }
1195
+ var FIREBASE_SECURETOKEN_URL, FIREBASE_TOKEN_REFERER, REFRESH_SKEW_MS;
1196
+ var init_auth_token_source = __esm({
1197
+ "src/cloud/auth-token-source.ts"() {
1198
+ "use strict";
1199
+ FIREBASE_SECURETOKEN_URL = "https://securetoken.googleapis.com/v1/token";
1200
+ FIREBASE_TOKEN_REFERER = "https://algosuite.ai/";
1201
+ REFRESH_SKEW_MS = 6e4;
1202
+ }
1203
+ });
1204
+
1205
+ // src/cloud/keychain.ts
1206
+ import { createRequire } from "node:module";
1207
+ function loadKeyring() {
1208
+ if (cached !== void 0) return cached;
1209
+ try {
1210
+ const req = createRequire(import.meta.url);
1211
+ const mod = req("@napi-rs/keyring");
1212
+ cached = mod && typeof mod.Entry === "function" ? mod : null;
1213
+ } catch {
1214
+ cached = null;
1215
+ }
1216
+ return cached;
1217
+ }
1218
+ function keychainAvailable() {
1219
+ return loadKeyring() !== null;
1220
+ }
1221
+ function keychainGet() {
1222
+ const k = loadKeyring();
1223
+ if (!k) return null;
1224
+ try {
1225
+ return new k.Entry(SERVICE, ACCOUNT).getPassword();
1226
+ } catch {
1227
+ return null;
1228
+ }
1229
+ }
1230
+ function keychainSet(secret) {
1231
+ const k = loadKeyring();
1232
+ if (!k) return false;
1233
+ try {
1234
+ new k.Entry(SERVICE, ACCOUNT).setPassword(secret);
1235
+ return true;
1236
+ } catch {
1237
+ return false;
1238
+ }
1239
+ }
1240
+ function keychainDelete() {
1241
+ const k = loadKeyring();
1242
+ if (!k) return false;
1243
+ try {
1244
+ return new k.Entry(SERVICE, ACCOUNT).deletePassword();
1245
+ } catch {
1246
+ return false;
1247
+ }
1248
+ }
1249
+ var SERVICE, ACCOUNT, cached;
1250
+ var init_keychain = __esm({
1251
+ "src/cloud/keychain.ts"() {
1252
+ "use strict";
1253
+ SERVICE = "vo-mcp";
1254
+ ACCOUNT = "refresh-credential";
1255
+ }
1256
+ });
1257
+
1258
+ // src/cloud/credential-store.ts
1259
+ var credential_store_exports = {};
1260
+ __export(credential_store_exports, {
1261
+ KEYCHAIN_LOCATION: () => KEYCHAIN_LOCATION,
1262
+ credentialPath: () => credentialPath,
1263
+ readStoredCredential: () => readStoredCredential,
1264
+ writeStoredCredential: () => writeStoredCredential
1265
+ });
1266
+ import { homedir as homedir3 } from "node:os";
1267
+ import { join as join5, dirname as dirname4 } from "node:path";
1268
+ import {
1269
+ existsSync as existsSync3,
1270
+ mkdirSync as mkdirSync2,
1271
+ readFileSync as readFileSync5,
1272
+ writeFileSync as writeFileSync2,
1273
+ chmodSync as chmodSync2,
1274
+ rmSync
1275
+ } from "node:fs";
1276
+ function credentialPath(env = process.env) {
1277
+ const override = env["VO_MCP_CREDENTIALS_PATH"]?.trim();
1278
+ if (override) return override;
1279
+ return join5(homedir3(), ".config", "vo-mcp", "credentials.json");
1280
+ }
1281
+ function keychainEnabled(env, keychain) {
1282
+ const disabled = (env["VO_MCP_DISABLE_KEYCHAIN"] ?? "").trim().toLowerCase();
1283
+ if (disabled === "1" || disabled === "true" || disabled === "yes") return false;
1284
+ return keychain.available();
1285
+ }
1286
+ function deserialize(raw) {
1287
+ try {
1288
+ const parsed = JSON.parse(raw);
1289
+ const refresh = typeof parsed.refresh_token === "string" ? parsed.refresh_token.trim() : "";
1290
+ const apiKey = typeof parsed.api_key === "string" ? parsed.api_key.trim() : "";
1291
+ const voCred = typeof parsed.vo_credential === "string" ? parsed.vo_credential.trim() : "";
1292
+ if (!voCred && (!refresh || !apiKey)) return null;
1293
+ return {
1294
+ ...refresh ? { refresh_token: refresh } : {},
1295
+ ...apiKey ? { api_key: apiKey } : {},
1296
+ ...voCred ? { vo_credential: voCred } : {},
1297
+ ...typeof parsed.vo_credential_expires_at === "string" ? { vo_credential_expires_at: parsed.vo_credential_expires_at } : {},
1298
+ ...typeof parsed.email === "string" ? { email: parsed.email } : {},
1299
+ ...typeof parsed.stored_at === "string" ? { stored_at: parsed.stored_at } : {}
1300
+ };
1301
+ } catch {
1302
+ return null;
1303
+ }
1304
+ }
1305
+ function readFromFile(env) {
1306
+ try {
1307
+ const p = credentialPath(env);
1308
+ if (!existsSync3(p)) return null;
1309
+ return deserialize(readFileSync5(p, "utf8"));
1310
+ } catch {
1311
+ return null;
1312
+ }
1313
+ }
1314
+ function readStoredCredential(env = process.env, keychain = realKeychain) {
1315
+ if (keychainEnabled(env, keychain)) {
1316
+ const raw = keychain.get();
1317
+ const fromKeychain = raw ? deserialize(raw) : null;
1318
+ if (fromKeychain) return fromKeychain;
1319
+ }
1320
+ return readFromFile(env);
1321
+ }
1322
+ function deleteFile(env) {
1323
+ try {
1324
+ rmSync(credentialPath(env), { force: true });
1325
+ } catch {
1326
+ }
1327
+ }
1328
+ function writeToFile(payload, env) {
1329
+ const p = credentialPath(env);
1330
+ mkdirSync2(dirname4(p), { recursive: true });
1331
+ writeFileSync2(p, `${JSON.stringify(payload, null, 2)}
1332
+ `, { mode: 384 });
1333
+ try {
1334
+ chmodSync2(p, 384);
1335
+ } catch {
1336
+ }
1337
+ return p;
1338
+ }
1339
+ function writeStoredCredential(cred, storedAt, env = process.env, keychain = realKeychain) {
1340
+ const payload = {
1341
+ ...cred.refresh_token ? { refresh_token: cred.refresh_token } : {},
1342
+ ...cred.api_key ? { api_key: cred.api_key } : {},
1343
+ ...cred.vo_credential ? { vo_credential: cred.vo_credential } : {},
1344
+ ...cred.vo_credential_expires_at ? { vo_credential_expires_at: cred.vo_credential_expires_at } : {},
1345
+ ...cred.email ? { email: cred.email } : {},
1346
+ stored_at: cred.stored_at ?? storedAt
1347
+ };
1348
+ if (keychainEnabled(env, keychain) && keychain.set(JSON.stringify(payload))) {
1349
+ deleteFile(env);
1350
+ return KEYCHAIN_LOCATION;
1351
+ }
1352
+ const p = writeToFile(payload, env);
1353
+ if (keychainEnabled(env, keychain)) keychain.delete();
1354
+ return p;
1355
+ }
1356
+ var realKeychain, KEYCHAIN_LOCATION;
1357
+ var init_credential_store = __esm({
1358
+ "src/cloud/credential-store.ts"() {
1359
+ "use strict";
1360
+ init_keychain();
1361
+ realKeychain = {
1362
+ available: keychainAvailable,
1363
+ get: keychainGet,
1364
+ set: keychainSet,
1365
+ delete: keychainDelete
1366
+ };
1367
+ KEYCHAIN_LOCATION = 'OS keychain (service "vo-mcp")';
1368
+ }
1369
+ });
1370
+
1371
+ // src/tools/memory/sync-config.ts
1372
+ var sync_config_exports = {};
1373
+ __export(sync_config_exports, {
1374
+ TOOL_NAME: () => TOOL_NAME22,
1375
+ deriveProjectSlug: () => deriveProjectSlug,
1376
+ description: () => description22,
1377
+ getMemoryDir: () => getMemoryDir,
1378
+ handleSyncConfig: () => handleSyncConfig,
1379
+ inputSchema: () => inputSchema22,
1380
+ isNoopSyncReason: () => isNoopSyncReason,
1381
+ runMemorySync: () => runMemorySync
1382
+ });
1383
+ import { homedir as homedir5 } from "node:os";
1384
+ import { join as join7 } from "node:path";
1385
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, readdirSync as readdirSync4 } from "node:fs";
1386
+ function isToolInput22(v) {
1387
+ if (typeof v !== "object" || v === null) return false;
1388
+ const o = v;
1389
+ if (o["action"] !== "pull" && o["action"] !== "push") return false;
1390
+ if (o["cwd"] !== void 0 && typeof o["cwd"] !== "string") return false;
1391
+ return true;
1392
+ }
1393
+ function deriveProjectSlug(cwd) {
1394
+ return cwd.replace(/\\/g, "/").replace(/\/+$/g, "").replace(/^([a-zA-Z]):/, (_m, drive) => `${drive.toUpperCase()}:`).replace(/[^a-zA-Z0-9]/g, "-");
1395
+ }
1396
+ function getMemoryDir(cwd) {
1397
+ const slug = deriveProjectSlug(cwd);
1398
+ return join7(homedir5(), ".claude", "projects", slug, "memory");
1399
+ }
1400
+ async function pullMemory(controlPlaneUrl, token, memoryDir, fetchFn) {
1401
+ const url = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
1402
+ const response = await fetchFn(url, {
1403
+ method: "GET",
1404
+ headers: {
1405
+ authorization: `Bearer ${token}`
1406
+ }
1407
+ });
1408
+ if (response.status !== 200) {
1409
+ const text = await response.text();
1410
+ throw new Error(`GET /api/v1/agent-config/memory/me returned HTTP ${response.status}: ${text.slice(0, 200)}`);
1411
+ }
1412
+ const data = JSON.parse(await response.text());
1413
+ if (!data.ok || !Array.isArray(data.entries)) {
1414
+ throw new Error("GET /api/v1/agent-config/memory/me response missing ok=true or entries array");
1415
+ }
1416
+ mkdirSync4(memoryDir, { recursive: true });
1417
+ const files = [];
1418
+ for (const entry of data.entries) {
1419
+ const filePath = join7(memoryDir, entry.file_name);
1420
+ writeFileSync3(filePath, entry.content, "utf8");
1421
+ files.push(entry.file_name);
1422
+ }
1423
+ return { pulled: data.entries.length, files };
1424
+ }
1425
+ async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
1426
+ if (!existsSync5(memoryDir)) {
1427
+ return { pushed: 0, created: 0, updated: 0 };
1428
+ }
1429
+ const localFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
1430
+ file_name: f,
1431
+ content: readFileSync7(join7(memoryDir, f), "utf8"),
1432
+ entry_type: f === "MEMORY.md" ? "index" : "topic"
1433
+ }));
1434
+ if (localFiles.length === 0) {
1435
+ return { pushed: 0, created: 0, updated: 0 };
1436
+ }
1437
+ const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
1438
+ const getResponse = await fetchFn(getUrl, {
1439
+ method: "GET",
1440
+ headers: {
1441
+ authorization: `Bearer ${token}`
1442
+ }
1443
+ });
1444
+ const existingMap = /* @__PURE__ */ new Map();
1445
+ if (getResponse.status === 200) {
1446
+ const getData = JSON.parse(await getResponse.text());
1447
+ if (getData.ok && Array.isArray(getData.entries)) {
1448
+ for (const entry of getData.entries) {
1449
+ existingMap.set(entry.file_name, entry.memory_id);
1450
+ }
1451
+ }
1452
+ }
1453
+ let created = 0;
1454
+ let updated = 0;
1455
+ for (const localFile of localFiles) {
1456
+ const memoryId = existingMap.get(localFile.file_name);
1457
+ if (memoryId) {
1458
+ const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${memoryId}`;
1459
+ const updateBody = {
1460
+ content: localFile.content,
1461
+ session_id: sessionId
1462
+ };
1463
+ const updateResponse = await fetchFn(updateUrl, {
1464
+ method: "PUT",
1465
+ headers: {
1466
+ authorization: `Bearer ${token}`,
1467
+ "content-type": "application/json"
1468
+ },
1469
+ body: JSON.stringify(updateBody)
1470
+ });
1471
+ if (updateResponse.status !== 200) {
1472
+ const text = await updateResponse.text();
1473
+ throw new Error(
1474
+ `PUT /api/v1/agent-config/memory/${memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`
1475
+ );
1476
+ }
1477
+ const updateData = JSON.parse(await updateResponse.text());
1478
+ if (!updateData.ok) {
1479
+ throw new Error(`PUT /api/v1/agent-config/memory/${memoryId} returned ok=false`);
1480
+ }
1481
+ updated++;
1482
+ } else {
1483
+ const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
1484
+ const createBody = {
1485
+ entry_type: localFile.entry_type,
1486
+ file_name: localFile.file_name,
1487
+ content: localFile.content,
1488
+ session_id: sessionId
1489
+ };
1490
+ const createResponse = await fetchFn(createUrl, {
1491
+ method: "POST",
1492
+ headers: {
1493
+ authorization: `Bearer ${token}`,
1494
+ "content-type": "application/json"
1495
+ },
1496
+ body: JSON.stringify(createBody)
1497
+ });
1498
+ if (createResponse.status !== 200 && createResponse.status !== 201) {
1499
+ const text = await createResponse.text();
1500
+ throw new Error(
1501
+ `POST /api/v1/agent-config/memory/me returned HTTP ${createResponse.status}: ${text.slice(0, 200)}`
1502
+ );
1503
+ }
1504
+ const createData = JSON.parse(await createResponse.text());
1505
+ if (!createData.ok) {
1506
+ throw new Error("POST /api/v1/agent-config/memory/me returned ok=false");
1507
+ }
1508
+ created++;
1509
+ }
1510
+ }
1511
+ return { pushed: localFiles.length, created, updated };
1512
+ }
1513
+ function isNoopSyncReason(reason) {
1514
+ if (!reason) return false;
1515
+ return /not set|No auth configured|Failed to obtain auth token/.test(reason);
1516
+ }
1517
+ async function runMemorySync(action, cwd, sessionId, fetchFn = globalThis.fetch) {
1518
+ const controlPlaneUrl = process.env["VO_CONTROL_PLANE_URL"];
1519
+ if (!controlPlaneUrl) {
1520
+ return { synced: false, reason: "VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled" };
1521
+ }
1522
+ const { createAuthTokenSourceFromEnv: createAuthTokenSourceFromEnv2 } = await Promise.resolve().then(() => (init_auth_token_source(), auth_token_source_exports));
1523
+ const { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
1524
+ const tokenSource = createAuthTokenSourceFromEnv2(process.env, fetchFn, () => readStoredCredential2(process.env));
1525
+ if (!tokenSource) {
1526
+ return { synced: false, reason: "No auth configured. Run `vo-mcp login` to authenticate as an operator." };
1527
+ }
1528
+ const token = await tokenSource.getToken();
1529
+ if (!token) {
1530
+ return { synced: false, reason: "Failed to obtain auth token. Run `vo-mcp login` to re-authenticate." };
1531
+ }
1532
+ const memoryDir = getMemoryDir(cwd);
1533
+ const baseUrl = controlPlaneUrl.replace(/\/+$/, "");
1534
+ try {
1535
+ if (action === "pull") {
1536
+ const result2 = await pullMemory(baseUrl, token, memoryDir, fetchFn);
1537
+ return { synced: true, action: "pull", pulled: result2.pulled, files: result2.files, memory_dir: memoryDir };
1538
+ }
1539
+ const result = await pushMemory(baseUrl, token, memoryDir, sessionId, fetchFn);
1540
+ return {
1541
+ synced: true,
1542
+ action: "push",
1543
+ pushed: result.pushed,
1544
+ created: result.created,
1545
+ updated: result.updated,
1546
+ memory_dir: memoryDir
1547
+ };
1548
+ } catch (err) {
1549
+ const message = err instanceof Error ? err.message : String(err);
1550
+ return { synced: false, reason: `Sync failed: ${message}` };
1551
+ }
1552
+ }
1553
+ async function handleSyncConfig(deps, rawInput, _signal, fetchFn = globalThis.fetch) {
1554
+ if (!isToolInput22(rawInput)) {
1555
+ throw invalidParams(
1556
+ TOOL_NAME22,
1557
+ 'invalid input. Required: { action: "pull" | "push" }. Optional: { cwd: "<path>" }.'
1558
+ );
1559
+ }
1560
+ const cwd = rawInput.cwd?.trim() || process.cwd();
1561
+ const result = await runMemorySync(rawInput.action, cwd, deps.session.sessionId, fetchFn);
1562
+ return jsonContent({ tool: TOOL_NAME22, schema_version: 1, payload: result });
1563
+ }
1564
+ var TOOL_NAME22, inputSchema22, description22;
1565
+ var init_sync_config = __esm({
1566
+ "src/tools/memory/sync-config.ts"() {
1567
+ "use strict";
1568
+ init_common();
1569
+ TOOL_NAME22 = "vo_sync_config";
1570
+ inputSchema22 = {
1571
+ type: "object",
1572
+ properties: {
1573
+ action: {
1574
+ type: "string",
1575
+ enum: ["pull", "push"],
1576
+ description: "pull: download cloud memory to local files. push: upload local files to cloud."
1577
+ },
1578
+ cwd: {
1579
+ type: "string",
1580
+ description: "Working directory to derive project slug from (default: process.cwd())."
1581
+ }
1582
+ },
1583
+ required: ["action"],
1584
+ additionalProperties: false
1585
+ };
1586
+ 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.";
1587
+ }
1588
+ });
1589
+
1590
+ // src/cli.ts
1591
+ import { homedir as homedir6, hostname } from "node:os";
1592
+ import { randomUUID as randomUUID5 } from "node:crypto";
1593
+ import { join as join8 } from "node:path";
1594
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1595
+
1596
+ // src/server.ts
1597
+ init_common();
1598
+ import { randomUUID as randomUUID2 } from "node:crypto";
1599
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1600
+ import {
1601
+ CallToolRequestSchema,
1602
+ ListToolsRequestSchema
1603
+ } from "@modelcontextprotocol/sdk/types.js";
1212
1604
 
1213
1605
  // src/modes/local.ts
1214
1606
  function createLocalMode() {
@@ -1223,6 +1615,7 @@ function createLocalMode() {
1223
1615
  }
1224
1616
 
1225
1617
  // src/tools/check-assertion-strength.ts
1618
+ init_common();
1226
1619
  var TOOL_NAME = "vo_check_assertion_strength";
1227
1620
  var GATE_TYPE = "ratchet";
1228
1621
  var MAX_SOURCE_BYTES = 512 * 1024;
@@ -1319,7 +1712,11 @@ async function handleCheckAssertionStrength(deps, rawInput, _signal) {
1319
1712
  return jsonContent(envelope);
1320
1713
  }
1321
1714
 
1715
+ // src/tools/check-hollow-test.ts
1716
+ init_common();
1717
+
1322
1718
  // src/tools/architecture-review-kb-prefilter.ts
1719
+ init_src();
1323
1720
  var DEFAULT_STACK = [
1324
1721
  "node",
1325
1722
  "node-pnpm-monorepo",
@@ -1405,6 +1802,7 @@ function formatRulesForPrompt(hits, truncated, domainLabel = "ARCHITECTURAL") {
1405
1802
  }
1406
1803
 
1407
1804
  // src/tools/kb-metadata-prefilter.ts
1805
+ init_src();
1408
1806
  function findRulesByMetadata(opts) {
1409
1807
  if (opts.category === void 0 && opts.tagsAny === void 0) {
1410
1808
  return {
@@ -1598,6 +1996,7 @@ async function handleCheckHollowTest(deps, rawInput, signal) {
1598
1996
  }
1599
1997
 
1600
1998
  // src/tools/verify-answer.ts
1999
+ init_common();
1601
2000
  var TOOL_NAME3 = "vo_verify_answer";
1602
2001
  var SHALLOW_GATE = "mid-exec-verify";
1603
2002
  var DEEP_GATE = "final-deep-verify";
@@ -1779,6 +2178,9 @@ async function handleVerifyAnswer(deps, rawInput, signal) {
1779
2178
  return jsonContent(envelope);
1780
2179
  }
1781
2180
 
2181
+ // src/tools/consensus-judgment.ts
2182
+ init_common();
2183
+
1782
2184
  // src/consensus/gate-types.ts
1783
2185
  var LEGACY_GATE_TYPES = [
1784
2186
  "test_assertion",
@@ -2036,6 +2438,8 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
2036
2438
  ...engineResult.synthesized_verdict.confidence_badge !== void 0 ? { confidence_badge: engineResult.synthesized_verdict.confidence_badge } : {},
2037
2439
  // Feature 1 (agreement-gate) — fan-out diagnostics (present iff the gate ran).
2038
2440
  ...engineResult.fan_out_diagnostics !== void 0 ? { fan_out_diagnostics: engineResult.fan_out_diagnostics } : {},
2441
+ // Stage A7-shadow — adaptive-vs-incumbent comparison (PII-free; present iff shadow on).
2442
+ ...engineResult.shadow_synthesis !== void 0 ? { shadow_synthesis: engineResult.shadow_synthesis } : {},
2039
2443
  // Source-grounded Tier-4 outputs (present iff the call was source-grounded).
2040
2444
  ...engineResult.source_grounded === true ? { source_grounded: true } : {},
2041
2445
  ...engineResult.citation_grade !== void 0 ? { citation_grade: engineResult.citation_grade } : {},
@@ -2056,6 +2460,8 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
2056
2460
  }
2057
2461
 
2058
2462
  // src/tools/architecture-review.ts
2463
+ init_common();
2464
+ init_events_writer();
2059
2465
  var TOOL_NAME5 = "vo_architecture_review";
2060
2466
  var GATE_TYPE3 = "architecture-review";
2061
2467
  var MAX_DIFF_BYTES = 1024 * 1024;
@@ -3300,6 +3706,7 @@ function partitionByAllowlist(findings, allowlist) {
3300
3706
  }
3301
3707
 
3302
3708
  // src/tools/check-ratchets.ts
3709
+ init_common();
3303
3710
  var TOOL_NAME6 = "vo_check_ratchets";
3304
3711
  var GATE_TYPE4 = "ratchet";
3305
3712
  var ALL_RATCHET_IDS = [
@@ -3432,6 +3839,7 @@ function buildSummary(report) {
3432
3839
  }
3433
3840
 
3434
3841
  // src/tools/decompose-dispatch.ts
3842
+ init_common();
3435
3843
  var TOOL_NAME7 = "vo_decompose_dispatch";
3436
3844
  var GATE_TYPE5 = "plan-review";
3437
3845
  var MAX_GOAL_BYTES = 32 * 1024;
@@ -3709,6 +4117,9 @@ Produce the JSON dispatch plan now.`;
3709
4117
  return jsonContent(envelope);
3710
4118
  }
3711
4119
 
4120
+ // src/tools/heal/trigger-heal.ts
4121
+ init_common();
4122
+
3712
4123
  // src/cloud/admin-callable-client.ts
3713
4124
  init_auth_token_source();
3714
4125
  init_credential_store();
@@ -3838,6 +4249,7 @@ function buildAdminCallableClientFromEnv(env = process.env) {
3838
4249
  }
3839
4250
 
3840
4251
  // src/tools/cloud-call.ts
4252
+ init_common();
3841
4253
  var ADMIN_READONLY_GATE_REASON = "admin callables are in read-only mode (VO_ADMIN_CALLABLES_READONLY) \u2014 this write tool is gated to its stub. Unset VO_ADMIN_CALLABLES_READONLY to enable write tools.";
3842
4254
  async function buildCloudOrStubResponse(args) {
3843
4255
  const inputJson = JSON.stringify(args.normalizedInput);
@@ -3911,6 +4323,7 @@ async function buildCloudOrStubResponse(args) {
3911
4323
  }
3912
4324
 
3913
4325
  // src/tools/heal/common-heal.ts
4326
+ init_common();
3914
4327
  var HEAL_STUB_REASON = 'cloud-mode not yet wired; tool surface is live, admin-callable wiring pending vo-cloud-tenant-model dispatch (see packages/vo-mcp/src/modes/cloud.ts + EXTRACTION_AUDIT.md "Stub remaining")';
3915
4328
  var HEAL_GATE_TYPE = "admin-action";
3916
4329
 
@@ -3970,6 +4383,7 @@ async function handleTriggerHeal(deps, rawInput, _signal) {
3970
4383
  }
3971
4384
 
3972
4385
  // src/tools/heal/fix-retry.ts
4386
+ init_common();
3973
4387
  var TOOL_NAME9 = "vo_fix_retry";
3974
4388
  var MAX_BATCH = 50;
3975
4389
  var ADMIN_PATH_SINGLE = "/api/v1/admin/heal/retry-attempt";
@@ -4062,6 +4476,7 @@ async function handleFixRetry(deps, rawInput, _signal) {
4062
4476
  }
4063
4477
 
4064
4478
  // src/tools/heal/fix-clear.ts
4479
+ init_common();
4065
4480
  var TOOL_NAME10 = "vo_fix_clear";
4066
4481
  var CALLABLE_NAME2 = "voClearFixAttempt";
4067
4482
  var ADMIN_PATH2 = "/api/v1/admin/heal/clear-attempt";
@@ -4105,6 +4520,7 @@ async function handleFixClear(deps, rawInput, _signal) {
4105
4520
  }
4106
4521
 
4107
4522
  // src/tools/heal/stop-workflow.ts
4523
+ init_common();
4108
4524
  var TOOL_NAME11 = "vo_stop_workflow";
4109
4525
  var CALLABLE_NAME3 = "voStopWorkflow";
4110
4526
  var ADMIN_PATH3 = "/api/v1/admin/workflow/stop";
@@ -4160,6 +4576,7 @@ async function handleStopWorkflow(deps, rawInput, _signal) {
4160
4576
  }
4161
4577
 
4162
4578
  // src/tools/heal/get-workflow-runs.ts
4579
+ init_common();
4163
4580
  var TOOL_NAME12 = "vo_get_workflow_runs";
4164
4581
  var CALLABLE_NAME4 = "voGetWorkflowRuns";
4165
4582
  var ADMIN_PATH4 = "/api/v1/admin/workflow/runs";
@@ -4190,7 +4607,11 @@ async function handleGetWorkflowRuns(deps, rawInput, _signal) {
4190
4607
  });
4191
4608
  }
4192
4609
 
4610
+ // src/tools/pr/list-pending-prs.ts
4611
+ init_common();
4612
+
4193
4613
  // src/tools/pr/common-pr.ts
4614
+ init_common();
4194
4615
  var PR_STUB_REASON = 'cloud-mode not yet wired; tool surface is live, admin-callable wiring pending vo-cloud-tenant-model dispatch (see packages/vo-mcp/src/modes/cloud.ts + EXTRACTION_AUDIT.md "Stub remaining")';
4195
4616
  var PR_GATE_TYPE = "admin-action";
4196
4617
 
@@ -4225,6 +4646,7 @@ async function handleListPendingPRs(deps, rawInput, _signal) {
4225
4646
  }
4226
4647
 
4227
4648
  // src/tools/pr/merge-pr.ts
4649
+ init_common();
4228
4650
  var TOOL_NAME14 = "vo_merge_pr";
4229
4651
  var CALLABLE_NAME6 = "voMergePR";
4230
4652
  var ADMIN_PATH6 = "/api/v1/admin/pr/merge";
@@ -4269,6 +4691,7 @@ async function handleMergePR(deps, rawInput, _signal) {
4269
4691
  }
4270
4692
 
4271
4693
  // src/tools/pr/reject-pr.ts
4694
+ init_common();
4272
4695
  var TOOL_NAME15 = "vo_reject_pr";
4273
4696
  var CALLABLE_NAME7 = "voRejectPR";
4274
4697
  var ADMIN_PATH7 = "/api/v1/admin/pr/reject";
@@ -4313,6 +4736,7 @@ async function handleRejectPR(deps, rawInput, _signal) {
4313
4736
  }
4314
4737
 
4315
4738
  // src/tools/pr/approve-all-fixes.ts
4739
+ init_common();
4316
4740
  var TOOL_NAME16 = "vo_approve_all_fixes";
4317
4741
  var CALLABLE_NAME8 = "voApproveAllFixes";
4318
4742
  var ADMIN_PATH8 = "/api/v1/admin/pr/approve-all";
@@ -4341,6 +4765,7 @@ async function handleApproveAllFixes(deps, rawInput, _signal) {
4341
4765
  }
4342
4766
 
4343
4767
  // src/tools/pr/reject-and-retry.ts
4768
+ init_common();
4344
4769
  var TOOL_NAME17 = "vo_reject_and_retry";
4345
4770
  var CALLABLE_NAME9 = "voRejectAndRetry";
4346
4771
  var ADMIN_PATH9 = "/api/v1/admin/pr/reject-retry";
@@ -4385,6 +4810,7 @@ async function handleRejectAndRetry(deps, rawInput, _signal) {
4385
4810
  }
4386
4811
 
4387
4812
  // src/tools/pr/review-merge.ts
4813
+ init_common();
4388
4814
  var TOOL_NAME18 = "vo_review_merge";
4389
4815
  var LIST_PATH = "/api/v1/admin/pr/list";
4390
4816
  var ENGINE_GATE = "final-deep-verify";
@@ -4563,6 +4989,9 @@ async function handleReviewMerge(deps, rawInput, signal) {
4563
4989
  });
4564
4990
  }
4565
4991
 
4992
+ // src/tools/session/report-session-state.ts
4993
+ init_common();
4994
+
4566
4995
  // src/tools/session/directive.ts
4567
4996
  var SESSION_DIRECTIVE_THRESHOLDS = {
4568
4997
  prepare_handoff_pct: 70,
@@ -4644,7 +5073,7 @@ var inputSchema19 = {
4644
5073
  required: ["operator_id", "session_id", "agent_type", "context_used_pct"],
4645
5074
  additionalProperties: false
4646
5075
  };
4647
- 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).";
5076
+ 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. Cloud-control-plane mode when VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN + VO_TENANT_ID are set; auto-allocates the session on first report so interactive agents (Claude Code, Cursor, Codex, Continue) appear on the live fleet whiteboard. Stub-local fallback when cloud config is absent or fails. The response shape stays stable across modes (`backend_mode` field in the payload tells the caller which mode produced the verdict).";
4648
5077
  function isStringArray2(v, maxItems) {
4649
5078
  if (!Array.isArray(v)) return false;
4650
5079
  if (v.length > maxItems) return false;
@@ -4672,30 +5101,62 @@ function isToolInput19(v) {
4672
5101
  function getCloudConfig() {
4673
5102
  const url = process.env["VO_CONTROL_PLANE_URL"];
4674
5103
  const token = process.env["VO_CONTROL_PLANE_ADMIN_TOKEN"];
4675
- if (!url || !token) return null;
4676
- return { url, token };
5104
+ const tenant_id = process.env["VO_TENANT_ID"];
5105
+ if (!url || !token || !tenant_id) return null;
5106
+ return { url, token, tenant_id };
4677
5107
  }
4678
5108
  async function tryCloudReportState(cloud, input) {
4679
5109
  try {
4680
- const body = {
5110
+ const reportBody = {
4681
5111
  context_used_pct: input.context_used_pct
4682
5112
  };
4683
- if (input.current_goal !== void 0) body["current_goal"] = input.current_goal;
5113
+ if (input.current_goal !== void 0) reportBody["current_goal"] = input.current_goal;
4684
5114
  if (input.recent_files_touched !== void 0) {
4685
- body["recent_files_touched"] = input.recent_files_touched;
5115
+ reportBody["recent_files_touched"] = input.recent_files_touched;
4686
5116
  }
4687
5117
  if (input.recent_tool_uses !== void 0) {
4688
- body["recent_tool_uses"] = input.recent_tool_uses;
5118
+ reportBody["recent_tool_uses"] = input.recent_tool_uses;
4689
5119
  }
4690
- const url = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;
4691
- const response = await fetch(url, {
5120
+ const reportUrl = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;
5121
+ let response = await fetch(reportUrl, {
4692
5122
  method: "POST",
4693
5123
  headers: {
4694
5124
  "Content-Type": "application/json",
4695
5125
  "Authorization": `Bearer ${cloud.token}`
4696
5126
  },
4697
- body: JSON.stringify(body)
5127
+ body: JSON.stringify(reportBody)
4698
5128
  });
5129
+ if (response.status === 404) {
5130
+ const allocateBody = {
5131
+ operator_id: input.operator_id,
5132
+ tenant_id: cloud.tenant_id,
5133
+ agent_type: input.agent_type,
5134
+ current_goal: input.current_goal ?? "Interactive session"
5135
+ };
5136
+ if (input.context_used_pct > 0) {
5137
+ allocateBody["initial_context_used_pct"] = input.context_used_pct;
5138
+ }
5139
+ const allocateUrl = `${cloud.url}/api/v1/session`;
5140
+ const allocateResponse = await fetch(allocateUrl, {
5141
+ method: "POST",
5142
+ headers: {
5143
+ "Content-Type": "application/json",
5144
+ "Authorization": `Bearer ${cloud.token}`
5145
+ },
5146
+ body: JSON.stringify(allocateBody)
5147
+ });
5148
+ if (!allocateResponse.ok) {
5149
+ return null;
5150
+ }
5151
+ response = await fetch(reportUrl, {
5152
+ method: "POST",
5153
+ headers: {
5154
+ "Content-Type": "application/json",
5155
+ "Authorization": `Bearer ${cloud.token}`
5156
+ },
5157
+ body: JSON.stringify(reportBody)
5158
+ });
5159
+ }
4699
5160
  if (!response.ok) {
4700
5161
  return null;
4701
5162
  }
@@ -4751,6 +5212,7 @@ async function handleReportSessionState(deps, rawInput, _signal) {
4751
5212
  }
4752
5213
 
4753
5214
  // src/tools/session/spawn-successor.ts
5215
+ init_common();
4754
5216
  import { spawn } from "node:child_process";
4755
5217
  import { homedir as homedir4 } from "node:os";
4756
5218
  import { join as join6 } from "node:path";
@@ -4885,6 +5347,9 @@ async function handleSpawnSuccessor(_deps, rawInput, _signal, spawnImpl = spawn)
4885
5347
  });
4886
5348
  }
4887
5349
 
5350
+ // src/tools/concierge/dispatch.ts
5351
+ init_common();
5352
+
4888
5353
  // src/tools/concierge/common-concierge.ts
4889
5354
  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.";
4890
5355
  var CONCIERGE_GATE_TYPE = "concierge-dispatch";
@@ -4966,255 +5431,8 @@ async function handleConciergeDispatch(deps, rawInput, _signal) {
4966
5431
  });
4967
5432
  }
4968
5433
 
4969
- // src/tools/memory/sync-config.ts
4970
- import { homedir as homedir5 } from "node:os";
4971
- import { join as join7 } from "node:path";
4972
- import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, readdirSync as readdirSync4 } from "node:fs";
4973
- var TOOL_NAME22 = "vo_sync_config";
4974
- var inputSchema22 = {
4975
- type: "object",
4976
- properties: {
4977
- action: {
4978
- type: "string",
4979
- enum: ["pull", "push"],
4980
- description: "pull: download cloud memory to local files. push: upload local files to cloud."
4981
- },
4982
- cwd: {
4983
- type: "string",
4984
- description: "Working directory to derive project slug from (default: process.cwd())."
4985
- }
4986
- },
4987
- required: ["action"],
4988
- additionalProperties: false
4989
- };
4990
- 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.";
4991
- function isToolInput22(v) {
4992
- if (typeof v !== "object" || v === null) return false;
4993
- const o = v;
4994
- if (o["action"] !== "pull" && o["action"] !== "push") return false;
4995
- if (o["cwd"] !== void 0 && typeof o["cwd"] !== "string") return false;
4996
- return true;
4997
- }
4998
- function deriveProjectSlug(cwd) {
4999
- const normalized = cwd.replace(/\\/g, "/");
5000
- return normalized.replace(/^([A-Z]):/i, (_, drive) => `${drive.toUpperCase()}-`).replace(/\/$/g, "").split("/").join("--").replace(/\s+/g, "-");
5001
- }
5002
- function getMemoryDir(cwd) {
5003
- const slug = deriveProjectSlug(cwd);
5004
- return join7(homedir5(), ".claude", "projects", slug, "memory");
5005
- }
5006
- async function pullMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
5007
- const url = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
5008
- const response = await fetchFn(url, {
5009
- method: "GET",
5010
- headers: {
5011
- authorization: `Bearer ${token}`
5012
- }
5013
- });
5014
- if (response.status !== 200) {
5015
- const text = await response.text();
5016
- throw new Error(`GET /api/v1/agent-config/memory/me returned HTTP ${response.status}: ${text.slice(0, 200)}`);
5017
- }
5018
- const data = JSON.parse(await response.text());
5019
- if (!data.ok || !Array.isArray(data.entries)) {
5020
- throw new Error("GET /api/v1/agent-config/memory/me response missing ok=true or entries array");
5021
- }
5022
- mkdirSync4(memoryDir, { recursive: true });
5023
- const files = [];
5024
- for (const entry of data.entries) {
5025
- const filePath = join7(memoryDir, entry.file_name);
5026
- writeFileSync3(filePath, entry.content, "utf8");
5027
- files.push(entry.file_name);
5028
- }
5029
- return { pulled: data.entries.length, files };
5030
- }
5031
- async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
5032
- if (!existsSync5(memoryDir)) {
5033
- return { pushed: 0, created: 0, updated: 0 };
5034
- }
5035
- const localFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
5036
- file_name: f,
5037
- content: readFileSync7(join7(memoryDir, f), "utf8"),
5038
- entry_type: f === "MEMORY.md" ? "index" : "topic"
5039
- }));
5040
- if (localFiles.length === 0) {
5041
- return { pushed: 0, created: 0, updated: 0 };
5042
- }
5043
- const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
5044
- const getResponse = await fetchFn(getUrl, {
5045
- method: "GET",
5046
- headers: {
5047
- authorization: `Bearer ${token}`
5048
- }
5049
- });
5050
- const existingMap = /* @__PURE__ */ new Map();
5051
- if (getResponse.status === 200) {
5052
- const getData = JSON.parse(await getResponse.text());
5053
- if (getData.ok && Array.isArray(getData.entries)) {
5054
- for (const entry of getData.entries) {
5055
- existingMap.set(entry.file_name, entry.memory_id);
5056
- }
5057
- }
5058
- }
5059
- let created = 0;
5060
- let updated = 0;
5061
- for (const localFile of localFiles) {
5062
- const memoryId = existingMap.get(localFile.file_name);
5063
- if (memoryId) {
5064
- const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${memoryId}`;
5065
- const updateBody = {
5066
- content: localFile.content,
5067
- session_id: sessionId
5068
- };
5069
- const updateResponse = await fetchFn(updateUrl, {
5070
- method: "PUT",
5071
- headers: {
5072
- authorization: `Bearer ${token}`,
5073
- "content-type": "application/json"
5074
- },
5075
- body: JSON.stringify(updateBody)
5076
- });
5077
- if (updateResponse.status !== 200) {
5078
- const text = await updateResponse.text();
5079
- throw new Error(
5080
- `PUT /api/v1/agent-config/memory/${memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`
5081
- );
5082
- }
5083
- const updateData = JSON.parse(await updateResponse.text());
5084
- if (!updateData.ok) {
5085
- throw new Error(`PUT /api/v1/agent-config/memory/${memoryId} returned ok=false`);
5086
- }
5087
- updated++;
5088
- } else {
5089
- const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
5090
- const createBody = {
5091
- entry_type: localFile.entry_type,
5092
- file_name: localFile.file_name,
5093
- content: localFile.content,
5094
- session_id: sessionId
5095
- };
5096
- const createResponse = await fetchFn(createUrl, {
5097
- method: "POST",
5098
- headers: {
5099
- authorization: `Bearer ${token}`,
5100
- "content-type": "application/json"
5101
- },
5102
- body: JSON.stringify(createBody)
5103
- });
5104
- if (createResponse.status !== 200 && createResponse.status !== 201) {
5105
- const text = await createResponse.text();
5106
- throw new Error(
5107
- `POST /api/v1/agent-config/memory/me returned HTTP ${createResponse.status}: ${text.slice(0, 200)}`
5108
- );
5109
- }
5110
- const createData = JSON.parse(await createResponse.text());
5111
- if (!createData.ok) {
5112
- throw new Error("POST /api/v1/agent-config/memory/me returned ok=false");
5113
- }
5114
- created++;
5115
- }
5116
- }
5117
- return { pushed: localFiles.length, created, updated };
5118
- }
5119
- async function handleSyncConfig(deps, rawInput, _signal, fetchFn = globalThis.fetch) {
5120
- if (!isToolInput22(rawInput)) {
5121
- throw invalidParams(
5122
- TOOL_NAME22,
5123
- 'invalid input. Required: { action: "pull" | "push" }. Optional: { cwd: "<path>" }.'
5124
- );
5125
- }
5126
- const controlPlaneUrl = process.env["VO_CONTROL_PLANE_URL"];
5127
- if (!controlPlaneUrl) {
5128
- return jsonContent({
5129
- tool: TOOL_NAME22,
5130
- schema_version: 1,
5131
- payload: {
5132
- synced: false,
5133
- reason: "VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled"
5134
- }
5135
- });
5136
- }
5137
- const { createAuthTokenSourceFromEnv: createAuthTokenSourceFromEnv2 } = await Promise.resolve().then(() => (init_auth_token_source(), auth_token_source_exports));
5138
- const { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
5139
- const tokenSource = createAuthTokenSourceFromEnv2(process.env, fetchFn, () => readStoredCredential2(process.env));
5140
- if (!tokenSource) {
5141
- return jsonContent({
5142
- tool: TOOL_NAME22,
5143
- schema_version: 1,
5144
- payload: {
5145
- synced: false,
5146
- reason: "No auth configured. Run `vo-mcp login` to authenticate as an operator."
5147
- }
5148
- });
5149
- }
5150
- const token = await tokenSource.getToken();
5151
- if (!token) {
5152
- return jsonContent({
5153
- tool: TOOL_NAME22,
5154
- schema_version: 1,
5155
- payload: {
5156
- synced: false,
5157
- reason: "Failed to obtain auth token. Run `vo-mcp login` to re-authenticate."
5158
- }
5159
- });
5160
- }
5161
- const cwd = rawInput.cwd?.trim() || process.cwd();
5162
- const memoryDir = getMemoryDir(cwd);
5163
- try {
5164
- if (rawInput.action === "pull") {
5165
- const result = await pullMemory(
5166
- controlPlaneUrl.replace(/\/+$/, ""),
5167
- token,
5168
- memoryDir,
5169
- deps.sessionId,
5170
- fetchFn
5171
- );
5172
- return jsonContent({
5173
- tool: TOOL_NAME22,
5174
- schema_version: 1,
5175
- payload: {
5176
- synced: true,
5177
- action: "pull",
5178
- pulled: result.pulled,
5179
- files: result.files,
5180
- memory_dir: memoryDir
5181
- }
5182
- });
5183
- } else {
5184
- const result = await pushMemory(
5185
- controlPlaneUrl.replace(/\/+$/, ""),
5186
- token,
5187
- memoryDir,
5188
- deps.sessionId,
5189
- fetchFn
5190
- );
5191
- return jsonContent({
5192
- tool: TOOL_NAME22,
5193
- schema_version: 1,
5194
- payload: {
5195
- synced: true,
5196
- action: "push",
5197
- pushed: result.pushed,
5198
- created: result.created,
5199
- updated: result.updated,
5200
- memory_dir: memoryDir
5201
- }
5202
- });
5203
- }
5204
- } catch (err) {
5205
- const message = err instanceof Error ? err.message : String(err);
5206
- return jsonContent({
5207
- tool: TOOL_NAME22,
5208
- schema_version: 1,
5209
- payload: {
5210
- synced: false,
5211
- reason: `Sync failed: ${message}`
5212
- }
5213
- });
5214
- }
5215
- }
5216
-
5217
5434
  // src/server.ts
5435
+ init_sync_config();
5218
5436
  function buildToolRegistry() {
5219
5437
  return {
5220
5438
  [TOOL_NAME]: {
@@ -5569,6 +5787,9 @@ function createSqliteCache(options) {
5569
5787
  };
5570
5788
  }
5571
5789
 
5790
+ // src/cli.ts
5791
+ init_events_writer();
5792
+
5572
5793
  // src/ratchets/stub-client.ts
5573
5794
  var HOLLOW_PATTERNS = [
5574
5795
  {
@@ -5656,6 +5877,8 @@ function buildSummary2(args) {
5656
5877
  }
5657
5878
 
5658
5879
  // src/consensus/engine-client.ts
5880
+ init_events_writer();
5881
+ init_common();
5659
5882
  import { randomUUID as randomUUID3 } from "node:crypto";
5660
5883
 
5661
5884
  // src/consensus/null-client.ts
@@ -5724,6 +5947,25 @@ function mapFanOutDiagnostics(fd) {
5724
5947
  refused: fd.refused
5725
5948
  };
5726
5949
  }
5950
+ var SHADOW_SYNTHESIS_ENV_VAR = "VO_CONSENSUS_SHADOW";
5951
+ function shadowEnabled(env) {
5952
+ const raw = (env ?? {})[SHADOW_SYNTHESIS_ENV_VAR];
5953
+ if (raw === void 0) return true;
5954
+ const norm = raw.trim().toLowerCase();
5955
+ return !(norm === "0" || norm === "false" || norm === "no" || norm === "off" || norm === "");
5956
+ }
5957
+ function mapShadowSynthesis(s) {
5958
+ if (s === void 0) return void 0;
5959
+ return {
5960
+ incumbent: { verdict: s.incumbent.verdict, confidence: s.incumbent.confidence, synthesizer: s.incumbent.synthesizer },
5961
+ adaptive: {
5962
+ verdict: s.adaptive.verdict,
5963
+ confidence: s.adaptive.confidence,
5964
+ ...s.adaptive.calibrated_confidence !== void 0 ? { calibrated_confidence: s.adaptive.calibrated_confidence } : {}
5965
+ },
5966
+ agree: s.agree
5967
+ };
5968
+ }
5727
5969
  function mapCitationGrade(cg) {
5728
5970
  if (cg === void 0) return void 0;
5729
5971
  return {
@@ -5864,7 +6106,12 @@ function createEngineConsensusClient(options) {
5864
6106
  const engineOptions = {
5865
6107
  panel,
5866
6108
  ...options.per_model_timeout_ms !== void 0 ? { per_model_timeout_ms: options.per_model_timeout_ms } : {},
5867
- ...agreementGate !== void 0 ? { agreement_gate: agreementGate } : {}
6109
+ ...agreementGate !== void 0 ? { agreement_gate: agreementGate } : {},
6110
+ // Stage A7-shadow: run the adaptive verdict alongside the live one for grading.
6111
+ // Cheap (pure log-odds over already-fetched verdicts; no extra model calls),
6112
+ // PII-free, and never alters the live verdict. ON by default; kill with
6113
+ // VO_CONSENSUS_SHADOW=0. Cold-start has no skill registry → neutral priors.
6114
+ shadow_synthesis: { enabled: shadowEnabled(options.env) }
5868
6115
  };
5869
6116
  const sources = request.source_urls;
5870
6117
  const useSourceGrounded = sources !== void 0 && sources.length > 0 && typeof engine.runSourceGroundedConsensus === "function";
@@ -5920,6 +6167,8 @@ function createEngineConsensusClient(options) {
5920
6167
  ...sourceExtras?.escalation_reason !== void 0 ? { escalation_reason: sourceExtras.escalation_reason } : response.escalation_reason !== void 0 ? { escalation_reason: response.escalation_reason } : {},
5921
6168
  // Feature 1 (agreement-gate) — fan-out diagnostics (additive telemetry).
5922
6169
  ...mapFanOutDiagnostics(response.fan_out_diagnostics) !== void 0 ? { fan_out_diagnostics: mapFanOutDiagnostics(response.fan_out_diagnostics) } : {},
6170
+ // Stage A7-shadow — adaptive-vs-incumbent comparison (PII-free; present iff shadow on).
6171
+ ...mapShadowSynthesis(response.shadow_synthesis) !== void 0 ? { shadow_synthesis: mapShadowSynthesis(response.shadow_synthesis) } : {},
5923
6172
  // Source-grounded additive outputs (Tier-4 features).
5924
6173
  ...useSourceGrounded ? { source_grounded: true } : {},
5925
6174
  ...sourceExtras?.citation_grade !== void 0 ? { citation_grade: sourceExtras.citation_grade } : {},
@@ -6354,6 +6603,7 @@ async function exchangeForVoCredential(opts) {
6354
6603
  }
6355
6604
 
6356
6605
  // src/cli.ts
6606
+ init_common();
6357
6607
  function defaultCacheDbPath() {
6358
6608
  const env = process.env["VO_MCP_DB_PATH"];
6359
6609
  if (env && env.length > 0) return env;
@@ -6486,6 +6736,31 @@ if (process.argv[2] === "login") {
6486
6736
  console.error("[vo-mcp] login failed:", err instanceof Error ? err.message : String(err));
6487
6737
  process.exit(1);
6488
6738
  });
6739
+ } else if (process.argv[2] === "sync") {
6740
+ const action = process.argv[3];
6741
+ if (action !== "push" && action !== "pull") {
6742
+ console.error("[vo-mcp] usage: vo-mcp sync <push|pull> [--cwd <path>]");
6743
+ process.exit(2);
6744
+ }
6745
+ const cwdFlag = process.argv.indexOf("--cwd");
6746
+ const cwd = cwdFlag >= 0 && typeof process.argv[cwdFlag + 1] === "string" ? process.argv[cwdFlag + 1] : process.cwd();
6747
+ const sessionId = randomUUID5();
6748
+ Promise.resolve().then(() => (init_sync_config(), sync_config_exports)).then(async ({ runMemorySync: runMemorySync2, isNoopSyncReason: isNoopSyncReason2 }) => {
6749
+ const r = await runMemorySync2(action, cwd, sessionId);
6750
+ if (r.synced) {
6751
+ console.error(`[vo-mcp] sync ${action} ok: ${JSON.stringify(r)}`);
6752
+ process.exit(0);
6753
+ }
6754
+ if (isNoopSyncReason2(r.reason)) {
6755
+ console.error(`[vo-mcp] sync ${action} skipped: ${r.reason}`);
6756
+ process.exit(0);
6757
+ }
6758
+ console.error(`[vo-mcp] sync ${action} failed: ${r.reason}`);
6759
+ process.exit(1);
6760
+ }).catch((err) => {
6761
+ console.error("[vo-mcp] sync fatal:", err instanceof Error ? err.message : String(err));
6762
+ process.exit(1);
6763
+ });
6489
6764
  } else {
6490
6765
  main().catch((err) => {
6491
6766
  console.error("[vo-mcp] fatal:", err);