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

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
  }
@@ -1175,40 +1052,590 @@ function buildBaseEvent(args) {
1175
1052
  cache_hit: false
1176
1053
  };
1177
1054
  }
1178
- function jsonContent(value) {
1179
- return {
1180
- content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
1181
- };
1055
+ function jsonContent(value) {
1056
+ return {
1057
+ content: [{ type: "text", text: JSON.stringify(value, null, 2) }]
1058
+ };
1059
+ }
1060
+ function toEventPerModelVerdicts(src) {
1061
+ return src.map((v) => {
1062
+ const sanitizedExcerpt = sanitizeExcerpt(v.raw_response_excerpt);
1063
+ return {
1064
+ model: v.model,
1065
+ model_id: v.model,
1066
+ provider: v.provider,
1067
+ verdict: v.verdict,
1068
+ confidence: v.confidence,
1069
+ duration_ms: v.duration_ms,
1070
+ raw_response_excerpt: sanitizedExcerpt,
1071
+ // Hash over the sanitized excerpt — engine doesn't yet thread the full
1072
+ // raw response. Documented limitation on `raw_response_hash` in types.ts.
1073
+ raw_response_hash: sha256Hex(v.raw_response_excerpt),
1074
+ reasoning_summary: typeof v.reasoning_excerpt === "string" && v.reasoning_excerpt.length > 0 ? sanitizeExcerpt(v.reasoning_excerpt, 500) : null,
1075
+ // Engine doesn't yet emit cited_sources; ship empty array so cloud
1076
+ // ingestion doesn't NPE on `Array.isArray()` checks.
1077
+ cited_sources: [],
1078
+ error: v.error ?? null
1079
+ };
1080
+ });
1081
+ }
1082
+ function toEventSynthesizedVerdict(src) {
1083
+ return {
1084
+ verdict: src.verdict,
1085
+ confidence: src.confidence,
1086
+ reasoning_excerpt: sanitizeExcerpt(src.reasoning_excerpt)
1087
+ };
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
+ readStoredCredentialKeychainOnly: () => readStoredCredentialKeychainOnly,
1265
+ writeStoredCredential: () => writeStoredCredential
1266
+ });
1267
+ import { homedir as homedir3 } from "node:os";
1268
+ import { join as join5, dirname as dirname4 } from "node:path";
1269
+ import {
1270
+ existsSync as existsSync3,
1271
+ mkdirSync as mkdirSync2,
1272
+ readFileSync as readFileSync5,
1273
+ writeFileSync as writeFileSync2,
1274
+ chmodSync as chmodSync2,
1275
+ rmSync
1276
+ } from "node:fs";
1277
+ function credentialPath(env = process.env) {
1278
+ const override = env["VO_MCP_CREDENTIALS_PATH"]?.trim();
1279
+ if (override) return override;
1280
+ return join5(homedir3(), ".config", "vo-mcp", "credentials.json");
1281
+ }
1282
+ function keychainEnabled(env, keychain) {
1283
+ const disabled = (env["VO_MCP_DISABLE_KEYCHAIN"] ?? "").trim().toLowerCase();
1284
+ if (disabled === "1" || disabled === "true" || disabled === "yes") return false;
1285
+ return keychain.available();
1286
+ }
1287
+ function deserialize(raw) {
1288
+ try {
1289
+ const parsed = JSON.parse(raw);
1290
+ const refresh = typeof parsed.refresh_token === "string" ? parsed.refresh_token.trim() : "";
1291
+ const apiKey = typeof parsed.api_key === "string" ? parsed.api_key.trim() : "";
1292
+ const voCred = typeof parsed.vo_credential === "string" ? parsed.vo_credential.trim() : "";
1293
+ if (!voCred && (!refresh || !apiKey)) return null;
1294
+ return {
1295
+ ...refresh ? { refresh_token: refresh } : {},
1296
+ ...apiKey ? { api_key: apiKey } : {},
1297
+ ...voCred ? { vo_credential: voCred } : {},
1298
+ ...typeof parsed.vo_credential_expires_at === "string" ? { vo_credential_expires_at: parsed.vo_credential_expires_at } : {},
1299
+ ...typeof parsed.email === "string" ? { email: parsed.email } : {},
1300
+ ...typeof parsed.stored_at === "string" ? { stored_at: parsed.stored_at } : {}
1301
+ };
1302
+ } catch {
1303
+ return null;
1304
+ }
1305
+ }
1306
+ function readFromFile(env) {
1307
+ try {
1308
+ const p = credentialPath(env);
1309
+ if (!existsSync3(p)) return null;
1310
+ return deserialize(readFileSync5(p, "utf8"));
1311
+ } catch {
1312
+ return null;
1313
+ }
1314
+ }
1315
+ function readStoredCredential(env = process.env, keychain = realKeychain) {
1316
+ if (keychainEnabled(env, keychain)) {
1317
+ const raw = keychain.get();
1318
+ const fromKeychain = raw ? deserialize(raw) : null;
1319
+ if (fromKeychain) return fromKeychain;
1320
+ }
1321
+ return readFromFile(env);
1322
+ }
1323
+ function readStoredCredentialKeychainOnly(env = process.env, keychain = realKeychain) {
1324
+ if (!keychainEnabled(env, keychain)) return null;
1325
+ const raw = keychain.get();
1326
+ return raw ? deserialize(raw) : null;
1327
+ }
1328
+ function deleteFile(env) {
1329
+ try {
1330
+ rmSync(credentialPath(env), { force: true });
1331
+ } catch {
1332
+ }
1333
+ }
1334
+ function writeToFile(payload, env) {
1335
+ const p = credentialPath(env);
1336
+ mkdirSync2(dirname4(p), { recursive: true });
1337
+ writeFileSync2(p, `${JSON.stringify(payload, null, 2)}
1338
+ `, { mode: 384 });
1339
+ try {
1340
+ chmodSync2(p, 384);
1341
+ } catch {
1342
+ }
1343
+ return p;
1344
+ }
1345
+ function writeStoredCredential(cred, storedAt, env = process.env, keychain = realKeychain) {
1346
+ const payload = {
1347
+ ...cred.refresh_token ? { refresh_token: cred.refresh_token } : {},
1348
+ ...cred.api_key ? { api_key: cred.api_key } : {},
1349
+ ...cred.vo_credential ? { vo_credential: cred.vo_credential } : {},
1350
+ ...cred.vo_credential_expires_at ? { vo_credential_expires_at: cred.vo_credential_expires_at } : {},
1351
+ ...cred.email ? { email: cred.email } : {},
1352
+ stored_at: cred.stored_at ?? storedAt
1353
+ };
1354
+ if (keychainEnabled(env, keychain) && keychain.set(JSON.stringify(payload))) {
1355
+ deleteFile(env);
1356
+ return KEYCHAIN_LOCATION;
1357
+ }
1358
+ const p = writeToFile(payload, env);
1359
+ if (keychainEnabled(env, keychain)) keychain.delete();
1360
+ return p;
1361
+ }
1362
+ var realKeychain, KEYCHAIN_LOCATION;
1363
+ var init_credential_store = __esm({
1364
+ "src/cloud/credential-store.ts"() {
1365
+ "use strict";
1366
+ init_keychain();
1367
+ realKeychain = {
1368
+ available: keychainAvailable,
1369
+ get: keychainGet,
1370
+ set: keychainSet,
1371
+ delete: keychainDelete
1372
+ };
1373
+ KEYCHAIN_LOCATION = 'OS keychain (service "vo-mcp")';
1374
+ }
1375
+ });
1376
+
1377
+ // src/tools/memory/safe-memory-file.ts
1378
+ import { resolve, sep } from "node:path";
1379
+ function isSafeMemoryFileName(fileName) {
1380
+ return fileName.length <= 200 && fileName.trim() === fileName && !fileName.includes("/") && !fileName.includes("\\") && !fileName.includes(":") && SAFE_MEMORY_FILE_RE.test(fileName);
1381
+ }
1382
+ function resolveMemoryFilePath(memoryDir, fileName) {
1383
+ if (!isSafeMemoryFileName(fileName)) {
1384
+ throw new Error(`unsafe memory file_name: ${fileName.slice(0, 80)}`);
1385
+ }
1386
+ const root = resolve(memoryDir);
1387
+ const filePath = resolve(root, fileName);
1388
+ const rootPrefix = root.endsWith(sep) ? root : `${root}${sep}`;
1389
+ if (filePath !== root && !filePath.startsWith(rootPrefix)) {
1390
+ throw new Error(`memory file path escapes memory directory: ${fileName.slice(0, 80)}`);
1391
+ }
1392
+ return filePath;
1393
+ }
1394
+ var SAFE_MEMORY_FILE_RE;
1395
+ var init_safe_memory_file = __esm({
1396
+ "src/tools/memory/safe-memory-file.ts"() {
1397
+ "use strict";
1398
+ SAFE_MEMORY_FILE_RE = /^[A-Za-z0-9][A-Za-z0-9._ -]*\.md$/i;
1399
+ }
1400
+ });
1401
+
1402
+ // src/tools/memory/sync-config.ts
1403
+ var sync_config_exports = {};
1404
+ __export(sync_config_exports, {
1405
+ TOOL_NAME: () => TOOL_NAME22,
1406
+ deriveProjectSlug: () => deriveProjectSlug,
1407
+ description: () => description22,
1408
+ getMemoryDir: () => getMemoryDir,
1409
+ handleSyncConfig: () => handleSyncConfig,
1410
+ inputSchema: () => inputSchema22,
1411
+ isNoopSyncReason: () => isNoopSyncReason,
1412
+ runMemorySync: () => runMemorySync
1413
+ });
1414
+ import { homedir as homedir5 } from "node:os";
1415
+ import { join as join7 } from "node:path";
1416
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync7, writeFileSync as writeFileSync3, readdirSync as readdirSync4 } from "node:fs";
1417
+ function isToolInput22(v) {
1418
+ if (typeof v !== "object" || v === null) return false;
1419
+ const o = v;
1420
+ if (o["action"] !== "pull" && o["action"] !== "push") return false;
1421
+ if (o["cwd"] !== void 0 && typeof o["cwd"] !== "string") return false;
1422
+ return true;
1423
+ }
1424
+ function deriveProjectSlug(cwd) {
1425
+ return cwd.replace(/\\/g, "/").replace(/\/+$/g, "").replace(/^([a-zA-Z]):/, (_m, drive) => `${drive.toUpperCase()}:`).replace(/[^a-zA-Z0-9]/g, "-");
1426
+ }
1427
+ function getMemoryDir(cwd) {
1428
+ const slug = deriveProjectSlug(cwd);
1429
+ return join7(homedir5(), ".claude", "projects", slug, "memory");
1430
+ }
1431
+ async function pullMemory(controlPlaneUrl, token, memoryDir, fetchFn) {
1432
+ const url = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
1433
+ const response = await fetchFn(url, {
1434
+ method: "GET",
1435
+ headers: {
1436
+ authorization: `Bearer ${token}`
1437
+ }
1438
+ });
1439
+ if (response.status !== 200) {
1440
+ const text = await response.text();
1441
+ throw new Error(`GET /api/v1/agent-config/memory/me returned HTTP ${response.status}: ${text.slice(0, 200)}`);
1442
+ }
1443
+ const data = JSON.parse(await response.text());
1444
+ if (!data.ok || !Array.isArray(data.entries)) {
1445
+ throw new Error("GET /api/v1/agent-config/memory/me response missing ok=true or entries array");
1446
+ }
1447
+ const writes = data.entries.map((entry) => ({
1448
+ entry,
1449
+ filePath: resolveMemoryFilePath(memoryDir, entry.file_name)
1450
+ }));
1451
+ mkdirSync4(memoryDir, { recursive: true });
1452
+ const files = [];
1453
+ for (const { entry, filePath } of writes) {
1454
+ writeFileSync3(filePath, entry.content, "utf8");
1455
+ files.push(entry.file_name);
1456
+ }
1457
+ return { pulled: data.entries.length, files };
1458
+ }
1459
+ async function pushMemory(controlPlaneUrl, token, memoryDir, sessionId, fetchFn) {
1460
+ if (!existsSync5(memoryDir)) {
1461
+ return { pushed: 0, created: 0, updated: 0 };
1462
+ }
1463
+ const localFiles = readdirSync4(memoryDir).filter((f) => f.endsWith(".md")).map((f) => ({
1464
+ file_name: f,
1465
+ content: readFileSync7(resolveMemoryFilePath(memoryDir, f), "utf8"),
1466
+ entry_type: f === "MEMORY.md" ? "index" : "topic"
1467
+ }));
1468
+ if (localFiles.length === 0) {
1469
+ return { pushed: 0, created: 0, updated: 0 };
1470
+ }
1471
+ const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
1472
+ const getResponse = await fetchFn(getUrl, {
1473
+ method: "GET",
1474
+ headers: {
1475
+ authorization: `Bearer ${token}`
1476
+ }
1477
+ });
1478
+ const existingMap = /* @__PURE__ */ new Map();
1479
+ if (getResponse.status === 200) {
1480
+ const getData = JSON.parse(await getResponse.text());
1481
+ if (getData.ok && Array.isArray(getData.entries)) {
1482
+ for (const entry of getData.entries) {
1483
+ existingMap.set(entry.file_name, entry.memory_id);
1484
+ }
1485
+ }
1486
+ }
1487
+ let created = 0;
1488
+ let updated = 0;
1489
+ for (const localFile of localFiles) {
1490
+ const memoryId = existingMap.get(localFile.file_name);
1491
+ if (memoryId) {
1492
+ const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${memoryId}`;
1493
+ const updateBody = {
1494
+ content: localFile.content,
1495
+ session_id: sessionId
1496
+ };
1497
+ const updateResponse = await fetchFn(updateUrl, {
1498
+ method: "PUT",
1499
+ headers: {
1500
+ authorization: `Bearer ${token}`,
1501
+ "content-type": "application/json"
1502
+ },
1503
+ body: JSON.stringify(updateBody)
1504
+ });
1505
+ if (updateResponse.status !== 200) {
1506
+ const text = await updateResponse.text();
1507
+ throw new Error(
1508
+ `PUT /api/v1/agent-config/memory/${memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`
1509
+ );
1510
+ }
1511
+ const updateData = JSON.parse(await updateResponse.text());
1512
+ if (!updateData.ok) {
1513
+ throw new Error(`PUT /api/v1/agent-config/memory/${memoryId} returned ok=false`);
1514
+ }
1515
+ updated++;
1516
+ } else {
1517
+ const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;
1518
+ const createBody = {
1519
+ entry_type: localFile.entry_type,
1520
+ file_name: localFile.file_name,
1521
+ content: localFile.content,
1522
+ session_id: sessionId
1523
+ };
1524
+ const createResponse = await fetchFn(createUrl, {
1525
+ method: "POST",
1526
+ headers: {
1527
+ authorization: `Bearer ${token}`,
1528
+ "content-type": "application/json"
1529
+ },
1530
+ body: JSON.stringify(createBody)
1531
+ });
1532
+ if (createResponse.status !== 200 && createResponse.status !== 201) {
1533
+ const text = await createResponse.text();
1534
+ throw new Error(
1535
+ `POST /api/v1/agent-config/memory/me returned HTTP ${createResponse.status}: ${text.slice(0, 200)}`
1536
+ );
1537
+ }
1538
+ const createData = JSON.parse(await createResponse.text());
1539
+ if (!createData.ok) {
1540
+ throw new Error("POST /api/v1/agent-config/memory/me returned ok=false");
1541
+ }
1542
+ created++;
1543
+ }
1544
+ }
1545
+ return { pushed: localFiles.length, created, updated };
1546
+ }
1547
+ function isNoopSyncReason(reason) {
1548
+ if (!reason) return false;
1549
+ return /not set|No auth configured|Failed to obtain auth token/.test(reason);
1182
1550
  }
1183
- function toEventPerModelVerdicts(src) {
1184
- return src.map((v) => {
1185
- const sanitizedExcerpt = sanitizeExcerpt(v.raw_response_excerpt);
1551
+ async function runMemorySync(action, cwd, sessionId, fetchFn = globalThis.fetch) {
1552
+ const controlPlaneUrl = process.env["VO_CONTROL_PLANE_URL"];
1553
+ if (!controlPlaneUrl) {
1554
+ return { synced: false, reason: "VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled" };
1555
+ }
1556
+ const { createAuthTokenSourceFromEnv: createAuthTokenSourceFromEnv2 } = await Promise.resolve().then(() => (init_auth_token_source(), auth_token_source_exports));
1557
+ const { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
1558
+ const tokenSource = createAuthTokenSourceFromEnv2(process.env, fetchFn, () => readStoredCredential2(process.env));
1559
+ if (!tokenSource) {
1560
+ return { synced: false, reason: "No auth configured. Run `vo-mcp login` to authenticate as an operator." };
1561
+ }
1562
+ const token = await tokenSource.getToken();
1563
+ if (!token) {
1564
+ return { synced: false, reason: "Failed to obtain auth token. Run `vo-mcp login` to re-authenticate." };
1565
+ }
1566
+ const memoryDir = getMemoryDir(cwd);
1567
+ const baseUrl = controlPlaneUrl.replace(/\/+$/, "");
1568
+ try {
1569
+ if (action === "pull") {
1570
+ const result2 = await pullMemory(baseUrl, token, memoryDir, fetchFn);
1571
+ return { synced: true, action: "pull", pulled: result2.pulled, files: result2.files, memory_dir: memoryDir };
1572
+ }
1573
+ const result = await pushMemory(baseUrl, token, memoryDir, sessionId, fetchFn);
1186
1574
  return {
1187
- model: v.model,
1188
- model_id: v.model,
1189
- provider: v.provider,
1190
- verdict: v.verdict,
1191
- confidence: v.confidence,
1192
- duration_ms: v.duration_ms,
1193
- raw_response_excerpt: sanitizedExcerpt,
1194
- // Hash over the sanitized excerpt — engine doesn't yet thread the full
1195
- // raw response. Documented limitation on `raw_response_hash` in types.ts.
1196
- raw_response_hash: sha256Hex(v.raw_response_excerpt),
1197
- reasoning_summary: typeof v.reasoning_excerpt === "string" && v.reasoning_excerpt.length > 0 ? sanitizeExcerpt(v.reasoning_excerpt, 500) : null,
1198
- // Engine doesn't yet emit cited_sources; ship empty array so cloud
1199
- // ingestion doesn't NPE on `Array.isArray()` checks.
1200
- cited_sources: [],
1201
- error: v.error ?? null
1575
+ synced: true,
1576
+ action: "push",
1577
+ pushed: result.pushed,
1578
+ created: result.created,
1579
+ updated: result.updated,
1580
+ memory_dir: memoryDir
1202
1581
  };
1203
- });
1582
+ } catch (err) {
1583
+ const message = err instanceof Error ? err.message : String(err);
1584
+ return { synced: false, reason: `Sync failed: ${message}` };
1585
+ }
1204
1586
  }
1205
- function toEventSynthesizedVerdict(src) {
1206
- return {
1207
- verdict: src.verdict,
1208
- confidence: src.confidence,
1209
- reasoning_excerpt: sanitizeExcerpt(src.reasoning_excerpt)
1210
- };
1587
+ async function handleSyncConfig(deps, rawInput, _signal, fetchFn = globalThis.fetch) {
1588
+ if (!isToolInput22(rawInput)) {
1589
+ throw invalidParams(
1590
+ TOOL_NAME22,
1591
+ 'invalid input. Required: { action: "pull" | "push" }. Optional: { cwd: "<path>" }.'
1592
+ );
1593
+ }
1594
+ const cwd = rawInput.cwd?.trim() || process.cwd();
1595
+ const result = await runMemorySync(rawInput.action, cwd, deps.session.sessionId, fetchFn);
1596
+ return jsonContent({ tool: TOOL_NAME22, schema_version: 1, payload: result });
1211
1597
  }
1598
+ var TOOL_NAME22, inputSchema22, description22;
1599
+ var init_sync_config = __esm({
1600
+ "src/tools/memory/sync-config.ts"() {
1601
+ "use strict";
1602
+ init_common();
1603
+ init_safe_memory_file();
1604
+ TOOL_NAME22 = "vo_sync_config";
1605
+ inputSchema22 = {
1606
+ type: "object",
1607
+ properties: {
1608
+ action: {
1609
+ type: "string",
1610
+ enum: ["pull", "push"],
1611
+ description: "pull: download cloud memory to local files. push: upload local files to cloud."
1612
+ },
1613
+ cwd: {
1614
+ type: "string",
1615
+ description: "Working directory to derive project slug from (default: process.cwd())."
1616
+ }
1617
+ },
1618
+ required: ["action"],
1619
+ additionalProperties: false
1620
+ };
1621
+ 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.";
1622
+ }
1623
+ });
1624
+
1625
+ // src/cli.ts
1626
+ import { homedir as homedir6, hostname } from "node:os";
1627
+ import { randomUUID as randomUUID5 } from "node:crypto";
1628
+ import { join as join10 } from "node:path";
1629
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
1630
+
1631
+ // src/server.ts
1632
+ init_common();
1633
+ import { randomUUID as randomUUID2 } from "node:crypto";
1634
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
1635
+ import {
1636
+ CallToolRequestSchema,
1637
+ ListToolsRequestSchema
1638
+ } from "@modelcontextprotocol/sdk/types.js";
1212
1639
 
1213
1640
  // src/modes/local.ts
1214
1641
  function createLocalMode() {
@@ -1223,6 +1650,7 @@ function createLocalMode() {
1223
1650
  }
1224
1651
 
1225
1652
  // src/tools/check-assertion-strength.ts
1653
+ init_common();
1226
1654
  var TOOL_NAME = "vo_check_assertion_strength";
1227
1655
  var GATE_TYPE = "ratchet";
1228
1656
  var MAX_SOURCE_BYTES = 512 * 1024;
@@ -1319,7 +1747,11 @@ async function handleCheckAssertionStrength(deps, rawInput, _signal) {
1319
1747
  return jsonContent(envelope);
1320
1748
  }
1321
1749
 
1750
+ // src/tools/check-hollow-test.ts
1751
+ init_common();
1752
+
1322
1753
  // src/tools/architecture-review-kb-prefilter.ts
1754
+ init_src();
1323
1755
  var DEFAULT_STACK = [
1324
1756
  "node",
1325
1757
  "node-pnpm-monorepo",
@@ -1405,6 +1837,7 @@ function formatRulesForPrompt(hits, truncated, domainLabel = "ARCHITECTURAL") {
1405
1837
  }
1406
1838
 
1407
1839
  // src/tools/kb-metadata-prefilter.ts
1840
+ init_src();
1408
1841
  function findRulesByMetadata(opts) {
1409
1842
  if (opts.category === void 0 && opts.tagsAny === void 0) {
1410
1843
  return {
@@ -1598,6 +2031,7 @@ async function handleCheckHollowTest(deps, rawInput, signal) {
1598
2031
  }
1599
2032
 
1600
2033
  // src/tools/verify-answer.ts
2034
+ init_common();
1601
2035
  var TOOL_NAME3 = "vo_verify_answer";
1602
2036
  var SHALLOW_GATE = "mid-exec-verify";
1603
2037
  var DEEP_GATE = "final-deep-verify";
@@ -1779,6 +2213,9 @@ async function handleVerifyAnswer(deps, rawInput, signal) {
1779
2213
  return jsonContent(envelope);
1780
2214
  }
1781
2215
 
2216
+ // src/tools/consensus-judgment.ts
2217
+ init_common();
2218
+
1782
2219
  // src/consensus/gate-types.ts
1783
2220
  var LEGACY_GATE_TYPES = [
1784
2221
  "test_assertion",
@@ -2036,13 +2473,19 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
2036
2473
  ...engineResult.synthesized_verdict.confidence_badge !== void 0 ? { confidence_badge: engineResult.synthesized_verdict.confidence_badge } : {},
2037
2474
  // Feature 1 (agreement-gate) — fan-out diagnostics (present iff the gate ran).
2038
2475
  ...engineResult.fan_out_diagnostics !== void 0 ? { fan_out_diagnostics: engineResult.fan_out_diagnostics } : {},
2476
+ // Stage A7-shadow — adaptive-vs-incumbent comparison (PII-free; present iff shadow on).
2477
+ ...engineResult.shadow_synthesis !== void 0 ? { shadow_synthesis: engineResult.shadow_synthesis } : {},
2039
2478
  // Source-grounded Tier-4 outputs (present iff the call was source-grounded).
2040
2479
  ...engineResult.source_grounded === true ? { source_grounded: true } : {},
2041
2480
  ...engineResult.citation_grade !== void 0 ? { citation_grade: engineResult.citation_grade } : {},
2042
2481
  ...engineResult.low_confidence_sources !== void 0 ? { low_confidence_sources: engineResult.low_confidence_sources } : {},
2043
2482
  // Escalation (from citation grade or human-tiebreak synthesizer).
2044
2483
  ...engineResult.escalation_required !== void 0 ? { escalation_required: engineResult.escalation_required } : {},
2045
- ...engineResult.escalation_reason !== void 0 ? { escalation_reason: engineResult.escalation_reason } : {}
2484
+ ...engineResult.escalation_reason !== void 0 ? { escalation_reason: engineResult.escalation_reason } : {},
2485
+ // Critique-uptake (2026-07-20 red-team fix) — the engine computes this
2486
+ // on every call; this spread closes the gap where the visibility report
2487
+ // was itself silently dropped at the payload boundary.
2488
+ ...engineResult.critique_uptake !== void 0 ? { critique_uptake: engineResult.critique_uptake } : {}
2046
2489
  };
2047
2490
  const envelope = {
2048
2491
  tool: TOOL_NAME4,
@@ -2056,6 +2499,8 @@ async function handleConsensusJudgment(deps, rawInput, signal) {
2056
2499
  }
2057
2500
 
2058
2501
  // src/tools/architecture-review.ts
2502
+ init_common();
2503
+ init_events_writer();
2059
2504
  var TOOL_NAME5 = "vo_architecture_review";
2060
2505
  var GATE_TYPE3 = "architecture-review";
2061
2506
  var MAX_DIFF_BYTES = 1024 * 1024;
@@ -3300,6 +3745,7 @@ function partitionByAllowlist(findings, allowlist) {
3300
3745
  }
3301
3746
 
3302
3747
  // src/tools/check-ratchets.ts
3748
+ init_common();
3303
3749
  var TOOL_NAME6 = "vo_check_ratchets";
3304
3750
  var GATE_TYPE4 = "ratchet";
3305
3751
  var ALL_RATCHET_IDS = [
@@ -3432,6 +3878,7 @@ function buildSummary(report) {
3432
3878
  }
3433
3879
 
3434
3880
  // src/tools/decompose-dispatch.ts
3881
+ init_common();
3435
3882
  var TOOL_NAME7 = "vo_decompose_dispatch";
3436
3883
  var GATE_TYPE5 = "plan-review";
3437
3884
  var MAX_GOAL_BYTES = 32 * 1024;
@@ -3709,6 +4156,9 @@ Produce the JSON dispatch plan now.`;
3709
4156
  return jsonContent(envelope);
3710
4157
  }
3711
4158
 
4159
+ // src/tools/heal/trigger-heal.ts
4160
+ init_common();
4161
+
3712
4162
  // src/cloud/admin-callable-client.ts
3713
4163
  init_auth_token_source();
3714
4164
  init_credential_store();
@@ -3838,6 +4288,7 @@ function buildAdminCallableClientFromEnv(env = process.env) {
3838
4288
  }
3839
4289
 
3840
4290
  // src/tools/cloud-call.ts
4291
+ init_common();
3841
4292
  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
4293
  async function buildCloudOrStubResponse(args) {
3843
4294
  const inputJson = JSON.stringify(args.normalizedInput);
@@ -3911,6 +4362,7 @@ async function buildCloudOrStubResponse(args) {
3911
4362
  }
3912
4363
 
3913
4364
  // src/tools/heal/common-heal.ts
4365
+ init_common();
3914
4366
  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
4367
  var HEAL_GATE_TYPE = "admin-action";
3916
4368
 
@@ -3970,6 +4422,7 @@ async function handleTriggerHeal(deps, rawInput, _signal) {
3970
4422
  }
3971
4423
 
3972
4424
  // src/tools/heal/fix-retry.ts
4425
+ init_common();
3973
4426
  var TOOL_NAME9 = "vo_fix_retry";
3974
4427
  var MAX_BATCH = 50;
3975
4428
  var ADMIN_PATH_SINGLE = "/api/v1/admin/heal/retry-attempt";
@@ -4062,6 +4515,7 @@ async function handleFixRetry(deps, rawInput, _signal) {
4062
4515
  }
4063
4516
 
4064
4517
  // src/tools/heal/fix-clear.ts
4518
+ init_common();
4065
4519
  var TOOL_NAME10 = "vo_fix_clear";
4066
4520
  var CALLABLE_NAME2 = "voClearFixAttempt";
4067
4521
  var ADMIN_PATH2 = "/api/v1/admin/heal/clear-attempt";
@@ -4105,6 +4559,7 @@ async function handleFixClear(deps, rawInput, _signal) {
4105
4559
  }
4106
4560
 
4107
4561
  // src/tools/heal/stop-workflow.ts
4562
+ init_common();
4108
4563
  var TOOL_NAME11 = "vo_stop_workflow";
4109
4564
  var CALLABLE_NAME3 = "voStopWorkflow";
4110
4565
  var ADMIN_PATH3 = "/api/v1/admin/workflow/stop";
@@ -4160,6 +4615,7 @@ async function handleStopWorkflow(deps, rawInput, _signal) {
4160
4615
  }
4161
4616
 
4162
4617
  // src/tools/heal/get-workflow-runs.ts
4618
+ init_common();
4163
4619
  var TOOL_NAME12 = "vo_get_workflow_runs";
4164
4620
  var CALLABLE_NAME4 = "voGetWorkflowRuns";
4165
4621
  var ADMIN_PATH4 = "/api/v1/admin/workflow/runs";
@@ -4190,7 +4646,11 @@ async function handleGetWorkflowRuns(deps, rawInput, _signal) {
4190
4646
  });
4191
4647
  }
4192
4648
 
4649
+ // src/tools/pr/list-pending-prs.ts
4650
+ init_common();
4651
+
4193
4652
  // src/tools/pr/common-pr.ts
4653
+ init_common();
4194
4654
  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
4655
  var PR_GATE_TYPE = "admin-action";
4196
4656
 
@@ -4203,7 +4663,7 @@ var inputSchema13 = {
4203
4663
  properties: {},
4204
4664
  additionalProperties: false
4205
4665
  };
4206
- var description13 = "Lists open VO-source pull requests with blocker / source / tester / specialist-context metadata. Read-only diagnostic for Command Center reads. Wraps `voListPendingPRs` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.";
4666
+ var description13 = "Lists open AlgoHQ-source pull requests with blocker / source / tester / specialist-context metadata. Read-only diagnostic for Command Center reads. Wraps `voListPendingPRs` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.";
4207
4667
  function isToolInput13(v) {
4208
4668
  return typeof v === "object" && v !== null;
4209
4669
  }
@@ -4225,6 +4685,7 @@ async function handleListPendingPRs(deps, rawInput, _signal) {
4225
4685
  }
4226
4686
 
4227
4687
  // src/tools/pr/merge-pr.ts
4688
+ init_common();
4228
4689
  var TOOL_NAME14 = "vo_merge_pr";
4229
4690
  var CALLABLE_NAME6 = "voMergePR";
4230
4691
  var ADMIN_PATH6 = "/api/v1/admin/pr/merge";
@@ -4239,7 +4700,7 @@ var inputSchema14 = {
4239
4700
  required: ["pr_number"],
4240
4701
  additionalProperties: false
4241
4702
  };
4242
- var description14 = "Approves + merges a single VO-source pull request by number. Wraps `voMergePR` admin Cloud Function (server-side refuses non-VO PRs with permission-denied). V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.";
4703
+ var description14 = "Approves + merges a single AlgoHQ-source pull request by number. Wraps `voMergePR` admin Cloud Function (server-side refuses non-AlgoHQ PRs with permission-denied). V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.";
4243
4704
  function isToolInput14(v) {
4244
4705
  if (typeof v !== "object" || v === null) return false;
4245
4706
  const o = v;
@@ -4269,6 +4730,7 @@ async function handleMergePR(deps, rawInput, _signal) {
4269
4730
  }
4270
4731
 
4271
4732
  // src/tools/pr/reject-pr.ts
4733
+ init_common();
4272
4734
  var TOOL_NAME15 = "vo_reject_pr";
4273
4735
  var CALLABLE_NAME7 = "voRejectPR";
4274
4736
  var ADMIN_PATH7 = "/api/v1/admin/pr/reject";
@@ -4313,6 +4775,7 @@ async function handleRejectPR(deps, rawInput, _signal) {
4313
4775
  }
4314
4776
 
4315
4777
  // src/tools/pr/approve-all-fixes.ts
4778
+ init_common();
4316
4779
  var TOOL_NAME16 = "vo_approve_all_fixes";
4317
4780
  var CALLABLE_NAME8 = "voApproveAllFixes";
4318
4781
  var ADMIN_PATH8 = "/api/v1/admin/pr/approve-all";
@@ -4321,7 +4784,7 @@ var inputSchema16 = {
4321
4784
  properties: {},
4322
4785
  additionalProperties: false
4323
4786
  };
4324
- var description16 = "Iterates all open VO-source pull requests and merges (or arms auto-merge) on each. Returns counts of merged / accepted / total plus per-PR results. Wraps `voApproveAllFixes` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.";
4787
+ var description16 = "Iterates all open AlgoHQ-source pull requests and merges (or arms auto-merge) on each. Returns counts of merged / accepted / total plus per-PR results. Wraps `voApproveAllFixes` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.";
4325
4788
  function isToolInput16(v) {
4326
4789
  return typeof v === "object" && v !== null;
4327
4790
  }
@@ -4341,6 +4804,7 @@ async function handleApproveAllFixes(deps, rawInput, _signal) {
4341
4804
  }
4342
4805
 
4343
4806
  // src/tools/pr/reject-and-retry.ts
4807
+ init_common();
4344
4808
  var TOOL_NAME17 = "vo_reject_and_retry";
4345
4809
  var CALLABLE_NAME9 = "voRejectAndRetry";
4346
4810
  var ADMIN_PATH9 = "/api/v1/admin/pr/reject-retry";
@@ -4355,7 +4819,7 @@ var inputSchema17 = {
4355
4819
  required: ["pr_number"],
4356
4820
  additionalProperties: false
4357
4821
  };
4358
- var description17 = "Closes a VO pull request and dispatches a self-heal pass to retry the same focus page. Cloud callable refuses non-VO PRs and respects the self-heal kill switch + per-PR retry block. Wraps `voRejectAndRetry` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.";
4822
+ var description17 = "Closes an AlgoHQ pull request and dispatches a self-heal pass to retry the same focus page. Cloud callable refuses non-AlgoHQ PRs and respects the self-heal kill switch + per-PR retry block. Wraps `voRejectAndRetry` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.";
4359
4823
  function isToolInput17(v) {
4360
4824
  if (typeof v !== "object" || v === null) return false;
4361
4825
  const o = v;
@@ -4385,6 +4849,7 @@ async function handleRejectAndRetry(deps, rawInput, _signal) {
4385
4849
  }
4386
4850
 
4387
4851
  // src/tools/pr/review-merge.ts
4852
+ init_common();
4388
4853
  var TOOL_NAME18 = "vo_review_merge";
4389
4854
  var LIST_PATH = "/api/v1/admin/pr/list";
4390
4855
  var ENGINE_GATE = "final-deep-verify";
@@ -4431,7 +4896,7 @@ function buildPrompt4(pr, notes) {
4431
4896
  const lines = [
4432
4897
  "You are a release gatekeeper deciding whether a pull request is safe to MERGE.",
4433
4898
  "Recommend exactly one of: merge / hold / reject. Be conservative \u2014 this is a high-stakes irreversible action.",
4434
- "Rules: HOLD if CI is failing/blocked, there is a merge conflict, or the change is a draft. REJECT if the PR is not a legitimate VO-source change or has no clear purpose. MERGE only if it looks complete, scoped, and unblocked.",
4899
+ "Rules: HOLD if CI is failing/blocked, there is a merge conflict, or the change is a draft. REJECT if the PR is not a legitimate AlgoHQ-source change or has no clear purpose. MERGE only if it looks complete, scoped, and unblocked.",
4435
4900
  "",
4436
4901
  `PR #${pr.number}: ${pr.title}`,
4437
4902
  `Source: ${pr.source ?? "unknown"}`,
@@ -4499,7 +4964,7 @@ async function handleReviewMerge(deps, rawInput, signal) {
4499
4964
  }
4500
4965
  if (pr === null) {
4501
4966
  return emit(
4502
- emptyPayload("hold", `PR #${prNumber} is not among open VO PRs (already merged/closed, or not a VO-source PR).`, null)
4967
+ emptyPayload("hold", `PR #${prNumber} is not among open AlgoHQ PRs (already merged/closed, or not an AlgoHQ-source PR).`, null)
4503
4968
  );
4504
4969
  }
4505
4970
  const hasBlocker = pr.blocker !== null && pr.blocker !== "none";
@@ -4563,6 +5028,9 @@ async function handleReviewMerge(deps, rawInput, signal) {
4563
5028
  });
4564
5029
  }
4565
5030
 
5031
+ // src/tools/session/report-session-state.ts
5032
+ init_common();
5033
+
4566
5034
  // src/tools/session/directive.ts
4567
5035
  var SESSION_DIRECTIVE_THRESHOLDS = {
4568
5036
  prepare_handoff_pct: 70,
@@ -4594,6 +5062,8 @@ function suggestedHandoffPath(session_id, isoTimestamp) {
4594
5062
  }
4595
5063
 
4596
5064
  // src/tools/session/report-session-state.ts
5065
+ init_auth_token_source();
5066
+ init_credential_store();
4597
5067
  var TOOL_NAME19 = "vo_report_session_state";
4598
5068
  var VALID_AGENT_TYPES = ["claude-code", "codex", "cursor", "continue"];
4599
5069
  var MAX_GOAL_CHARS = 500;
@@ -4644,7 +5114,7 @@ var inputSchema19 = {
4644
5114
  required: ["operator_id", "session_id", "agent_type", "context_used_pct"],
4645
5115
  additionalProperties: false
4646
5116
  };
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).";
5117
+ var description19 = "Reports per-session context-window utilization to AlgoHQ 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 AlgoHQ roadmap. Cloud-control-plane mode when VO_CONTROL_PLANE_URL plus a user/scoped HQ credential (or legacy admin token) is available; 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
5118
  function isStringArray2(v, maxItems) {
4649
5119
  if (!Array.isArray(v)) return false;
4650
5120
  if (v.length > maxItems) return false;
@@ -4669,33 +5139,93 @@ function isToolInput19(v) {
4669
5139
  }
4670
5140
  return true;
4671
5141
  }
4672
- function getCloudConfig() {
4673
- const url = process.env["VO_CONTROL_PLANE_URL"];
4674
- const token = process.env["VO_CONTROL_PLANE_ADMIN_TOKEN"];
4675
- if (!url || !token) return null;
4676
- return { url, token };
5142
+ async function fetchCloudIdentity(url, token, fetchFn) {
5143
+ try {
5144
+ const response = await fetchFn(`${url}/api/v1/auth/me`, {
5145
+ method: "GET",
5146
+ headers: {
5147
+ "Authorization": `Bearer ${token}`
5148
+ }
5149
+ });
5150
+ if (!response.ok) return null;
5151
+ const data = await response.json();
5152
+ if (!data.ok || !data.provisioned || !data.operator_id || !data.tenant_id) return null;
5153
+ return { operator_id: data.operator_id, tenant_id: data.tenant_id };
5154
+ } catch {
5155
+ return null;
5156
+ }
5157
+ }
5158
+ async function getCloudConfig(fetchFn = fetch) {
5159
+ const url = process.env["VO_CONTROL_PLANE_URL"]?.trim();
5160
+ if (!url) return null;
5161
+ const tokenSource = createAuthTokenSourceFromEnv(
5162
+ process.env,
5163
+ fetchFn,
5164
+ () => readStoredCredential(process.env)
5165
+ );
5166
+ const token = await tokenSource?.getToken();
5167
+ if (!token) return null;
5168
+ const tenant_id = process.env["VO_TENANT_ID"]?.trim();
5169
+ if (tenant_id) return { url, token, tenant_id };
5170
+ const identity = await fetchCloudIdentity(url, token, fetchFn);
5171
+ if (!identity) return null;
5172
+ return { url, token, tenant_id: identity.tenant_id, operator_id: identity.operator_id };
4677
5173
  }
4678
- async function tryCloudReportState(cloud, input) {
5174
+ async function tryCloudReportState(cloud, input, fetchFn = fetch) {
4679
5175
  try {
4680
- const body = {
5176
+ const reportBody = {
4681
5177
  context_used_pct: input.context_used_pct
4682
5178
  };
4683
- if (input.current_goal !== void 0) body["current_goal"] = input.current_goal;
5179
+ if (input.current_goal !== void 0) reportBody["current_goal"] = input.current_goal;
4684
5180
  if (input.recent_files_touched !== void 0) {
4685
- body["recent_files_touched"] = input.recent_files_touched;
5181
+ reportBody["recent_files_touched"] = input.recent_files_touched;
4686
5182
  }
4687
5183
  if (input.recent_tool_uses !== void 0) {
4688
- body["recent_tool_uses"] = input.recent_tool_uses;
5184
+ reportBody["recent_tool_uses"] = input.recent_tool_uses;
4689
5185
  }
4690
- const url = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;
4691
- const response = await fetch(url, {
5186
+ const reportUrl = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;
5187
+ let response = await fetchFn(reportUrl, {
4692
5188
  method: "POST",
4693
5189
  headers: {
4694
5190
  "Content-Type": "application/json",
4695
5191
  "Authorization": `Bearer ${cloud.token}`
4696
5192
  },
4697
- body: JSON.stringify(body)
5193
+ body: JSON.stringify(reportBody)
4698
5194
  });
5195
+ if (response.status === 404) {
5196
+ const allocateBody = {
5197
+ operator_id: cloud.operator_id ?? input.operator_id,
5198
+ tenant_id: cloud.tenant_id,
5199
+ agent_type: input.agent_type,
5200
+ current_goal: input.current_goal ?? "Interactive session"
5201
+ };
5202
+ if (input.context_used_pct > 0) {
5203
+ allocateBody["initial_context_used_pct"] = input.context_used_pct;
5204
+ }
5205
+ const allocateUrl = `${cloud.url}/api/v1/session`;
5206
+ const allocateResponse = await fetchFn(allocateUrl, {
5207
+ method: "POST",
5208
+ headers: {
5209
+ "Content-Type": "application/json",
5210
+ "Authorization": `Bearer ${cloud.token}`
5211
+ },
5212
+ body: JSON.stringify(allocateBody)
5213
+ });
5214
+ if (!allocateResponse.ok) {
5215
+ return null;
5216
+ }
5217
+ const allocateData = await allocateResponse.json();
5218
+ const retrySessionId = typeof allocateData.session?.session_id === "string" && allocateData.session.session_id.length > 0 ? allocateData.session.session_id : input.session_id;
5219
+ const retryReportUrl = `${cloud.url}/api/v1/session/${retrySessionId}/report-state`;
5220
+ response = await fetchFn(retryReportUrl, {
5221
+ method: "POST",
5222
+ headers: {
5223
+ "Content-Type": "application/json",
5224
+ "Authorization": `Bearer ${cloud.token}`
5225
+ },
5226
+ body: JSON.stringify(reportBody)
5227
+ });
5228
+ }
4699
5229
  if (!response.ok) {
4700
5230
  return null;
4701
5231
  }
@@ -4726,7 +5256,7 @@ async function handleReportSessionState(deps, rawInput, _signal) {
4726
5256
  `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}).`
4727
5257
  );
4728
5258
  }
4729
- const cloud = getCloudConfig();
5259
+ const cloud = await getCloudConfig();
4730
5260
  if (cloud !== null) {
4731
5261
  const cloudPayload = await tryCloudReportState(cloud, rawInput);
4732
5262
  if (cloudPayload !== null) {
@@ -4751,6 +5281,7 @@ async function handleReportSessionState(deps, rawInput, _signal) {
4751
5281
  }
4752
5282
 
4753
5283
  // src/tools/session/spawn-successor.ts
5284
+ init_common();
4754
5285
  import { spawn } from "node:child_process";
4755
5286
  import { homedir as homedir4 } from "node:os";
4756
5287
  import { join as join6 } from "node:path";
@@ -4809,7 +5340,7 @@ var MANDATORY_READS = [
4809
5340
  function buildSuccessorPrompt(handoffMarkdown, goal) {
4810
5341
  const reads = MANDATORY_READS.map((r, i) => ` ${i + 1}. ${r}`).join("\n");
4811
5342
  const lines = [
4812
- "You are the SUCCESSOR agent for a Virtual Office lane. The previous session",
5343
+ "You are the SUCCESSOR agent for an AlgoHQ lane. The previous session",
4813
5344
  "exhausted its context and wrote the handoff below. Read it fully, verify its",
4814
5345
  '"verification needed" items against live state (a handoff is a claim, not',
4815
5346
  "evidence \u2014 verify via `git show origin/main:<path>`), then continue the lane.",
@@ -4820,7 +5351,7 @@ function buildSuccessorPrompt(handoffMarkdown, goal) {
4820
5351
  "NON-NEGOTIABLES: multi-model consensus verification is the core; test honesty",
4821
5352
  "(verified-answer-only, no fake green); verify-before-act + human merge approval;",
4822
5353
  "never a full functions-shared deploy; Gen2 only; work in a worktree on your own",
4823
- "branch; finish line is MERGED + DEPLOYED + LIVE-VERIFIED, and VO changes update",
5354
+ "branch; finish line is MERGED + DEPLOYED + LIVE-VERIFIED, and AlgoHQ changes update",
4824
5355
  "the roadmap in the same PR.",
4825
5356
  "",
4826
5357
  "--- HANDOFF ---",
@@ -4885,6 +5416,9 @@ async function handleSpawnSuccessor(_deps, rawInput, _signal, spawnImpl = spawn)
4885
5416
  });
4886
5417
  }
4887
5418
 
5419
+ // src/tools/concierge/dispatch.ts
5420
+ init_common();
5421
+
4888
5422
  // src/tools/concierge/common-concierge.ts
4889
5423
  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
5424
  var CONCIERGE_GATE_TYPE = "concierge-dispatch";
@@ -4966,252 +5500,439 @@ async function handleConciergeDispatch(deps, rawInput, _signal) {
4966
5500
  });
4967
5501
  }
4968
5502
 
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 = {
5503
+ // src/server.ts
5504
+ init_sync_config();
5505
+
5506
+ // src/tools/memory/private-knowledge.ts
5507
+ init_common();
5508
+ var UPSERT_TOOL_NAME = "vo_private_knowledge_upsert";
5509
+ var CONTEXT_TOOL_NAME = "vo_private_knowledge_context";
5510
+ var KNOWLEDGE_CLASSES = ["memory", "skill", "doctrine", "hook", "command"];
5511
+ var PRECISION_CHAR_BUDGET = 12e3;
5512
+ var upsertInputSchema = {
4975
5513
  type: "object",
4976
5514
  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."
5515
+ knowledge_class: { type: "string", enum: KNOWLEDGE_CLASSES },
5516
+ source_path: { type: "string", description: "Stable private source identifier; not exposed to other users." },
5517
+ title: { type: "string", description: 'Descriptive, retrieval-friendly title (e.g. "AlgoTax OCR redaction architecture", not "notes") \u2014 retrieval matches on it.' },
5518
+ content: { type: "string", description: "Private knowledge text to store server-side. Keep each entry tight and focused (~1-3 pages, under ~12k chars); split larger corpora into separate entries." }
5519
+ },
5520
+ required: ["knowledge_class", "source_path", "title", "content"],
5521
+ additionalProperties: false
5522
+ };
5523
+ var contextInputSchema = {
5524
+ type: "object",
5525
+ properties: {
5526
+ query: { type: "string" },
5527
+ limit: { type: "number", minimum: 1, maximum: 50 },
5528
+ knowledge_class: { type: "string", enum: KNOWLEDGE_CLASSES }
5529
+ },
5530
+ required: ["query"],
5531
+ additionalProperties: false
5532
+ };
5533
+ var upsertDescription = "Uploads or refreshes the authenticated operator\u2019s private cloud knowledge. Works for Claude, Codex, Cursor, and cowork clients via the same vo-mcp login credential. Returns metadata only, not raw stored content. PRECISION DISCIPLINE: keep each entry tight and focused (~1-3 pages) with a descriptive retrieval-friendly title \u2014 retrieval surfaces whole entries, so small dense entries beat bulk dumps. Split large corpora into focused entries, then run a retrieval self-test via vo_private_knowledge_context before relying on the knowledge.";
5534
+ var contextDescription = "Retrieves prompt-ready private knowledge context for the authenticated operator. Returns snippets/context only; no raw corpus download. Also the retrieval self-test surface: after upserting critical knowledge, query for it here and confirm the entry surfaces before trusting it in downstream work.";
5535
+ function isKnowledgeClass(value) {
5536
+ return typeof value === "string" && KNOWLEDGE_CLASSES.includes(value);
5537
+ }
5538
+ function isUpsertInput(value) {
5539
+ if (typeof value !== "object" || value === null) return false;
5540
+ const input = value;
5541
+ return isKnowledgeClass(input["knowledge_class"]) && typeof input["source_path"] === "string" && typeof input["title"] === "string" && typeof input["content"] === "string";
5542
+ }
5543
+ function isContextInput(value) {
5544
+ if (typeof value !== "object" || value === null) return false;
5545
+ const input = value;
5546
+ if (typeof input["query"] !== "string") return false;
5547
+ if (input["limit"] !== void 0 && typeof input["limit"] !== "number") return false;
5548
+ if (input["knowledge_class"] !== void 0 && !isKnowledgeClass(input["knowledge_class"])) return false;
5549
+ return true;
5550
+ }
5551
+ async function getCloudAuth(fetchFn) {
5552
+ const controlPlaneUrl = process.env["VO_CONTROL_PLANE_URL"]?.replace(/\/+$/, "");
5553
+ if (!controlPlaneUrl) {
5554
+ return { ok: false, reason: "VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled" };
5555
+ }
5556
+ const { createAuthTokenSourceFromEnv: createAuthTokenSourceFromEnv2 } = await Promise.resolve().then(() => (init_auth_token_source(), auth_token_source_exports));
5557
+ const { readStoredCredential: readStoredCredential2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
5558
+ const tokenSource = createAuthTokenSourceFromEnv2(process.env, fetchFn, () => readStoredCredential2(process.env));
5559
+ if (!tokenSource) return { ok: false, reason: "No auth configured. Run `vo-mcp login`." };
5560
+ const token = await tokenSource.getToken();
5561
+ if (!token) return { ok: false, reason: "Failed to obtain auth token. Run `vo-mcp login` again." };
5562
+ return { ok: true, controlPlaneUrl, token };
5563
+ }
5564
+ async function callPrivateKnowledge(path3, body, fetchFn) {
5565
+ const auth = await getCloudAuth(fetchFn);
5566
+ if (!auth.ok) return { ok: false, reason: auth.reason };
5567
+ const response = await fetchFn(`${auth.controlPlaneUrl}${path3}`, {
5568
+ method: "POST",
5569
+ headers: {
5570
+ authorization: `Bearer ${auth.token}`,
5571
+ "content-type": "application/json"
4981
5572
  },
4982
- cwd: {
5573
+ body: JSON.stringify(body)
5574
+ });
5575
+ const text = await response.text();
5576
+ const parsed = text ? JSON.parse(text) : null;
5577
+ if (response.status < 200 || response.status >= 300) {
5578
+ return { ok: false, status: response.status, response: parsed ?? text };
5579
+ }
5580
+ return parsed;
5581
+ }
5582
+ async function handlePrivateKnowledgeUpsert(_deps, rawInput, _signal, fetchFn = globalThis.fetch) {
5583
+ if (!isUpsertInput(rawInput)) {
5584
+ throw invalidParams(UPSERT_TOOL_NAME, "expected { knowledge_class, source_path, title, content }.");
5585
+ }
5586
+ const payload = await callPrivateKnowledge("/api/v1/knowledge/private", rawInput, fetchFn);
5587
+ const envelope = {
5588
+ tool: UPSERT_TOOL_NAME,
5589
+ schema_version: 1,
5590
+ payload
5591
+ };
5592
+ if (rawInput.content.length > PRECISION_CHAR_BUDGET) {
5593
+ envelope.precision_note = `content is ${rawInput.content.length} chars (> ${PRECISION_CHAR_BUDGET}). Tight 1-3 page entries retrieve better \u2014 consider splitting into focused entries, then re-test retrieval via ${CONTEXT_TOOL_NAME}.`;
5594
+ }
5595
+ return jsonContent(envelope);
5596
+ }
5597
+ async function handlePrivateKnowledgeContext(_deps, rawInput, _signal, fetchFn = globalThis.fetch) {
5598
+ if (!isContextInput(rawInput)) {
5599
+ throw invalidParams(CONTEXT_TOOL_NAME, "expected { query, optional limit, optional knowledge_class }.");
5600
+ }
5601
+ const payload = await callPrivateKnowledge("/api/v1/knowledge/private/context", rawInput, fetchFn);
5602
+ return jsonContent({ tool: CONTEXT_TOOL_NAME, schema_version: 1, payload });
5603
+ }
5604
+
5605
+ // src/tools/hq/whiteboard.ts
5606
+ init_auth_token_source();
5607
+ init_credential_store();
5608
+ init_common();
5609
+ var POST_TOOL_NAME = "hq_whiteboard_post";
5610
+ var READ_TOOL_NAME = "hq_whiteboard_read";
5611
+ var postDescription = "Post an append-only coordination note to the live AlgoHQ whiteboard. Uses the scoped credential from vo-mcp login; operator and tenant ownership are derived by the server.";
5612
+ var readDescription = "Read recent coordination notes from the caller's live AlgoHQ whiteboard. Uses the scoped credential from vo-mcp login and cannot widen tenant scope.";
5613
+ var postInputSchema = {
5614
+ type: "object",
5615
+ properties: {
5616
+ from: { type: "string", minLength: 1, maxLength: 100, description: "Agent/session display name." },
5617
+ type: { type: "string", minLength: 1, maxLength: 64, description: "Message kind, such as intent, worklog, blocker, or completion." },
5618
+ content: { type: "string", minLength: 1, maxLength: 500, description: "Short coordination note." },
5619
+ targetAgent: { type: "string", maxLength: 100 },
5620
+ tester: { type: "string", maxLength: 100 },
5621
+ tier: { type: "string", maxLength: 32 }
5622
+ },
5623
+ required: ["from", "type", "content"],
5624
+ additionalProperties: false
5625
+ };
5626
+ var readInputSchema = {
5627
+ type: "object",
5628
+ properties: {
5629
+ limit: { type: "integer", minimum: 1, maximum: 100, default: 25 },
5630
+ since: { type: "string", description: "Optional ISO-8601 lower bound." },
5631
+ type: { type: "string", minLength: 1, maxLength: 64 }
5632
+ },
5633
+ additionalProperties: false
5634
+ };
5635
+ function resolveTimeoutMs() {
5636
+ const parsed = Number(process.env["HQ_WHITEBOARD_TIMEOUT_MS"]);
5637
+ return Number.isFinite(parsed) && parsed >= 10 && parsed <= 12e4 ? parsed : 1e4;
5638
+ }
5639
+ function isRecord(value) {
5640
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5641
+ }
5642
+ function onlyKeys(value, allowed) {
5643
+ return Object.keys(value).every((key) => allowed.includes(key));
5644
+ }
5645
+ function isBoundedString(value, min, max) {
5646
+ return typeof value === "string" && value.trim().length >= min && value.trim().length <= max;
5647
+ }
5648
+ function parsePostInput(value) {
5649
+ if (!isRecord(value) || !onlyKeys(value, ["from", "type", "content", "targetAgent", "tester", "tier"])) return null;
5650
+ if (!isBoundedString(value["from"], 1, 100)) return null;
5651
+ if (!isBoundedString(value["type"], 1, 64) || !/^[a-zA-Z0-9_-]+$/.test(value["type"].trim())) return null;
5652
+ if (!isBoundedString(value["content"], 1, 500)) return null;
5653
+ for (const [key, max] of [["targetAgent", 100], ["tester", 100], ["tier", 32]]) {
5654
+ if (value[key] !== void 0 && !isBoundedString(value[key], 0, max)) return null;
5655
+ }
5656
+ return {
5657
+ from: value["from"].trim(),
5658
+ type: value["type"].trim(),
5659
+ content: value["content"].trim(),
5660
+ ...typeof value["targetAgent"] === "string" ? { targetAgent: value["targetAgent"].trim() } : {},
5661
+ ...typeof value["tester"] === "string" ? { tester: value["tester"].trim() } : {},
5662
+ ...typeof value["tier"] === "string" ? { tier: value["tier"].trim() } : {}
5663
+ };
5664
+ }
5665
+ function parseReadInput(value) {
5666
+ if (!isRecord(value) || !onlyKeys(value, ["limit", "since", "type"])) return null;
5667
+ if (value["limit"] !== void 0 && (!Number.isInteger(value["limit"]) || Number(value["limit"]) < 1 || Number(value["limit"]) > 100)) return null;
5668
+ if (value["since"] !== void 0 && (typeof value["since"] !== "string" || Number.isNaN(Date.parse(value["since"])))) return null;
5669
+ if (value["type"] !== void 0 && !isBoundedString(value["type"], 1, 64)) return null;
5670
+ return {
5671
+ ...typeof value["limit"] === "number" ? { limit: value["limit"] } : {},
5672
+ ...typeof value["since"] === "string" ? { since: value["since"] } : {},
5673
+ ...typeof value["type"] === "string" ? { type: value["type"].trim() } : {}
5674
+ };
5675
+ }
5676
+ async function resolveCloud(fetchFn) {
5677
+ const url = process.env["VO_CONTROL_PLANE_URL"]?.trim().replace(/\/$/, "");
5678
+ if (!url) return null;
5679
+ try {
5680
+ const source = createAuthTokenSourceFromEnv(process.env, fetchFn, () => readStoredCredential(process.env));
5681
+ const token = await source?.getToken();
5682
+ return token ? { url, token } : null;
5683
+ } catch {
5684
+ return null;
5685
+ }
5686
+ }
5687
+ async function callWhiteboard(method, bodyOrQuery, signal, fetchFn = fetch) {
5688
+ const cloud = await resolveCloud(fetchFn);
5689
+ if (!cloud) {
5690
+ return {
5691
+ ok: false,
5692
+ error: "hq_whiteboard_not_configured",
5693
+ message: "Set VO_CONTROL_PLANE_URL and run vo-mcp login to install a scoped HQ credential."
5694
+ };
5695
+ }
5696
+ const timeoutSignal = AbortSignal.timeout(resolveTimeoutMs());
5697
+ const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
5698
+ const query = new URLSearchParams();
5699
+ if (method === "GET") {
5700
+ const input = bodyOrQuery;
5701
+ query.set("limit", String(input.limit ?? 25));
5702
+ if (input.since) query.set("since", input.since);
5703
+ if (input.type) query.set("type", input.type);
5704
+ }
5705
+ try {
5706
+ const response = await fetchFn(
5707
+ `${cloud.url}/api/v1/hq/whiteboard/messages${query.size ? `?${query}` : ""}`,
5708
+ {
5709
+ method,
5710
+ headers: {
5711
+ Authorization: `Bearer ${cloud.token}`,
5712
+ ...method === "POST" ? { "Content-Type": "application/json" } : {}
5713
+ },
5714
+ ...method === "POST" ? { body: JSON.stringify(bodyOrQuery) } : {},
5715
+ signal: requestSignal
5716
+ }
5717
+ );
5718
+ const text = await response.text();
5719
+ let payload;
5720
+ try {
5721
+ payload = JSON.parse(text);
5722
+ } catch {
5723
+ payload = { ok: false, error: "invalid_response", message: text.slice(0, 200) };
5724
+ }
5725
+ if (!response.ok) {
5726
+ return { ok: false, error: "hq_whiteboard_http_error", status: response.status, response: payload };
5727
+ }
5728
+ return payload;
5729
+ } catch (error) {
5730
+ return {
5731
+ ok: false,
5732
+ error: signal?.aborted ? "cancelled" : timeoutSignal.aborted ? "hq_whiteboard_timeout" : "hq_whiteboard_unreachable",
5733
+ message: error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200)
5734
+ };
5735
+ }
5736
+ }
5737
+ async function handleHqWhiteboardPost(_deps, rawInput, signal) {
5738
+ const input = parsePostInput(rawInput);
5739
+ if (!input) throw invalidParams(POST_TOOL_NAME, "requires from, type, and 1-500 character content; unknown fields are rejected");
5740
+ return jsonContent(await callWhiteboard("POST", input, signal));
5741
+ }
5742
+ async function handleHqWhiteboardRead(_deps, rawInput, signal) {
5743
+ const input = parseReadInput(rawInput);
5744
+ if (!input) throw invalidParams(READ_TOOL_NAME, "limit must be 1-100, since must be ISO-8601, and unknown fields are rejected");
5745
+ return jsonContent(await callWhiteboard("GET", input, signal));
5746
+ }
5747
+
5748
+ // src/tools/skills/skill-corpus.ts
5749
+ import { existsSync as existsSync6, statSync as statSync5 } from "node:fs";
5750
+ import { dirname as dirname5, isAbsolute, join as join9, resolve as resolve2 } from "node:path";
5751
+
5752
+ // ../skill-registry/src/loader.ts
5753
+ import { readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
5754
+ import { join as join8 } from "node:path";
5755
+ var InvalidSkillFrontmatterError = class extends Error {
5756
+ constructor(skillFile, reason) {
5757
+ super(`Invalid frontmatter in ${skillFile}: ${reason}`);
5758
+ this.skillFile = skillFile;
5759
+ this.reason = reason;
5760
+ }
5761
+ skillFile;
5762
+ reason;
5763
+ name = "InvalidSkillFrontmatterError";
5764
+ };
5765
+ var FRONTMATTER_DELIMITER = "---";
5766
+ function parseFrontmatter(rawInput, sourcePath) {
5767
+ const raw = rawInput.replace(/\r\n/g, "\n");
5768
+ if (!raw.startsWith(`${FRONTMATTER_DELIMITER}
5769
+ `)) {
5770
+ throw new InvalidSkillFrontmatterError(sourcePath, 'file does not start with frontmatter delimiter "---"');
5771
+ }
5772
+ const afterFirst = raw.slice(FRONTMATTER_DELIMITER.length + 1);
5773
+ const closingIdx = afterFirst.indexOf(`
5774
+ ${FRONTMATTER_DELIMITER}
5775
+ `);
5776
+ if (closingIdx === -1) {
5777
+ throw new InvalidSkillFrontmatterError(sourcePath, 'missing closing frontmatter delimiter "---"');
5778
+ }
5779
+ const frontmatterText = afterFirst.slice(0, closingIdx);
5780
+ const body = afterFirst.slice(closingIdx + `
5781
+ ${FRONTMATTER_DELIMITER}
5782
+ `.length);
5783
+ let name = "";
5784
+ let description23 = "";
5785
+ for (const line of frontmatterText.split("\n")) {
5786
+ const trimmed = line.trim();
5787
+ if (trimmed.length === 0) continue;
5788
+ const colonIdx = trimmed.indexOf(":");
5789
+ if (colonIdx === -1) continue;
5790
+ const key = trimmed.slice(0, colonIdx).trim();
5791
+ const value = trimmed.slice(colonIdx + 1).trim();
5792
+ if (key === "name") name = value;
5793
+ else if (key === "description") description23 = value;
5794
+ }
5795
+ if (name.length === 0) {
5796
+ throw new InvalidSkillFrontmatterError(sourcePath, 'missing required field "name"');
5797
+ }
5798
+ if (description23.length === 0) {
5799
+ throw new InvalidSkillFrontmatterError(sourcePath, 'missing required field "description"');
5800
+ }
5801
+ return { name, description: description23, body };
5802
+ }
5803
+ function loadSkillsFromDir(skillsDir) {
5804
+ const entries = readdirSync5(skillsDir);
5805
+ const skills = [];
5806
+ for (const entry of entries) {
5807
+ const entryPath = join8(skillsDir, entry);
5808
+ let stat;
5809
+ try {
5810
+ stat = statSync4(entryPath);
5811
+ } catch {
5812
+ continue;
5813
+ }
5814
+ if (!stat.isDirectory()) continue;
5815
+ const skillFile = join8(entryPath, "SKILL.md");
5816
+ let raw;
5817
+ try {
5818
+ raw = readFileSync8(skillFile, "utf8");
5819
+ } catch {
5820
+ continue;
5821
+ }
5822
+ const { name, description: description23, body } = parseFrontmatter(raw, skillFile);
5823
+ skills.push({ name, description: description23, body, sourcePath: skillFile });
5824
+ }
5825
+ return [...skills].sort((a, b) => a.name.localeCompare(b.name));
5826
+ }
5827
+
5828
+ // src/tools/skills/skill-corpus.ts
5829
+ init_common();
5830
+ var LIST_TOOL_NAME = "vo_skill_list";
5831
+ var GET_TOOL_NAME = "vo_skill_get";
5832
+ var listDescription = "List the Algosuite skill corpus (name + trigger description for every skill). Call once near session start to learn which skills exist; then fetch the full instructions for a relevant skill with vo_skill_get. This is the same corpus Claude Code loads natively from .claude/skills \u2014 served over MCP so every vendor works from identical playbooks. Pass refresh:true to re-scan from disk.";
5833
+ var getDescription = "Fetch the full markdown instructions of one Algosuite skill by name. Follow the returned instructions for the current task the same way a native skill invocation would. Use vo_skill_list to discover skill names.";
5834
+ var listInputSchema = {
5835
+ type: "object",
5836
+ properties: {
5837
+ refresh: {
5838
+ type: "boolean",
5839
+ description: "Re-scan the skills directory instead of using the cached corpus."
5840
+ }
5841
+ },
5842
+ required: []
5843
+ };
5844
+ var getInputSchema = {
5845
+ type: "object",
5846
+ properties: {
5847
+ name: {
4983
5848
  type: "string",
4984
- description: "Working directory to derive project slug from (default: process.cwd())."
5849
+ description: "Skill name exactly as returned by vo_skill_list."
4985
5850
  }
4986
5851
  },
4987
- required: ["action"],
4988
- additionalProperties: false
5852
+ required: ["name"]
4989
5853
  };
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)}`);
5854
+ var MAX_WALK_UP_LEVELS = 8;
5855
+ var cachedCorpus = null;
5856
+ function resolveSkillsDir(env = process.env, startDir = process.cwd()) {
5857
+ const override = env.VO_SKILLS_DIR;
5858
+ if (typeof override === "string" && override.length > 0) {
5859
+ const abs = isAbsolute(override) ? override : resolve2(startDir, override);
5860
+ return existsSync6(abs) && statSync5(abs).isDirectory() ? abs : null;
5861
+ }
5862
+ let dir = resolve2(startDir);
5863
+ for (let i = 0; i < MAX_WALK_UP_LEVELS; i += 1) {
5864
+ const candidate = join9(dir, ".claude", "skills");
5865
+ if (existsSync6(candidate) && statSync5(candidate).isDirectory()) return candidate;
5866
+ const parent = dirname5(dir);
5867
+ if (parent === dir) break;
5868
+ dir = parent;
5017
5869
  }
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");
5870
+ return null;
5871
+ }
5872
+ function loadCorpus() {
5873
+ const skillsDir = resolveSkillsDir();
5874
+ if (skillsDir === null) {
5875
+ return {
5876
+ skills: [],
5877
+ skillsDir: null,
5878
+ unavailableReason: "No skills directory found. Set VO_SKILLS_DIR or run inside a repo with .claude/skills."
5879
+ };
5021
5880
  }
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);
5881
+ try {
5882
+ return { skills: loadSkillsFromDir(skillsDir), skillsDir, unavailableReason: null };
5883
+ } catch (err) {
5884
+ const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
5885
+ return { skills: [], skillsDir, unavailableReason: message };
5028
5886
  }
5029
- return { pulled: data.entries.length, files };
5030
5887
  }
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 };
5888
+ function getCorpus(refresh) {
5889
+ if (refresh || cachedCorpus === null) {
5890
+ cachedCorpus = loadCorpus();
5042
5891
  }
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
- }
5892
+ return cachedCorpus;
5893
+ }
5894
+ async function handleSkillList(_deps, rawInput) {
5895
+ const input = rawInput ?? {};
5896
+ const refresh = input.refresh === true;
5897
+ const corpus = getCorpus(refresh);
5898
+ return jsonContent({
5899
+ corpus_available: corpus.unavailableReason === null,
5900
+ skills_dir: corpus.skillsDir,
5901
+ unavailable_reason: corpus.unavailableReason,
5902
+ skill_count: corpus.skills.length,
5903
+ skills: corpus.skills.map((s) => ({ name: s.name, description: s.description }))
5049
5904
  });
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
5905
  }
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
- });
5906
+ async function handleSkillGet(_deps, rawInput) {
5907
+ const input = rawInput ?? {};
5908
+ if (typeof input.name !== "string" || input.name.trim().length === 0) {
5909
+ throw invalidParams(GET_TOOL_NAME, 'input field "name" (non-empty string) is required');
5136
5910
  }
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) {
5911
+ const requested = input.name.trim();
5912
+ const corpus = getCorpus(false);
5913
+ if (corpus.unavailableReason !== null) {
5141
5914
  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
- }
5915
+ corpus_available: false,
5916
+ unavailable_reason: corpus.unavailableReason,
5917
+ skill: null
5148
5918
  });
5149
5919
  }
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
- });
5920
+ const skill = corpus.skills.find((s) => s.name === requested);
5921
+ if (skill === void 0) {
5922
+ throw invalidParams(
5923
+ GET_TOOL_NAME,
5924
+ `unknown skill "${requested}". Known skills: ${corpus.skills.map((s) => s.name).join(", ")}`
5925
+ );
5160
5926
  }
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
- });
5927
+ return jsonContent({
5928
+ corpus_available: true,
5929
+ skill: {
5930
+ name: skill.name,
5931
+ description: skill.description,
5932
+ instructions: skill.body,
5933
+ source_path: skill.sourcePath
5203
5934
  }
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
- }
5935
+ });
5215
5936
  }
5216
5937
 
5217
5938
  // src/server.ts
@@ -5392,6 +6113,54 @@ function buildToolRegistry() {
5392
6113
  inputSchema: inputSchema22
5393
6114
  },
5394
6115
  handler: handleSyncConfig
6116
+ },
6117
+ [UPSERT_TOOL_NAME]: {
6118
+ definition: {
6119
+ name: UPSERT_TOOL_NAME,
6120
+ description: upsertDescription,
6121
+ inputSchema: upsertInputSchema
6122
+ },
6123
+ handler: handlePrivateKnowledgeUpsert
6124
+ },
6125
+ [CONTEXT_TOOL_NAME]: {
6126
+ definition: {
6127
+ name: CONTEXT_TOOL_NAME,
6128
+ description: contextDescription,
6129
+ inputSchema: contextInputSchema
6130
+ },
6131
+ handler: handlePrivateKnowledgeContext
6132
+ },
6133
+ [POST_TOOL_NAME]: {
6134
+ definition: {
6135
+ name: POST_TOOL_NAME,
6136
+ description: postDescription,
6137
+ inputSchema: postInputSchema
6138
+ },
6139
+ handler: handleHqWhiteboardPost
6140
+ },
6141
+ [READ_TOOL_NAME]: {
6142
+ definition: {
6143
+ name: READ_TOOL_NAME,
6144
+ description: readDescription,
6145
+ inputSchema: readInputSchema
6146
+ },
6147
+ handler: handleHqWhiteboardRead
6148
+ },
6149
+ [LIST_TOOL_NAME]: {
6150
+ definition: {
6151
+ name: LIST_TOOL_NAME,
6152
+ description: listDescription,
6153
+ inputSchema: listInputSchema
6154
+ },
6155
+ handler: handleSkillList
6156
+ },
6157
+ [GET_TOOL_NAME]: {
6158
+ definition: {
6159
+ name: GET_TOOL_NAME,
6160
+ description: getDescription,
6161
+ inputSchema: getInputSchema
6162
+ },
6163
+ handler: handleSkillGet
5395
6164
  }
5396
6165
  };
5397
6166
  }
@@ -5447,7 +6216,7 @@ function createServer(options) {
5447
6216
  // src/cache/sqlite-cache.ts
5448
6217
  import { createHash as createHash3 } from "node:crypto";
5449
6218
  import { chmodSync as chmodSync3, mkdirSync as mkdirSync5 } from "node:fs";
5450
- import { dirname as dirname5 } from "node:path";
6219
+ import { dirname as dirname6 } from "node:path";
5451
6220
  import { DatabaseSync } from "node:sqlite";
5452
6221
 
5453
6222
  // src/cache/canonicalize.ts
@@ -5492,7 +6261,7 @@ function normalizeString(s) {
5492
6261
  function createSqliteCache(options) {
5493
6262
  const fileBacked = options.dbPath !== ":memory:";
5494
6263
  if (fileBacked) {
5495
- mkdirSync5(dirname5(options.dbPath), { recursive: true, mode: 448 });
6264
+ mkdirSync5(dirname6(options.dbPath), { recursive: true, mode: 448 });
5496
6265
  }
5497
6266
  const versionNamespace = options.cacheVersionNamespace ?? "";
5498
6267
  const db = new DatabaseSync(options.dbPath);
@@ -5569,6 +6338,9 @@ function createSqliteCache(options) {
5569
6338
  };
5570
6339
  }
5571
6340
 
6341
+ // src/cli.ts
6342
+ init_events_writer();
6343
+
5572
6344
  // src/ratchets/stub-client.ts
5573
6345
  var HOLLOW_PATTERNS = [
5574
6346
  {
@@ -5656,6 +6428,8 @@ function buildSummary2(args) {
5656
6428
  }
5657
6429
 
5658
6430
  // src/consensus/engine-client.ts
6431
+ init_events_writer();
6432
+ init_common();
5659
6433
  import { randomUUID as randomUUID3 } from "node:crypto";
5660
6434
 
5661
6435
  // src/consensus/null-client.ts
@@ -5678,6 +6452,44 @@ function createNullConsensusEngineClient(reason = NULL_CLIENT_DEFAULT_REASON) {
5678
6452
  };
5679
6453
  }
5680
6454
 
6455
+ // src/consensus/meta-model-caller.ts
6456
+ var META_CONSENSUS_MODEL = "muse-spark-1.1";
6457
+ var META_MODEL_API_KEY_ENV = "MODEL_API_KEY";
6458
+ var META_MODEL_API_KEY_ALIAS = "META_API";
6459
+ function createMetaModelCaller(options = {}) {
6460
+ void options;
6461
+ return async function callMetaWithMetrics2() {
6462
+ throw new Error(
6463
+ "Muse Spark direct consensus is disabled. Use an explicit sanitized task capsule through the AlgoSuite Model Firewall."
6464
+ );
6465
+ };
6466
+ }
6467
+ var callMetaWithMetrics = createMetaModelCaller();
6468
+
6469
+ // src/consensus/consensus-panel.ts
6470
+ var VO_MCP_CONSENSUS_PANEL = {
6471
+ anthropic: "claude-opus-4-7",
6472
+ openai: "gpt-5",
6473
+ // gemini-2.5-FLASH (not -pro): flash accepts the default thinkingBudget=0 from
6474
+ // callGeminiWithMetrics; 2.5-pro REJECTS budget 0 ("only works in thinking mode").
6475
+ // Flash is also ~10x cheaper. 2026-06-02.
6476
+ google: "gemini-2.5-flash",
6477
+ deepseek: "deepseek-chat",
6478
+ // Muse Spark identity is owned by meta-model-caller.ts (single source of
6479
+ // truth for the meta slot); re-exported here so the panel stays complete.
6480
+ meta: META_CONSENSUS_MODEL
6481
+ };
6482
+ function getVoMcpConsensusPanel(panel = VO_MCP_CONSENSUS_PANEL) {
6483
+ for (const [provider, modelId] of Object.entries(panel)) {
6484
+ if (typeof modelId !== "string" || modelId.trim().length === 0) {
6485
+ throw new Error(
6486
+ `getVoMcpConsensusPanel: panel slot "${provider}" has a missing or blank model ID`
6487
+ );
6488
+ }
6489
+ }
6490
+ return panel;
6491
+ }
6492
+
5681
6493
  // src/consensus/engine-options.ts
5682
6494
  var AGREEMENT_GATE_ENV_VAR = "VO_CONSENSUS_AGREEMENT_GATE";
5683
6495
  function isTruthyFlag(raw) {
@@ -5724,6 +6536,25 @@ function mapFanOutDiagnostics(fd) {
5724
6536
  refused: fd.refused
5725
6537
  };
5726
6538
  }
6539
+ var SHADOW_SYNTHESIS_ENV_VAR = "VO_CONSENSUS_SHADOW";
6540
+ function shadowEnabled(env) {
6541
+ const raw = (env ?? {})[SHADOW_SYNTHESIS_ENV_VAR];
6542
+ if (raw === void 0) return true;
6543
+ const norm = raw.trim().toLowerCase();
6544
+ return !(norm === "0" || norm === "false" || norm === "no" || norm === "off" || norm === "");
6545
+ }
6546
+ function mapShadowSynthesis(s) {
6547
+ if (s === void 0) return void 0;
6548
+ return {
6549
+ incumbent: { verdict: s.incumbent.verdict, confidence: s.incumbent.confidence, synthesizer: s.incumbent.synthesizer },
6550
+ adaptive: {
6551
+ verdict: s.adaptive.verdict,
6552
+ confidence: s.adaptive.confidence,
6553
+ ...s.adaptive.calibrated_confidence !== void 0 ? { calibrated_confidence: s.adaptive.calibrated_confidence } : {}
6554
+ },
6555
+ agree: s.agree
6556
+ };
6557
+ }
5727
6558
  function mapCitationGrade(cg) {
5728
6559
  if (cg === void 0) return void 0;
5729
6560
  return {
@@ -5864,7 +6695,12 @@ function createEngineConsensusClient(options) {
5864
6695
  const engineOptions = {
5865
6696
  panel,
5866
6697
  ...options.per_model_timeout_ms !== void 0 ? { per_model_timeout_ms: options.per_model_timeout_ms } : {},
5867
- ...agreementGate !== void 0 ? { agreement_gate: agreementGate } : {}
6698
+ ...agreementGate !== void 0 ? { agreement_gate: agreementGate } : {},
6699
+ // Stage A7-shadow: run the adaptive verdict alongside the live one for grading.
6700
+ // Cheap (pure log-odds over already-fetched verdicts; no extra model calls),
6701
+ // PII-free, and never alters the live verdict. ON by default; kill with
6702
+ // VO_CONSENSUS_SHADOW=0. Cold-start has no skill registry → neutral priors.
6703
+ shadow_synthesis: { enabled: shadowEnabled(options.env) }
5868
6704
  };
5869
6705
  const sources = request.source_urls;
5870
6706
  const useSourceGrounded = sources !== void 0 && sources.length > 0 && typeof engine.runSourceGroundedConsensus === "function";
@@ -5920,6 +6756,12 @@ function createEngineConsensusClient(options) {
5920
6756
  ...sourceExtras?.escalation_reason !== void 0 ? { escalation_reason: sourceExtras.escalation_reason } : response.escalation_reason !== void 0 ? { escalation_reason: response.escalation_reason } : {},
5921
6757
  // Feature 1 (agreement-gate) — fan-out diagnostics (additive telemetry).
5922
6758
  ...mapFanOutDiagnostics(response.fan_out_diagnostics) !== void 0 ? { fan_out_diagnostics: mapFanOutDiagnostics(response.fan_out_diagnostics) } : {},
6759
+ // Stage A7-shadow — adaptive-vs-incumbent comparison (PII-free; present iff shadow on).
6760
+ ...mapShadowSynthesis(response.shadow_synthesis) !== void 0 ? { shadow_synthesis: mapShadowSynthesis(response.shadow_synthesis) } : {},
6761
+ // Critique-uptake (2026-07-20 red-team fix) — verifier-critique
6762
+ // visibility report; previously computed by the engine on every
6763
+ // call but dropped at this boundary.
6764
+ ...response.critique_uptake !== void 0 ? { critique_uptake: response.critique_uptake } : {},
5923
6765
  // Source-grounded additive outputs (Tier-4 features).
5924
6766
  ...useSourceGrounded ? { source_grounded: true } : {},
5925
6767
  ...sourceExtras?.citation_grade !== void 0 ? { citation_grade: sourceExtras.citation_grade } : {},
@@ -5935,25 +6777,12 @@ function createEngineConsensusClient(options) {
5935
6777
  }
5936
6778
  };
5937
6779
  }
5938
- var DEFAULT_MODELS = {
5939
- // These ids match the strategic-roadmap §4 `newsStandard` / `newsDeep` panel
5940
- // intent — current production model ids. Per handoff §C-3 these MUST come
5941
- // from `CONSENSUS_PANELS` in `functions-shared/shared-model-resolvers.ts`
5942
- // for V1; placeholder defaults here keep Phase 2 Lane A non-blocking.
5943
- anthropic: "claude-opus-4-7",
5944
- openai: "gpt-5",
5945
- // gemini-2.5-FLASH (not -pro): flash accepts the default thinkingBudget=0 from
5946
- // callGeminiWithMetrics; 2.5-pro REJECTS budget 0 ("only works in thinking mode").
5947
- // Flash is also ~10x cheaper. 2026-06-02.
5948
- google: "gemini-2.5-flash",
5949
- deepseek: "deepseek-chat"
5950
- };
6780
+ var DEFAULT_MODELS = getVoMcpConsensusPanel();
5951
6781
  function probeProviders(env = process.env) {
5952
6782
  const out = [];
5953
6783
  if ((env["ANTHROPIC_API_KEY"] ?? "").trim().length > 0) out.push("anthropic");
5954
6784
  if ((env["OPENAI_API_KEY"] ?? "").trim().length > 0) out.push("openai");
5955
6785
  if ((env["GOOGLE_API_KEY"] ?? "").trim().length > 0) out.push("google");
5956
- if ((env["DEEPSEEK_API_KEY"] ?? "").trim().length > 0) out.push("deepseek");
5957
6786
  return out;
5958
6787
  }
5959
6788
  async function loadFactoryAndCallers(injectedEngine, injectedShared) {
@@ -5999,21 +6828,24 @@ async function tryCreateEngineConsensusClientFromEnvAsync(options = {}) {
5999
6828
  anthropic: loaded.shared.callAnthropicWithMetrics,
6000
6829
  openai: loaded.shared.callOpenAIWithMetrics,
6001
6830
  google: loaded.shared.callGeminiWithMetrics,
6002
- deepseek: loaded.shared.callDeepSeekWithMetrics
6831
+ deepseek: loaded.shared.callDeepSeekWithMetrics,
6832
+ meta: options.metaCaller ?? callMetaWithMetrics
6003
6833
  };
6004
6834
  const modelByProvider = {
6005
6835
  anthropic: options.models?.anthropic ?? DEFAULT_MODELS.anthropic,
6006
6836
  openai: options.models?.openai ?? DEFAULT_MODELS.openai,
6007
6837
  google: options.models?.google ?? DEFAULT_MODELS.google,
6008
- deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek
6838
+ deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek,
6839
+ meta: options.models?.meta ?? DEFAULT_MODELS.meta
6009
6840
  };
6841
+ const adapterEnv = !(env[META_MODEL_API_KEY_ENV] ?? "").trim() && (env[META_MODEL_API_KEY_ALIAS] ?? "").trim() ? { ...env, [META_MODEL_API_KEY_ENV]: env[META_MODEL_API_KEY_ALIAS] } : env;
6010
6842
  const panel = [];
6011
6843
  for (const p of providers) {
6012
6844
  try {
6013
6845
  const adapter = loaded.engine.createAdapter(p, {
6014
6846
  model: modelByProvider[p],
6015
6847
  caller: callerByProvider[p],
6016
- envSource: env
6848
+ envSource: adapterEnv
6017
6849
  });
6018
6850
  panel.push(adapter);
6019
6851
  } catch {
@@ -6170,6 +7002,68 @@ function tryCreateMoatConsensusClientFromEnv(env = process.env, fetchFn) {
6170
7002
  });
6171
7003
  }
6172
7004
 
7005
+ // src/consensus/fallback-client.ts
7006
+ var MIN_VALID_LOCAL_VERDICTS = 2;
7007
+ var INSUFFICIENT_LOCAL_VERDICTS_REASON = "local-panel-insufficient-valid-verdicts";
7008
+ function createConsensusFallbackClient(primary, fallback, options = {}) {
7009
+ return {
7010
+ async run(request) {
7011
+ const primaryResult = await primary.run(request);
7012
+ if (request.signal?.aborted || !primaryResult.ok && primaryResult.reason === CANCELLED_REASON) {
7013
+ return primaryResult;
7014
+ }
7015
+ if (primaryResult.ok) {
7016
+ const validVerdicts = primaryResult.per_model_verdicts.filter(
7017
+ (verdict) => verdict.verdict !== "error"
7018
+ );
7019
+ if (validVerdicts.length >= MIN_VALID_LOCAL_VERDICTS) return primaryResult;
7020
+ options.onFallback?.(INSUFFICIENT_LOCAL_VERDICTS_REASON);
7021
+ return fallback.run(request);
7022
+ }
7023
+ options.onFallback?.(primaryResult.reason);
7024
+ return fallback.run(request);
7025
+ }
7026
+ };
7027
+ }
7028
+
7029
+ // src/consensus/local-credential-env.ts
7030
+ import { createRequire as createRequire2 } from "node:module";
7031
+ var require2 = createRequire2(import.meta.url);
7032
+ var KEY_SERVICE = "algosuite-vo";
7033
+ var KEYCHAIN_TARGETS = [
7034
+ { account: "anthropic-api-key", envVar: "ANTHROPIC_API_KEY" },
7035
+ { account: "openai-api-key", envVar: "OPENAI_API_KEY" },
7036
+ { account: "meta-api-key", envVar: "MODEL_API_KEY" }
7037
+ ];
7038
+ function loadEntryCtor() {
7039
+ try {
7040
+ return require2("@napi-rs/keyring").Entry ?? null;
7041
+ } catch {
7042
+ return null;
7043
+ }
7044
+ }
7045
+ function readKey(EntryCtor, account) {
7046
+ try {
7047
+ return new EntryCtor(KEY_SERVICE, account).getPassword()?.trim() || null;
7048
+ } catch {
7049
+ return null;
7050
+ }
7051
+ }
7052
+ function withLocalConsensusCredentials(baseEnv = process.env, options = {}) {
7053
+ const env = { ...baseEnv };
7054
+ if (!env.OPENAI_API_KEY?.trim() && env.CODEX_API_KEY?.trim()) {
7055
+ env.OPENAI_API_KEY = env.CODEX_API_KEY;
7056
+ }
7057
+ const EntryCtor = options.EntryCtor === void 0 ? loadEntryCtor() : options.EntryCtor;
7058
+ if (!EntryCtor) return env;
7059
+ for (const target of KEYCHAIN_TARGETS) {
7060
+ if (env[target.envVar]?.trim()) continue;
7061
+ const key = readKey(EntryCtor, target.account);
7062
+ if (key) env[target.envVar] = key;
7063
+ }
7064
+ return env;
7065
+ }
7066
+
6173
7067
  // src/cloud/login.ts
6174
7068
  init_credential_store();
6175
7069
  import { createServer as createServer2 } from "node:http";
@@ -6204,7 +7098,7 @@ function processCapture(rawBody, expectedState, store) {
6204
7098
  };
6205
7099
  }
6206
7100
  function captureHtml() {
6207
- return `<!doctype html><html><head><meta charset="utf-8"><title>VO login</title></head>
7101
+ return `<!doctype html><html><head><meta charset="utf-8"><title>AlgoHQ login</title></head>
6208
7102
  <body style="font-family:system-ui;max-width:32rem;margin:4rem auto;text-align:center">
6209
7103
  <h2 id="m">Completing sign-in\u2026</h2>
6210
7104
  <script>
@@ -6220,7 +7114,7 @@ function captureHtml() {
6220
7114
  function defaultOpenBrowser(url) {
6221
7115
  const platform = process.platform;
6222
7116
  if (platform === "win32") {
6223
- spawn2("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore" }).unref();
7117
+ spawn2("rundll32", ["url.dll,FileProtocolHandler", url], { detached: true, stdio: "ignore" }).unref();
6224
7118
  } else if (platform === "darwin") {
6225
7119
  spawn2("open", [url], { detached: true, stdio: "ignore" }).unref();
6226
7120
  } else {
@@ -6235,7 +7129,7 @@ async function runLogin(opts = {}) {
6235
7129
  const nowIso = opts.nowIso ?? (() => (/* @__PURE__ */ new Date()).toISOString());
6236
7130
  const openBrowser = opts.openBrowser ?? defaultOpenBrowser;
6237
7131
  const state = randomBytes(32).toString("base64url");
6238
- return new Promise((resolve, reject) => {
7132
+ return new Promise((resolve3, reject) => {
6239
7133
  let settled = false;
6240
7134
  const finish = (err, result) => {
6241
7135
  if (settled) return;
@@ -6243,7 +7137,7 @@ async function runLogin(opts = {}) {
6243
7137
  clearTimeout(timer);
6244
7138
  server.close();
6245
7139
  if (err) reject(err);
6246
- else resolve(result);
7140
+ else resolve3(result);
6247
7141
  };
6248
7142
  const server = createServer2((req, res) => {
6249
7143
  const url = new URL(req.url ?? "/", "http://127.0.0.1");
@@ -6291,7 +7185,7 @@ async function runLogin(opts = {}) {
6291
7185
  result = { ...capt.email ? { email: capt.email } : {}, credentialPath: path3 };
6292
7186
  }
6293
7187
  res.writeHead(outcome.httpStatus, { "content-type": "text/html; charset=utf-8" });
6294
- res.end(outcome.ok ? "<h2>VO login complete \u2014 you can close this tab.</h2>" : `<h2>Login failed: ${outcome.error}</h2>`);
7188
+ res.end(outcome.ok ? "<h2>AlgoHQ login complete \u2014 you can close this tab.</h2>" : `<h2>Login failed: ${outcome.error}</h2>`);
6295
7189
  finish(outcome.ok ? null : new Error(outcome.error ?? "login failed"), result);
6296
7190
  })();
6297
7191
  });
@@ -6354,10 +7248,11 @@ async function exchangeForVoCredential(opts) {
6354
7248
  }
6355
7249
 
6356
7250
  // src/cli.ts
7251
+ init_common();
6357
7252
  function defaultCacheDbPath() {
6358
7253
  const env = process.env["VO_MCP_DB_PATH"];
6359
7254
  if (env && env.length > 0) return env;
6360
- return join8(homedir6(), ".claude", "vo-mcp-cache.db");
7255
+ return join10(homedir6(), ".claude", "vo-mcp-cache.db");
6361
7256
  }
6362
7257
  async function probeEngineVersion() {
6363
7258
  try {
@@ -6432,11 +7327,23 @@ async function main() {
6432
7327
  const ratchets = createStubRatchetClient();
6433
7328
  const testModule = process.env["VO_MCP_TEST_ENGINE_MODULE"];
6434
7329
  const testClient = testModule !== void 0 && testModule.length > 0 ? await loadTestEngineClient(testModule) : null;
7330
+ const localEnv = withLocalConsensusCredentials();
7331
+ const localProviders = probeProviders(localEnv);
7332
+ for (const key of ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "GOOGLE_API_KEY", "DEEPSEEK_API_KEY", "MODEL_API_KEY"]) {
7333
+ if (!process.env[key] && localEnv[key]) process.env[key] = localEnv[key];
7334
+ }
7335
+ const localConsensus = tryCreateEngineConsensusClientFromEnv({ envSource: localEnv });
6435
7336
  const cloudConsensus = testClient ? null : tryCreateMoatConsensusClientFromEnv();
6436
- if (cloudConsensus) {
6437
- console.error("[vo-mcp] cloud consensus active \u2014 verdicts via vo-moat-plane (no local model keys)");
7337
+ let consensus = testClient ?? localConsensus;
7338
+ if (!testClient && cloudConsensus && localProviders.length >= 2) {
7339
+ console.error(`[vo-mcp] local-first consensus active (${localProviders.join(", ")}); cloud moat is fallback-only`);
7340
+ consensus = createConsensusFallbackClient(localConsensus, cloudConsensus, {
7341
+ onFallback: (reason) => console.error(`[vo-mcp] local consensus unavailable (${reason}); using cloud moat fallback`)
7342
+ });
7343
+ } else if (!testClient && cloudConsensus) {
7344
+ console.error("[vo-mcp] fewer than 2 linked local providers; cloud moat consensus active");
7345
+ consensus = cloudConsensus;
6438
7346
  }
6439
- const consensus = testClient ?? cloudConsensus ?? tryCreateEngineConsensusClientFromEnv();
6440
7347
  let adminCallables = null;
6441
7348
  try {
6442
7349
  adminCallables = buildAdminCallableClientFromEnv();
@@ -6481,10 +7388,32 @@ if (process.argv[2] === "login") {
6481
7388
  console.error(`[vo-mcp] login successful${r.email ? ` as ${r.email}` : ""}. Credential stored at ${r.credentialPath}.`);
6482
7389
  console.error("[vo-mcp] You can now remove VO_CONTROL_PLANE_ADMIN_TOKEN (the god-token) from your MCP config.");
6483
7390
  console.error("[vo-mcp] NOTE: per-user auth requires VO_OPERATOR_ALLOWED_EMAILS (with your email) on the deployed control-plane.");
6484
- process.exit(0);
6485
7391
  }).catch((err) => {
6486
7392
  console.error("[vo-mcp] login failed:", err instanceof Error ? err.message : String(err));
6487
- process.exit(1);
7393
+ process.exitCode = 1;
7394
+ });
7395
+ } else if (process.argv[2] === "sync") {
7396
+ const action = process.argv[3];
7397
+ if (action !== "push" && action !== "pull") {
7398
+ console.error("[vo-mcp] usage: vo-mcp sync <push|pull> [--cwd <path>]");
7399
+ process.exit(2);
7400
+ }
7401
+ const cwdFlag = process.argv.indexOf("--cwd");
7402
+ const cwd = cwdFlag >= 0 && typeof process.argv[cwdFlag + 1] === "string" ? process.argv[cwdFlag + 1] : process.cwd();
7403
+ const sessionId = randomUUID5();
7404
+ Promise.resolve().then(() => (init_sync_config(), sync_config_exports)).then(async ({ runMemorySync: runMemorySync2, isNoopSyncReason: isNoopSyncReason2 }) => {
7405
+ const r = await runMemorySync2(action, cwd, sessionId);
7406
+ if (r.synced) {
7407
+ console.error(`[vo-mcp] sync ${action} ok: ${JSON.stringify(r)}`);
7408
+ } else if (isNoopSyncReason2(r.reason)) {
7409
+ console.error(`[vo-mcp] sync ${action} skipped: ${r.reason}`);
7410
+ } else {
7411
+ console.error(`[vo-mcp] sync ${action} failed: ${r.reason}`);
7412
+ process.exitCode = 1;
7413
+ }
7414
+ }).catch((err) => {
7415
+ console.error("[vo-mcp] sync fatal:", err instanceof Error ? err.message : String(err));
7416
+ process.exitCode = 1;
6488
7417
  });
6489
7418
  } else {
6490
7419
  main().catch((err) => {