@algosuite/vo-mcp 0.2.0-beta.1 → 0.2.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -153
- package/bin/vo-mcp +38 -33
- package/dist/autostart-cli.js +79 -2
- package/dist/autostart-cli.js.map +2 -2
- package/dist/cli.js +1020 -745
- package/dist/cli.js.map +4 -4
- package/dist/index.js +111 -96
- package/dist/index.js.map +3 -3
- package/dist/install-cli.js +88 -26
- package/dist/install-cli.js.map +3 -3
- package/dist/login-cli.js +0 -0
- package/dist/login-cli.js.map +1 -1
- package/dist/pair-cli.js +214 -0
- package/dist/pair-cli.js.map +7 -0
- package/dist/runner-cli.js +1365 -185
- package/dist/runner-cli.js.map +4 -4
- package/dist/set-key-cli.js +170 -0
- package/dist/set-key-cli.js.map +7 -0
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/cloud/auth-token-source.ts", "../src/cloud/keychain.ts", "../src/cloud/credential-store.ts", "../src/server.ts", "../src/tools/common.ts", "../src/logging/events-writer.ts", "../../vo-arch-defaults/src/schema/rule-v1.ts", "../../vo-arch-defaults/src/schema/override-v1.ts", "../../vo-arch-defaults/src/storage/load-bundled.ts", "../../vo-arch-defaults/src/storage/load-override.ts", "../../vo-arch-defaults/src/storage/merge.ts", "../../vo-arch-defaults/src/query/applies-to-stack.ts", "../../vo-arch-defaults/src/query/applies-to-change.ts", "../../vo-arch-defaults/src/query/glob.ts", "../../vo-arch-defaults/src/query/applies-to-files.ts", "../../vo-arch-defaults/src/analyze/diff-parser.ts", "../../vo-arch-defaults/src/analyze/evidence-matchers/regex-matcher.ts", "../../vo-arch-defaults/src/analyze/evidence-matchers/import-detector.ts", "../../vo-arch-defaults/src/analyze/evidence-matchers/file-size.ts", "../../vo-arch-defaults/src/analyze/evidence-matchers/package-json.ts", "../../vo-arch-defaults/src/analyze/evidence-matchers/index.ts", "../../vo-arch-defaults/src/query/staleness.ts", "../../vo-arch-defaults/src/query/run-query.ts", "../../vo-arch-defaults/src/query/corpus-version.ts", "../../vo-arch-defaults/src/query/resolve-stack.ts", "../../vo-arch-defaults/src/pii-sanitize.ts", "../src/modes/local.ts", "../src/tools/check-assertion-strength.ts", "../src/tools/architecture-review-kb-prefilter.ts", "../src/tools/kb-metadata-prefilter.ts", "../src/tools/check-hollow-test.ts", "../src/tools/verify-answer.ts", "../src/consensus/gate-types.ts", "../src/tools/consensus-judgment-source-grounded.ts", "../src/tools/consensus-judgment.ts", "../src/tools/architecture-review.ts", "../../vo-ratchets/src/config.ts", "../../vo-ratchets/src/util/walk.ts", "../../vo-ratchets/src/util/test-files.ts", "../../vo-ratchets/src/detectors/assertion-strength.ts", "../../vo-ratchets/src/detectors/fix-strength.ts", "../../vo-ratchets/src/detectors/hollow-tests.ts", "../../vo-ratchets/src/detectors/qa-honesty.ts", "../../vo-ratchets/src/detectors/verify-answer.ts", "../../vo-ratchets/src/detectors/index.ts", "../../vo-ratchets/src/runner.ts", "../src/tools/check-ratchets.ts", "../src/tools/decompose-dispatch.ts", "../src/cloud/admin-callable-client.ts", "../src/tools/cloud-call.ts", "../src/tools/heal/common-heal.ts", "../src/tools/heal/trigger-heal.ts", "../src/tools/heal/fix-retry.ts", "../src/tools/heal/fix-clear.ts", "../src/tools/heal/stop-workflow.ts", "../src/tools/heal/get-workflow-runs.ts", "../src/tools/pr/common-pr.ts", "../src/tools/pr/list-pending-prs.ts", "../src/tools/pr/merge-pr.ts", "../src/tools/pr/reject-pr.ts", "../src/tools/pr/approve-all-fixes.ts", "../src/tools/pr/reject-and-retry.ts", "../src/tools/pr/review-merge.ts", "../src/tools/session/directive.ts", "../src/tools/session/report-session-state.ts", "../src/tools/session/spawn-successor.ts", "../src/tools/concierge/common-concierge.ts", "../src/tools/concierge/dispatch.ts", "../src/tools/memory/sync-config.ts", "../src/cache/sqlite-cache.ts", "../src/cache/canonicalize.ts", "../src/ratchets/stub-client.ts", "../src/consensus/null-client.ts", "../src/consensus/engine-client.ts", "../src/consensus/engine-options.ts", "../src/consensus/claim-classifier.ts"],
|
|
4
|
-
"sourcesContent": ["/**\r\n * Auth token sources for the cloud admin client \u2014 Increment 3 of the VO Command\r\n * Center unification (`docs/vo/vo-command-center-unification-spec-2026-06-05.md`\r\n * \u00A76): retire the shared god admin-token from the LOCAL install. Instead of a\r\n * single static `VO_CONTROL_PLANE_ADMIN_TOKEN`, the client can authenticate as\r\n * the USER with a short-lived Firebase ID token (the same credential the\r\n * dashboard sends; the control-plane already accepts it for an allow-listed\r\n * operator \u2014 `cloud-run/vo-control-plane/src/firebase-auth.ts`).\r\n *\r\n * Three sources, selected by env (per-user preferred):\r\n * 1. `VO_USER_REFRESH_TOKEN` (+ `VO_FIREBASE_API_KEY`) \u2192 auto-refreshing Firebase\r\n * user token (durable; exchanges the refresh token for fresh ID tokens via\r\n * the PUBLIC securetoken endpoint \u2014 no backend, no god-token, no model keys).\r\n * 2. `VO_USER_ID_TOKEN` \u2192 a raw Firebase ID token (simplest interim; expires ~1h).\r\n * 3. `VO_CONTROL_PLANE_ADMIN_TOKEN` \u2192 the legacy static god-token (back-compat).\r\n *\r\n * FAIL-CLOSED: a source that cannot produce a token returns `null`; the caller\r\n * (admin client) then refuses the request rather than sending an empty Bearer.\r\n *\r\n * NOTE: this slice removes the god-token from the *client config* (the user's\r\n * own credential is sent instead). Acquiring the initial refresh/ID token via a\r\n * browser/device-flow `login` command, and OS-keychain storage, are follow-up\r\n * slices (spec \u00A77); for now the token is supplied via env.\r\n */\r\n\r\n/** A source of a fresh bearer token for the control-plane. */\r\nexport interface AuthTokenSource {\r\n /** Stable label for diagnostics/logs \u2014 NEVER the token value. */\r\n readonly kind: 'admin-token' | 'firebase-id-token' | 'firebase-refresh' | 'vo-credential';\r\n /** Returns a fresh bearer token, or `null` when unavailable (fail-closed). */\r\n getToken(): Promise<string | null>;\r\n}\r\n\r\ntype FetchLike = (\r\n url: string,\r\n init?: { method?: string; headers?: Record<string, string>; body?: string },\r\n) => Promise<{ status: number; text: () => Promise<string> }>;\r\n\r\n/** Public Google endpoint that exchanges a Firebase refresh token for a fresh ID token. */\r\nexport const FIREBASE_SECURETOKEN_URL = 'https://securetoken.googleapis.com/v1/token';\r\n\r\n/**\r\n * The Algosuite Firebase Web API key is HTTP-referrer-restricted, and Google's\r\n * Identity Toolkit / securetoken endpoints reject key'd requests that arrive with\r\n * an EMPTY Referer (HTTP 403 \"Requests from referer <empty> are blocked\"). Browser\r\n * callers send one automatically; Node `fetch` sends none \u2014 so every server-side\r\n * exchange must pin an origin the key's restriction allows. Live-diagnosed\r\n * 2026-06-09: without this header the whole Inc 3a refresh flow (and the Inc 3b.4b\r\n * vocred_ exchange that builds on it) fail-opens silently.\r\n */\r\nexport const FIREBASE_TOKEN_REFERER = 'https://algosuite.ai/';\r\n\r\n/** Refresh this many ms BEFORE the ID token actually expires (clock-skew margin). */\r\nconst REFRESH_SKEW_MS = 60_000;\r\n\r\n/** A fixed, already-available token (the god-token or a pasted ID token). */\r\nexport function createStaticTokenSource(\r\n token: string,\r\n kind: AuthTokenSource['kind'] = 'admin-token',\r\n): AuthTokenSource {\r\n const value = token.trim();\r\n return { kind, getToken: async () => (value.length > 0 ? value : null) };\r\n}\r\n\r\nexport interface FirebaseRefreshTokenSourceOptions {\r\n readonly refreshToken: string;\r\n /** The Firebase Web API key (PUBLIC, not a secret) for the Algosuite project. */\r\n readonly apiKey: string;\r\n /** Injectable clock (ms) for tests; defaults to `Date.now`. */\r\n readonly now?: () => number;\r\n /** Injectable fetch for tests; defaults to the global `fetch`. */\r\n readonly fetchFn?: FetchLike;\r\n}\r\n\r\n/**\r\n * Auto-refreshing Firebase user token. Exchanges the long-lived refresh token\r\n * for short-lived ID tokens via the public securetoken endpoint and caches the\r\n * ID token until shortly before it expires. Fail-closed: any error \u21D2 `null`.\r\n */\r\nexport function createFirebaseRefreshTokenSource(\r\n opts: FirebaseRefreshTokenSourceOptions,\r\n): AuthTokenSource {\r\n const refreshToken = opts.refreshToken.trim();\r\n const apiKey = opts.apiKey.trim();\r\n const now = opts.now ?? (() => Date.now());\r\n const fetchFn: FetchLike = opts.fetchFn ?? (globalThis.fetch as unknown as FetchLike);\r\n\r\n let cachedToken: string | null = null;\r\n let expiresAtMs = 0;\r\n // Collapse concurrent refreshes so N parallel tool calls trigger ONE exchange.\r\n let inFlight: Promise<string | null> | null = null;\r\n\r\n async function refresh(): Promise<string | null> {\r\n try {\r\n const res = await fetchFn(`${FIREBASE_SECURETOKEN_URL}?key=${encodeURIComponent(apiKey)}`, {\r\n method: 'POST',\r\n headers: {\r\n 'content-type': 'application/x-www-form-urlencoded',\r\n referer: FIREBASE_TOKEN_REFERER,\r\n },\r\n body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`,\r\n });\r\n const text = await res.text();\r\n if (res.status < 200 || res.status >= 300) {\r\n cachedToken = null;\r\n return null;\r\n }\r\n const parsed = JSON.parse(text) as { id_token?: unknown; expires_in?: unknown };\r\n const idToken = typeof parsed.id_token === 'string' ? parsed.id_token : '';\r\n if (!idToken) {\r\n cachedToken = null;\r\n return null;\r\n }\r\n const expiresInSec = Number(parsed.expires_in);\r\n const ttlMs = Number.isFinite(expiresInSec) && expiresInSec > 0 ? expiresInSec * 1000 : 3_600_000;\r\n cachedToken = idToken;\r\n expiresAtMs = now() + ttlMs;\r\n return idToken;\r\n } catch {\r\n cachedToken = null;\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n kind: 'firebase-refresh',\r\n async getToken(): Promise<string | null> {\r\n if (cachedToken && now() < expiresAtMs - REFRESH_SKEW_MS) return cachedToken;\r\n if (!inFlight) {\r\n inFlight = refresh().finally(() => {\r\n inFlight = null;\r\n });\r\n }\r\n return inFlight;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Select an auth token source from env. Per-user credentials win over the legacy\r\n * god-token, so a thin install configured with the user's token needs NO\r\n * `VO_CONTROL_PLANE_ADMIN_TOKEN`. Returns `null` when nothing is configured.\r\n * Throws on a half-configured refresh source (refresh token without API key).\r\n */\r\n/** Minimal stored-credential shape this selector consumes (from `vo-mcp login`). */\r\nexport interface StoredRefreshCredential {\r\n /** Firebase refresh token (optional once a scoped vocred_ is minted \u2014 Inc 3b.4b). */\r\n readonly refresh_token?: string;\r\n readonly api_key?: string;\r\n /** Scoped, revocable VO credential (Inc 3b.4b) \u2014 preferred over the raw refresh. */\r\n readonly vo_credential?: string;\r\n}\r\n\r\nexport function createAuthTokenSourceFromEnv(\r\n env: Readonly<Record<string, string | undefined>> = process.env,\r\n fetchFn?: FetchLike,\r\n /**\r\n * Inc 3b: a reader for a stored login credential (`vo-mcp login`). Injected so\r\n * the env-only callers (and tests) stay filesystem-free; production passes\r\n * `readStoredCredential`. A stored login credential is preferred over the legacy\r\n * god-token (so logging in retires it) but explicit env user-tokens still win.\r\n */\r\n readStoredCred: () => StoredRefreshCredential | null = () => null,\r\n): AuthTokenSource | null {\r\n const refreshToken = env['VO_USER_REFRESH_TOKEN']?.trim();\r\n const apiKey = env['VO_FIREBASE_API_KEY']?.trim();\r\n const idToken = env['VO_USER_ID_TOKEN']?.trim();\r\n const adminToken = env['VO_CONTROL_PLANE_ADMIN_TOKEN']?.trim();\r\n\r\n // 1. explicit env per-user refresh (CI / override).\r\n if (refreshToken || apiKey) {\r\n if (!refreshToken || !apiKey) {\r\n throw new Error(\r\n 'Per-user refresh auth requires BOTH VO_USER_REFRESH_TOKEN and VO_FIREBASE_API_KEY',\r\n );\r\n }\r\n return createFirebaseRefreshTokenSource({\r\n refreshToken,\r\n apiKey,\r\n ...(fetchFn ? { fetchFn } : {}),\r\n });\r\n }\r\n // 2. explicit env ID token.\r\n if (idToken) return createStaticTokenSource(idToken, 'firebase-id-token');\r\n // 3. stored login credential (`vo-mcp login`) \u2014 preferred over the god-token.\r\n const stored = readStoredCred();\r\n // 3a. a scoped vocred_ (Inc 3b.4b) wins \u2014 revocable, server-validated, and it\r\n // means no raw Firebase refresh token sits on disk. Sent as a static bearer;\r\n // the control-plane validates expiry/revocation (a stale one \u21D2 401 \u21D2 re-login).\r\n if (stored?.vo_credential && stored.vo_credential.trim()) {\r\n return createStaticTokenSource(stored.vo_credential.trim(), 'vo-credential');\r\n }\r\n // 3b. else the Firebase refresh credential.\r\n if (stored && stored.refresh_token?.trim() && stored.api_key?.trim()) {\r\n return createFirebaseRefreshTokenSource({\r\n refreshToken: stored.refresh_token.trim(),\r\n apiKey: stored.api_key.trim(),\r\n ...(fetchFn ? { fetchFn } : {}),\r\n });\r\n }\r\n // 4. legacy static god-token (back-compat).\r\n if (adminToken) return createStaticTokenSource(adminToken, 'admin-token');\r\n return null;\r\n}\r\n", "/**\r\n * Optional OS-keychain backend for the thin-client credential store (Increment\r\n * 3b.3 \u2014 `docs/vo/vo-command-center-inc3b-login-design-2026-06-05.md` \u00A75/\u00A76).\r\n *\r\n * Loads `@napi-rs/keyring` at runtime via `createRequire`, so it is a TRUE\r\n * optional dependency: if the native module is absent or fails to load\r\n * (unsupported platform, prebuilt binary missing, headless CI), every function\r\n * degrades to a no-op and the caller (`credential-store.ts`) falls back to the\r\n * 0600 file store. `@napi-rs/keyring`'s `Entry` API is SYNCHRONOUS, so the\r\n * credential store stays synchronous \u2014 no async ripple into the Inc-3a token\r\n * source that reads it.\r\n *\r\n * Why `createRequire` and not a static/dynamic `import`: a static import would\r\n * make the native module a HARD dependency (a missing prebuilt would crash the\r\n * MCP at startup); a dynamic `import()` is async (would force the whole read\r\n * path async). `createRequire(...)` inside a try/catch loads it lazily and\r\n * synchronously, and a load failure is just \"keychain unavailable\".\r\n */\r\nimport { createRequire } from 'node:module';\r\n\r\n/** Keychain service + account the single refresh credential is stored under. */\r\nconst SERVICE = 'vo-mcp';\r\nconst ACCOUNT = 'refresh-credential';\r\n\r\ninterface KeyringEntry {\r\n getPassword(): string | null;\r\n setPassword(password: string): void;\r\n deletePassword(): boolean;\r\n}\r\ninterface KeyringModule {\r\n Entry: new (service: string, account: string) => KeyringEntry;\r\n}\r\n\r\n// undefined = not yet attempted; null = attempted and unavailable.\r\nlet cached: KeyringModule | null | undefined;\r\n\r\nfunction loadKeyring(): KeyringModule | null {\r\n if (cached !== undefined) return cached;\r\n try {\r\n const req = createRequire(import.meta.url);\r\n const mod = req('@napi-rs/keyring') as Partial<KeyringModule>;\r\n cached = mod && typeof mod.Entry === 'function' ? (mod as KeyringModule) : null;\r\n } catch {\r\n cached = null;\r\n }\r\n return cached;\r\n}\r\n\r\n/** True when the OS keychain backend is usable in this runtime. */\r\nexport function keychainAvailable(): boolean {\r\n return loadKeyring() !== null;\r\n}\r\n\r\n/** Read the raw stored secret string from the OS keychain, or null. Never throws. */\r\nexport function keychainGet(): string | null {\r\n const k = loadKeyring();\r\n if (!k) return null;\r\n try {\r\n return new k.Entry(SERVICE, ACCOUNT).getPassword();\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/** Store the raw secret string in the OS keychain. Returns true on success. Never throws. */\r\nexport function keychainSet(secret: string): boolean {\r\n const k = loadKeyring();\r\n if (!k) return false;\r\n try {\r\n new k.Entry(SERVICE, ACCOUNT).setPassword(secret);\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Delete the stored secret from the OS keychain. Returns true if an entry was\r\n * removed. Never throws \u2014 a no-op (and `false`) when the backend is unavailable\r\n * or the entry is absent. Used to keep ONE source of truth: when the credential\r\n * is (re)written to the file, any stale keychain entry is cleared so it can't\r\n * shadow the newer file credential on read (and vice-versa).\r\n */\r\nexport function keychainDelete(): boolean {\r\n const k = loadKeyring();\r\n if (!k) return false;\r\n try {\r\n return new k.Entry(SERVICE, ACCOUNT).deletePassword();\r\n } catch {\r\n return false;\r\n }\r\n}\r\n\r\n/** Test-only seam to reset the memoised module load. */\r\nexport function __resetKeychainCache(): void {\r\n cached = undefined;\r\n}\r\n", "/**\r\n * Local credential store for the thin-client `vo-mcp login` flow (Increment 3b,\r\n * Option A \u2014 `docs/vo/vo-command-center-inc3b-login-design-2026-06-05.md`).\r\n *\r\n * Persists the per-user Firebase refresh token (the user's OWN credential, never\r\n * the god-token, never model keys) captured by `login`, so the auto-refreshing\r\n * token source (Inc 3a) can mint fresh ID tokens across MCP restarts.\r\n *\r\n * Storage precedence (Inc 3b.3):\r\n * 1. **OS keychain** (Windows Credential Manager / macOS Keychain / libsecret)\r\n * via the optional `@napi-rs/keyring` backend (`keychain.ts`). The DEFAULT\r\n * when available \u2014 the secret never lands in plaintext on disk.\r\n * 2. **0600 file** at `$VO_MCP_CREDENTIALS_PATH` or `~/.config/vo-mcp/credentials.json`.\r\n * The fallback when the keychain is unavailable or disabled\r\n * (`VO_MCP_DISABLE_KEYCHAIN`). `VO_MCP_CREDENTIALS_PATH` only sets the file\r\n * LOCATION; force file storage with `VO_MCP_DISABLE_KEYCHAIN`.\r\n *\r\n * `env`-supplied tokens (`VO_USER_REFRESH_TOKEN`, etc.) still win over BOTH\r\n * stores \u2014 that precedence lives upstream in `auth-token-source.ts`.\r\n *\r\n * **Single source of truth.** The credential lives in EITHER the keychain OR the\r\n * file, never both: a write to one store CLEARS the other, so a stale entry can\r\n * never shadow the current credential on read, and the secret never lingers in\r\n * plaintext after a migration to the keychain.\r\n *\r\n * **Keychain durability.** A keychain-stored credential is only readable while\r\n * the `@napi-rs/keyring` native module loads. If the module later becomes\r\n * unavailable (an ABI break across a Node upgrade, a corrupted install), the\r\n * credential can't be read and the user re-runs `vo-mcp login` \u2014 the same\r\n * behaviour as `gh` / `gcloud` / `firebase` keychain storage. We deliberately do\r\n * NOT mirror the secret to a plaintext file as a fallback: that would defeat the\r\n * entire point of keychain storage (keeping the secret off plaintext disk).\r\n */\r\nimport { homedir } from 'node:os';\r\nimport { join, dirname } from 'node:path';\r\nimport {\r\n existsSync,\r\n mkdirSync,\r\n readFileSync,\r\n writeFileSync,\r\n chmodSync,\r\n rmSync,\r\n} from 'node:fs';\r\n\r\nimport { keychainAvailable, keychainGet, keychainSet, keychainDelete } from './keychain.js';\r\n\r\nexport interface StoredCredential {\r\n /**\r\n * Firebase refresh token (long-lived; exchanged for short-lived ID tokens).\r\n * OPTIONAL since Inc 3b.4b: once a scoped `vo_credential` is minted, the raw\r\n * refresh token is dropped, so a stored credential may carry ONLY the vocred_.\r\n */\r\n readonly refresh_token?: string;\r\n /** Firebase Web API key (PUBLIC) needed for the securetoken refresh exchange. */\r\n readonly api_key?: string;\r\n /**\r\n * Scoped, revocable VO credential (`vocred_`) minted by the control-plane\r\n * (Inc 3b.4b). Preferred over the raw refresh token; lets the client present a\r\n * revocable, server-side credential instead of the Firebase refresh token.\r\n */\r\n readonly vo_credential?: string;\r\n /** ISO-8601 expiry of `vo_credential` (the client re-logs-in past this). */\r\n readonly vo_credential_expires_at?: string;\r\n /** The signed-in operator email (diagnostics only). */\r\n readonly email?: string;\r\n /** ISO timestamp the credential was stored. */\r\n readonly stored_at?: string;\r\n}\r\n\r\n/**\r\n * Pluggable OS-keychain backend. Defaults to the real `@napi-rs/keyring` wrapper;\r\n * tests inject a deterministic fake so they never touch the host keychain.\r\n */\r\nexport interface KeychainBackend {\r\n available(): boolean;\r\n get(): string | null;\r\n set(secret: string): boolean;\r\n delete(): boolean;\r\n}\r\n\r\nconst realKeychain: KeychainBackend = {\r\n available: keychainAvailable,\r\n get: keychainGet,\r\n set: keychainSet,\r\n delete: keychainDelete,\r\n};\r\n\r\n/** Human-readable \"location\" returned when the credential was stored in the OS keychain. */\r\nexport const KEYCHAIN_LOCATION = 'OS keychain (service \"vo-mcp\")';\r\n\r\n/** Resolve the credentials file path (env override \u2192 XDG-ish default under home). */\r\nexport function credentialPath(env: Readonly<Record<string, string | undefined>> = process.env): string {\r\n const override = env['VO_MCP_CREDENTIALS_PATH']?.trim();\r\n if (override) return override;\r\n return join(homedir(), '.config', 'vo-mcp', 'credentials.json');\r\n}\r\n\r\n/**\r\n * Whether the keychain should be consulted at all (read OR write). False when the\r\n * native backend is unavailable or `VO_MCP_DISABLE_KEYCHAIN` is set (CI/headless).\r\n */\r\nfunction keychainEnabled(\r\n env: Readonly<Record<string, string | undefined>>,\r\n keychain: KeychainBackend,\r\n): boolean {\r\n const disabled = (env['VO_MCP_DISABLE_KEYCHAIN'] ?? '').trim().toLowerCase();\r\n if (disabled === '1' || disabled === 'true' || disabled === 'yes') return false;\r\n return keychain.available();\r\n}\r\n\r\n/** Parse + validate a stored credential blob. Returns null on any problem (never throws). */\r\nfunction deserialize(raw: string): StoredCredential | null {\r\n try {\r\n const parsed = JSON.parse(raw) as Partial<StoredCredential>;\r\n const refresh = typeof parsed.refresh_token === 'string' ? parsed.refresh_token.trim() : '';\r\n const apiKey = typeof parsed.api_key === 'string' ? parsed.api_key.trim() : '';\r\n const voCred = typeof parsed.vo_credential === 'string' ? parsed.vo_credential.trim() : '';\r\n // Valid if it carries a scoped vocred_ OR a full Firebase refresh pair.\r\n if (!voCred && (!refresh || !apiKey)) return null;\r\n return {\r\n ...(refresh ? { refresh_token: refresh } : {}),\r\n ...(apiKey ? { api_key: apiKey } : {}),\r\n ...(voCred ? { vo_credential: voCred } : {}),\r\n ...(typeof parsed.vo_credential_expires_at === 'string' ? { vo_credential_expires_at: parsed.vo_credential_expires_at } : {}),\r\n ...(typeof parsed.email === 'string' ? { email: parsed.email } : {}),\r\n ...(typeof parsed.stored_at === 'string' ? { stored_at: parsed.stored_at } : {}),\r\n };\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nfunction readFromFile(env: Readonly<Record<string, string | undefined>>): StoredCredential | null {\r\n try {\r\n const p = credentialPath(env);\r\n if (!existsSync(p)) return null;\r\n return deserialize(readFileSync(p, 'utf8'));\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/**\r\n * Read the stored credential, or `null` if absent/unreadable/invalid (never\r\n * throws). Consults an ENABLED keychain first (regardless of the write-target\r\n * flags, so a credential written to the keychain is found even if\r\n * `VO_MCP_CREDENTIALS_PATH` is later set), then the 0600 file.\r\n */\r\nexport function readStoredCredential(\r\n env: Readonly<Record<string, string | undefined>> = process.env,\r\n keychain: KeychainBackend = realKeychain,\r\n): StoredCredential | null {\r\n if (keychainEnabled(env, keychain)) {\r\n const raw = keychain.get();\r\n const fromKeychain = raw ? deserialize(raw) : null;\r\n if (fromKeychain) return fromKeychain;\r\n }\r\n return readFromFile(env);\r\n}\r\n\r\nfunction deleteFile(env: Readonly<Record<string, string | undefined>>): void {\r\n try {\r\n rmSync(credentialPath(env), { force: true });\r\n } catch {\r\n /* best-effort */\r\n }\r\n}\r\n\r\nfunction writeToFile(\r\n payload: StoredCredential,\r\n env: Readonly<Record<string, string | undefined>>,\r\n): string {\r\n const p = credentialPath(env);\r\n mkdirSync(dirname(p), { recursive: true });\r\n writeFileSync(p, `${JSON.stringify(payload, null, 2)}\\n`, { mode: 0o600 });\r\n // Best-effort tighten (no-op / throws on some Windows filesystems \u2014 ignore).\r\n try {\r\n chmodSync(p, 0o600);\r\n } catch {\r\n /* best-effort */\r\n }\r\n return p;\r\n}\r\n\r\n/**\r\n * Persist the credential. Prefers the OS keychain (secret never hits plaintext\r\n * disk); otherwise writes the 0600 file. Writing to one store CLEARS the other\r\n * (single source of truth \u2014 no stale shadow, no lingering plaintext). Returns the\r\n * location it was stored (`KEYCHAIN_LOCATION` or the file path).\r\n */\r\nexport function writeStoredCredential(\r\n cred: StoredCredential,\r\n storedAt: string,\r\n env: Readonly<Record<string, string | undefined>> = process.env,\r\n keychain: KeychainBackend = realKeychain,\r\n): string {\r\n const payload: StoredCredential = {\r\n ...(cred.refresh_token ? { refresh_token: cred.refresh_token } : {}),\r\n ...(cred.api_key ? { api_key: cred.api_key } : {}),\r\n ...(cred.vo_credential ? { vo_credential: cred.vo_credential } : {}),\r\n ...(cred.vo_credential_expires_at ? { vo_credential_expires_at: cred.vo_credential_expires_at } : {}),\r\n ...(cred.email ? { email: cred.email } : {}),\r\n stored_at: cred.stored_at ?? storedAt,\r\n };\r\n if (keychainEnabled(env, keychain) && keychain.set(JSON.stringify(payload))) {\r\n // Stored in the keychain \u2192 clear any stale plaintext file so the secret\r\n // doesn't linger on disk and can't shadow the keychain on read.\r\n deleteFile(env);\r\n return KEYCHAIN_LOCATION;\r\n }\r\n const p = writeToFile(payload, env);\r\n // Stored in the file \u2192 clear any stale keychain entry so it can't shadow the\r\n // newer file credential on read.\r\n if (keychainEnabled(env, keychain)) keychain.delete();\r\n return p;\r\n}\r\n", "/**\r\n * VO MCP server \u2014 registration and request handling.\r\n *\r\n * Built against `@modelcontextprotocol/sdk` 1.29.x. Stdio transport.\r\n *\r\n * Responsibilities:\r\n * 1. Build the `ToolDeps` runtime object (cache, events writer, ratchet\r\n * client, session context).\r\n * 2. Register tool definitions (name, description, input schema) for\r\n * `tools/list`.\r\n * 3. Dispatch `tools/call` invocations to the per-tool handlers.\r\n *\r\n * Cross-vendor design: nothing in here assumes a Claude-specific client.\r\n * `client_id` is read off the `initialize` handshake (`getClientVersion()`).\r\n */\r\nimport { randomUUID } from 'node:crypto';\r\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\r\nimport {\r\n CallToolRequestSchema,\r\n ListToolsRequestSchema,\r\n type Tool,\r\n} from '@modelcontextprotocol/sdk/types.js';\r\nimport { methodNotFound } from './tools/common.js';\r\n\r\nimport type { ContentHashCache } from './cache/sqlite-cache.js';\r\nimport type { AdminCallableClient } from './cloud/admin-callable-client.js';\r\nimport type { EventsWriter } from './logging/events-writer.js';\r\nimport type { RatchetClient } from './ratchets/client.js';\r\nimport type { ConsensusEngineClient } from './consensus/client.js';\r\nimport { createLocalMode } from './modes/local.js';\r\nimport type { SessionContext, ToolDeps } from './types.js';\r\n\r\nimport * as checkAssertionStrength from './tools/check-assertion-strength.js';\r\nimport * as checkHollowTest from './tools/check-hollow-test.js';\r\nimport * as verifyAnswer from './tools/verify-answer.js';\r\nimport * as consensusJudgment from './tools/consensus-judgment.js';\r\nimport * as architectureReview from './tools/architecture-review.js';\r\nimport * as checkRatchets from './tools/check-ratchets.js';\r\nimport * as decomposeDispatch from './tools/decompose-dispatch.js';\r\nimport * as triggerHeal from './tools/heal/trigger-heal.js';\r\nimport * as fixRetry from './tools/heal/fix-retry.js';\r\nimport * as fixClear from './tools/heal/fix-clear.js';\r\nimport * as stopWorkflow from './tools/heal/stop-workflow.js';\r\nimport * as getWorkflowRuns from './tools/heal/get-workflow-runs.js';\r\nimport * as listPendingPRs from './tools/pr/list-pending-prs.js';\r\nimport * as mergePR from './tools/pr/merge-pr.js';\r\nimport * as rejectPR from './tools/pr/reject-pr.js';\r\nimport * as approveAllFixes from './tools/pr/approve-all-fixes.js';\r\nimport * as rejectAndRetry from './tools/pr/reject-and-retry.js';\r\nimport * as reviewMerge from './tools/pr/review-merge.js';\r\nimport * as reportSessionState from './tools/session/report-session-state.js';\r\nimport * as spawnSuccessor from './tools/session/spawn-successor.js';\r\nimport * as conciergeDispatch from './tools/concierge/dispatch.js';\r\nimport * as syncConfig from './tools/memory/sync-config.js';\r\n\r\nexport interface CreateServerOptions {\r\n readonly cache: ContentHashCache;\r\n readonly events: EventsWriter;\r\n readonly ratchets: RatchetClient;\r\n readonly consensus: ConsensusEngineClient;\r\n /** Optional injection for tests; defaults to `() => new Date()`. */\r\n readonly now?: () => Date;\r\n /** Optional session ID; defaults to a fresh uuid. */\r\n readonly sessionId?: string;\r\n /**\r\n * Optional cloud-mode admin proxy client. When set, heal/PR family\r\n * tools forward to vo-control-plane's `/api/v1/admin/*` endpoints\r\n * instead of returning their stub envelopes. Wired by `cli.ts` when\r\n * `VO_CONTROL_PLANE_URL` + `VO_CONTROL_PLANE_ADMIN_TOKEN` are present.\r\n */\r\n readonly adminCallables?: AdminCallableClient | null;\r\n}\r\n\r\ntype ToolHandler = (\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n /**\r\n * Cancellation signal threaded from the MCP SDK's `RequestHandlerExtra`.\r\n * Fires when the client sends `notifications/cancelled` for this request,\r\n * or when the transport closes mid-call. Tool handlers MUST pass it down\r\n * to any awaited long-running work (consensus engine call, ratchet probe)\r\n * so per-model API fetches can abort. Added 2026-05-25 (audit HIGH \u2014\r\n * MCP `notifications/cancelled` handling).\r\n */\r\n signal?: AbortSignal,\r\n) => Promise<{\r\n content: Array<{ type: 'text'; text: string }>;\r\n}>;\r\n\r\ninterface RegisteredTool {\r\n readonly definition: Tool;\r\n readonly handler: ToolHandler;\r\n}\r\n\r\nfunction buildToolRegistry(): Record<string, RegisteredTool> {\r\n return {\r\n [checkAssertionStrength.TOOL_NAME]: {\r\n definition: {\r\n name: checkAssertionStrength.TOOL_NAME,\r\n description: checkAssertionStrength.description,\r\n inputSchema: checkAssertionStrength.inputSchema,\r\n },\r\n handler: checkAssertionStrength.handleCheckAssertionStrength,\r\n },\r\n [checkHollowTest.TOOL_NAME]: {\r\n definition: {\r\n name: checkHollowTest.TOOL_NAME,\r\n description: checkHollowTest.description,\r\n inputSchema: checkHollowTest.inputSchema,\r\n },\r\n handler: checkHollowTest.handleCheckHollowTest,\r\n },\r\n [verifyAnswer.TOOL_NAME]: {\r\n definition: {\r\n name: verifyAnswer.TOOL_NAME,\r\n description: verifyAnswer.description,\r\n inputSchema: verifyAnswer.inputSchema,\r\n },\r\n handler: verifyAnswer.handleVerifyAnswer,\r\n },\r\n [consensusJudgment.TOOL_NAME]: {\r\n definition: {\r\n name: consensusJudgment.TOOL_NAME,\r\n description: consensusJudgment.description,\r\n inputSchema: consensusJudgment.inputSchema,\r\n },\r\n handler: consensusJudgment.handleConsensusJudgment,\r\n },\r\n [architectureReview.TOOL_NAME]: {\r\n definition: {\r\n name: architectureReview.TOOL_NAME,\r\n description: architectureReview.description,\r\n inputSchema: architectureReview.inputSchema,\r\n },\r\n handler: architectureReview.handleArchitectureReview,\r\n },\r\n [decomposeDispatch.TOOL_NAME]: {\r\n definition: {\r\n name: decomposeDispatch.TOOL_NAME,\r\n description: decomposeDispatch.description,\r\n inputSchema: decomposeDispatch.inputSchema,\r\n },\r\n handler: decomposeDispatch.handleDecomposeDispatch,\r\n },\r\n [checkRatchets.TOOL_NAME]: {\r\n definition: {\r\n name: checkRatchets.TOOL_NAME,\r\n description: checkRatchets.description,\r\n inputSchema: checkRatchets.inputSchema,\r\n },\r\n handler: checkRatchets.handleCheckRatchets,\r\n },\r\n [triggerHeal.TOOL_NAME]: {\r\n definition: {\r\n name: triggerHeal.TOOL_NAME,\r\n description: triggerHeal.description,\r\n inputSchema: triggerHeal.inputSchema,\r\n },\r\n handler: triggerHeal.handleTriggerHeal,\r\n },\r\n [fixRetry.TOOL_NAME]: {\r\n definition: {\r\n name: fixRetry.TOOL_NAME,\r\n description: fixRetry.description,\r\n inputSchema: fixRetry.inputSchema,\r\n },\r\n handler: fixRetry.handleFixRetry,\r\n },\r\n [fixClear.TOOL_NAME]: {\r\n definition: {\r\n name: fixClear.TOOL_NAME,\r\n description: fixClear.description,\r\n inputSchema: fixClear.inputSchema,\r\n },\r\n handler: fixClear.handleFixClear,\r\n },\r\n [stopWorkflow.TOOL_NAME]: {\r\n definition: {\r\n name: stopWorkflow.TOOL_NAME,\r\n description: stopWorkflow.description,\r\n inputSchema: stopWorkflow.inputSchema,\r\n },\r\n handler: stopWorkflow.handleStopWorkflow,\r\n },\r\n [getWorkflowRuns.TOOL_NAME]: {\r\n definition: {\r\n name: getWorkflowRuns.TOOL_NAME,\r\n description: getWorkflowRuns.description,\r\n inputSchema: getWorkflowRuns.inputSchema,\r\n },\r\n handler: getWorkflowRuns.handleGetWorkflowRuns,\r\n },\r\n [listPendingPRs.TOOL_NAME]: {\r\n definition: {\r\n name: listPendingPRs.TOOL_NAME,\r\n description: listPendingPRs.description,\r\n inputSchema: listPendingPRs.inputSchema,\r\n },\r\n handler: listPendingPRs.handleListPendingPRs,\r\n },\r\n [mergePR.TOOL_NAME]: {\r\n definition: {\r\n name: mergePR.TOOL_NAME,\r\n description: mergePR.description,\r\n inputSchema: mergePR.inputSchema,\r\n },\r\n handler: mergePR.handleMergePR,\r\n },\r\n [rejectPR.TOOL_NAME]: {\r\n definition: {\r\n name: rejectPR.TOOL_NAME,\r\n description: rejectPR.description,\r\n inputSchema: rejectPR.inputSchema,\r\n },\r\n handler: rejectPR.handleRejectPR,\r\n },\r\n [approveAllFixes.TOOL_NAME]: {\r\n definition: {\r\n name: approveAllFixes.TOOL_NAME,\r\n description: approveAllFixes.description,\r\n inputSchema: approveAllFixes.inputSchema,\r\n },\r\n handler: approveAllFixes.handleApproveAllFixes,\r\n },\r\n [rejectAndRetry.TOOL_NAME]: {\r\n definition: {\r\n name: rejectAndRetry.TOOL_NAME,\r\n description: rejectAndRetry.description,\r\n inputSchema: rejectAndRetry.inputSchema,\r\n },\r\n handler: rejectAndRetry.handleRejectAndRetry,\r\n },\r\n [reviewMerge.TOOL_NAME]: {\r\n definition: {\r\n name: reviewMerge.TOOL_NAME,\r\n description: reviewMerge.description,\r\n inputSchema: reviewMerge.inputSchema,\r\n },\r\n handler: reviewMerge.handleReviewMerge,\r\n },\r\n [reportSessionState.TOOL_NAME]: {\r\n definition: {\r\n name: reportSessionState.TOOL_NAME,\r\n description: reportSessionState.description,\r\n inputSchema: reportSessionState.inputSchema,\r\n },\r\n handler: reportSessionState.handleReportSessionState,\r\n },\r\n [spawnSuccessor.TOOL_NAME]: {\r\n definition: {\r\n name: spawnSuccessor.TOOL_NAME,\r\n description: spawnSuccessor.description,\r\n inputSchema: spawnSuccessor.inputSchema,\r\n },\r\n handler: spawnSuccessor.handleSpawnSuccessor,\r\n },\r\n [conciergeDispatch.TOOL_NAME]: {\r\n definition: {\r\n name: conciergeDispatch.TOOL_NAME,\r\n description: conciergeDispatch.description,\r\n inputSchema: conciergeDispatch.inputSchema,\r\n },\r\n handler: conciergeDispatch.handleConciergeDispatch,\r\n },\r\n [syncConfig.TOOL_NAME]: {\r\n definition: {\r\n name: syncConfig.TOOL_NAME,\r\n description: syncConfig.description,\r\n inputSchema: syncConfig.inputSchema,\r\n },\r\n handler: syncConfig.handleSyncConfig,\r\n },\r\n };\r\n}\r\n\r\nexport function createServer(options: CreateServerOptions): Server {\r\n const sessionId = options.sessionId ?? randomUUID();\r\n const mode = createLocalMode();\r\n const now = options.now ?? ((): Date => new Date());\r\n\r\n const server = new Server(\r\n {\r\n name: 'vo-mcp',\r\n version: '0.1.0',\r\n },\r\n {\r\n capabilities: {\r\n tools: {},\r\n },\r\n },\r\n );\r\n\r\n const registry = buildToolRegistry();\r\n\r\n server.setRequestHandler(ListToolsRequestSchema, async () => {\r\n return {\r\n tools: Object.values(registry).map((t) => t.definition),\r\n };\r\n });\r\n\r\n server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {\r\n const toolName = req.params.name;\r\n const entry = registry[toolName];\r\n if (!entry) {\r\n throw methodNotFound(toolName, Object.keys(registry));\r\n }\r\n\r\n // The MCP SDK exposes the connected client's identity via\r\n // `server.getClientVersion()`. Read it lazily per-call so tests\r\n // that don't go through a transport still work (clientId defaults\r\n // to 'unknown' in that case).\r\n const clientInfo = server.getClientVersion();\r\n const session: SessionContext = {\r\n sessionId,\r\n clientId: clientInfo?.name ?? 'unknown',\r\n mode: mode.mode,\r\n tenantId: mode.tenantId,\r\n operatorId: mode.operatorId,\r\n };\r\n\r\n const deps: ToolDeps = {\r\n session,\r\n cache: options.cache,\r\n events: options.events,\r\n ratchets: options.ratchets,\r\n consensus: options.consensus,\r\n now,\r\n adminCallables: options.adminCallables ?? null,\r\n };\r\n\r\n // Forward the SDK-provided cancellation signal so tool handlers can\r\n // thread it into the consensus engine call (and any other long-\r\n // running work). The SDK auto-fires this signal when the connected\r\n // client sends `notifications/cancelled` for this request, or when\r\n // the transport closes mid-call. 2026-05-25 audit HIGH \u2014 MCP\r\n // `notifications/cancelled` handling.\r\n return entry.handler(deps, req.params.arguments ?? {}, extra?.signal);\r\n });\r\n\r\n return server;\r\n}\r\n\r\n/** Exported for tests \u2014 the in-process tool registry. */\r\nexport function listToolNames(): readonly string[] {\r\n return Object.keys(buildToolRegistry());\r\n}\r\n", "/**\r\n * Shared helpers for tool implementations.\r\n */\r\nimport { createHash, randomUUID } from 'node:crypto';\r\nimport { readFileSync } from 'node:fs';\r\nimport { dirname, join } from 'node:path';\r\nimport { fileURLToPath } from 'node:url';\r\nimport type { Tool } from '@modelcontextprotocol/sdk/types.js';\r\nimport { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';\r\nimport type {\r\n ConsensusCallEvent,\r\n PerModelVerdict,\r\n SessionContext,\r\n SynthesizedVerdict,\r\n} from '../types.js';\r\nimport { sanitizeExcerpt } from '../logging/events-writer.js';\r\nimport type {\r\n ConsensusPerModelVerdict,\r\n ConsensusSuccess,\r\n ConsensusSynthesizedVerdict,\r\n} from '../consensus/client.js';\r\n\r\n/**\r\n * vo-mcp package semver \u2014 populated once at module init from package.json.\r\n * Threaded into every `ConsensusCallEvent` as `vo_mcp_version` so cloud\r\n * ingestion can correlate event-shape drift with package releases.\r\n * Falls back to `0.0.0-unknown` if package.json can't be read (test isolation).\r\n */\r\nfunction readVoMcpVersion(): string {\r\n try {\r\n const here = dirname(fileURLToPath(import.meta.url));\r\n // common.ts lives at src/tools/ (source) or dist/tools/ (built); package.json\r\n // sits two directories up in either layout.\r\n const pkgPath = join(here, '..', '..', 'package.json');\r\n const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { version?: string };\r\n return typeof pkg.version === 'string' ? pkg.version : '0.0.0-unknown';\r\n } catch {\r\n return '0.0.0-unknown';\r\n }\r\n}\r\n\r\nexport const VO_MCP_VERSION: string = readVoMcpVersion();\r\n\r\n/** UTF-8 byte length of a string. Use for `input_size_bytes` on events. */\r\nexport function bytesOf(s: string): number {\r\n return Buffer.byteLength(s, 'utf8');\r\n}\r\n\r\n/**\r\n * sha256 of an arbitrary string, hex-encoded. Used for `raw_response_hash`.\r\n * Currently hashes the response excerpt the engine returns (the engine\r\n * doesn't yet thread the full raw response); will hash full text once the\r\n * engine adds that opt-in.\r\n */\r\nexport function sha256Hex(s: string): string {\r\n return createHash('sha256').update(s, 'utf8').digest('hex');\r\n}\r\n\r\n/** The shape MCP's `Tool.inputSchema` requires. Re-exported so tool files import it. */\r\nexport type ToolInputSchema = Tool['inputSchema'];\r\n\r\n/**\r\n * Throw an MCP-compliant `InvalidParams` (-32602) error for a bad tool input.\r\n *\r\n * Cross-vendor MCP clients (Cursor, Continue, Codex) branch on `error.code`\r\n * per the JSON-RPC spec; throwing a plain Node `Error` surfaces as `-32603`\r\n * (internal error) instead, breaking automated recovery. Tool input\r\n * validators MUST use this helper, not `throw new Error(...)`.\r\n */\r\nexport function invalidParams(toolName: string, message: string): McpError {\r\n return new McpError(ErrorCode.InvalidParams, `${toolName}: ${message}`);\r\n}\r\n\r\n/**\r\n * Throw an MCP-compliant `MethodNotFound` (-32601) error for an unregistered tool.\r\n */\r\nexport function methodNotFound(toolName: string, knownTools: readonly string[]): McpError {\r\n return new McpError(\r\n ErrorCode.MethodNotFound,\r\n `Unknown tool: ${toolName}. Registered tools: ${knownTools.join(', ')}`,\r\n );\r\n}\r\n\r\n/**\r\n * Per-field input-size cap. Throws `InvalidParams` (-32602) with a precise\r\n * over-by-N-bytes message so the caller can trim and retry. Byte-counted\r\n * (not char-counted) to bound real wire/memory cost.\r\n *\r\n * Caller picks the cap per field \u2014 there is no global default because the\r\n * right cap depends on the tool's gate (a 1MB diff is fine for architecture\r\n * review; a 1MB consensus prompt would blow the model context window).\r\n */\r\nexport function assertWithinByteCap(\r\n toolName: string,\r\n fieldName: string,\r\n value: string,\r\n maxBytes: number,\r\n): void {\r\n const bytes = Buffer.byteLength(value, 'utf8');\r\n if (bytes > maxBytes) {\r\n throw invalidParams(\r\n toolName,\r\n `input field '${fieldName}' exceeds ${maxBytes}-byte cap (got ${bytes} bytes). Trim the input or split across multiple calls.`,\r\n );\r\n }\r\n}\r\n\r\nexport interface BuildEventArgs {\r\n readonly tool: string;\r\n /** Which gate the tool routes to. Required since V1 gate #7 schema completion. */\r\n readonly gateType: string;\r\n readonly inputHash: string;\r\n readonly inputExcerpt: string;\r\n /** UTF-8 byte length of the original input. */\r\n readonly inputSizeBytes: number;\r\n readonly session: SessionContext;\r\n readonly now: Date;\r\n /** Override `event_id` (tests). Default: `randomUUID()`. */\r\n readonly eventId?: string;\r\n}\r\n\r\n/**\r\n * Build the V1 gate #7 base event. Late-update fields (per_model_verdicts,\r\n * synthesized_verdict, consensus_confidence, duration_ms, token counts,\r\n * cost, engine_version) are initialized to neutral defaults and overwritten\r\n * by tool-specific code paths (cache replay, engine success).\r\n */\r\nexport function buildBaseEvent(args: BuildEventArgs): ConsensusCallEvent {\r\n return {\r\n schema_version: 1,\r\n event_id: args.eventId ?? randomUUID(),\r\n ts: args.now.toISOString(),\r\n tenant_id: args.session.tenantId,\r\n operator_id: args.session.operatorId,\r\n session_id: args.session.sessionId,\r\n client_id: args.session.clientId,\r\n mode: args.session.mode,\r\n tool: args.tool,\r\n gate_type: args.gateType,\r\n input_hash: args.inputHash,\r\n input_excerpt: sanitizeExcerpt(args.inputExcerpt),\r\n input_size_bytes: args.inputSizeBytes,\r\n per_model_verdicts: [],\r\n synthesized_verdict: null,\r\n consensus_confidence: null,\r\n per_model_tokens_in: null,\r\n per_model_tokens_out: null,\r\n total_cost_usd: null,\r\n duration_ms: null,\r\n dev_override: null,\r\n downstream_outcome: null,\r\n vo_mcp_version: VO_MCP_VERSION,\r\n consensus_engine_version: null,\r\n cache_hit: false,\r\n };\r\n}\r\n\r\n/**\r\n * MCP content[] response wrapper. Tool handlers always return a single\r\n * text block whose `text` is JSON-encoded `ToolResultEnvelope`.\r\n */\r\nexport function jsonContent(value: unknown): { content: Array<{ type: 'text'; text: string }> } {\r\n return {\r\n content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],\r\n };\r\n}\r\n\r\n/**\r\n * Project engine `per_model_verdicts` onto the open-shell `PerModelVerdict`\r\n * shape used inside `ConsensusCallEvent`. Re-sanitizes excerpts defensively\r\n * (the engine already sanitizes, but the open shell owns the contract).\r\n *\r\n * Shared by Phase 2 Lane D-1 tools (`vo_check_hollow_test`, `vo_verify_answer`,\r\n * `vo_architecture_review`) and the existing `vo_consensus_judgment` tool;\r\n * extracting it removes the original copy-paste in `consensus-judgment.ts`.\r\n */\r\nexport function toEventPerModelVerdicts(\r\n src: ConsensusSuccess['per_model_verdicts'],\r\n): readonly PerModelVerdict[] {\r\n return src.map((v: ConsensusPerModelVerdict): PerModelVerdict => {\r\n const sanitizedExcerpt = sanitizeExcerpt(v.raw_response_excerpt);\r\n return {\r\n model: v.model,\r\n model_id: v.model,\r\n provider: v.provider,\r\n verdict: v.verdict,\r\n confidence: v.confidence,\r\n duration_ms: v.duration_ms,\r\n raw_response_excerpt: sanitizedExcerpt,\r\n // Hash over the sanitized excerpt \u2014 engine doesn't yet thread the full\r\n // raw response. Documented limitation on `raw_response_hash` in types.ts.\r\n raw_response_hash: sha256Hex(v.raw_response_excerpt),\r\n reasoning_summary:\r\n typeof v.reasoning_excerpt === 'string' && v.reasoning_excerpt.length > 0\r\n ? sanitizeExcerpt(v.reasoning_excerpt, 500)\r\n : null,\r\n // Engine doesn't yet emit cited_sources; ship empty array so cloud\r\n // ingestion doesn't NPE on `Array.isArray()` checks.\r\n cited_sources: [],\r\n error: v.error ?? null,\r\n };\r\n });\r\n}\r\n\r\n/** Companion to `toEventPerModelVerdicts` \u2014 projects the synthesized verdict. */\r\nexport function toEventSynthesizedVerdict(src: ConsensusSynthesizedVerdict): SynthesizedVerdict {\r\n return {\r\n verdict: src.verdict,\r\n confidence: src.confidence,\r\n reasoning_excerpt: sanitizeExcerpt(src.reasoning_excerpt),\r\n };\r\n}\r\n", "/**\r\n * Consensus-call events writer (V1 launch gate #7).\r\n *\r\n * Appends one JSONL line per tool invocation to either\r\n * `$VO_MCP_EVENTS_PATH` or `~/.claude/vo-mcp-events.jsonl`.\r\n *\r\n * Design notes:\r\n * - Append-only. Never rewrite the file. Cloud ingestion can tail it.\r\n * - Sync `appendFileSync` \u2014 small writes, low latency, avoids backpressure\r\n * on the MCP request loop. If this becomes a bottleneck, swap for a\r\n * buffered writer; the EventsWriter interface intentionally leaves\r\n * room for that.\r\n * - PII: callers must sanitize `input_excerpt` before passing it in.\r\n * The writer does NOT inspect content (we don't know the tool's\r\n * schema). See `sanitizeExcerpt` helper below for the canonical pass.\r\n * - Rotation (audit HIGH 2026-05-24 fix): when the file crosses\r\n * `maxBytes` (default 50 MB), gzip-rotate to `<path>.<ISO-ts>.gz`,\r\n * reset the counter, and prune to last `keepRotated` (default 10)\r\n * rotated files. On disk-full (`ENOSPC`): log to stderr + DROP the\r\n * event (best-effort audit log; never throws into the tool handler).\r\n */\r\nimport {\r\n appendFileSync,\r\n chmodSync,\r\n mkdirSync,\r\n readdirSync,\r\n readFileSync,\r\n statSync,\r\n unlinkSync,\r\n writeFileSync,\r\n} from 'node:fs';\r\nimport { homedir } from 'node:os';\r\nimport { basename, dirname, join } from 'node:path';\r\nimport { gzipSync } from 'node:zlib';\r\nimport type { ConsensusCallEvent } from '../types.js';\r\n\r\nexport interface EventsWriter {\r\n append(event: ConsensusCallEvent): void;\r\n /** Absolute path of the events file (or 'memory' for the in-memory variant). */\r\n path(): string;\r\n close(): void;\r\n}\r\n\r\nexport interface FileEventsWriterOptions {\r\n /** Override the events file location. Defaults to `$VO_MCP_EVENTS_PATH` or `~/.claude/vo-mcp-events.jsonl`. */\r\n readonly path?: string;\r\n /**\r\n * Rotate the file when it would exceed this many bytes after the next\r\n * write. Default: 50 MiB (52428800). Env override:\r\n * `VO_MCP_EVENTS_MAX_BYTES` (must be a positive integer).\r\n */\r\n readonly maxBytes?: number;\r\n /**\r\n * Number of rotated `.gz` files to retain after pruning. Default 10.\r\n * Older rotated files are unlinked. Env override:\r\n * `VO_MCP_EVENTS_KEEP_ROTATED` (must be a positive integer).\r\n */\r\n readonly keepRotated?: number;\r\n}\r\n\r\n/** Default rotation threshold: 50 MiB. Per 2026-05-24 audit HIGH. */\r\nexport const DEFAULT_EVENTS_MAX_BYTES = 50 * 1024 * 1024;\r\n/** Default rotated-file retention. */\r\nexport const DEFAULT_EVENTS_KEEP_ROTATED = 10;\r\n\r\nexport function defaultEventsPath(): string {\r\n const envPath = process.env['VO_MCP_EVENTS_PATH'];\r\n if (envPath && envPath.length > 0) return envPath;\r\n return join(homedir(), '.claude', 'vo-mcp-events.jsonl');\r\n}\r\n\r\n/** Parse a positive integer env var; null on missing/invalid. */\r\nfunction envPositiveInt(name: string): number | null {\r\n const raw = process.env[name];\r\n if (raw === undefined || raw.length === 0) return null;\r\n const n = Number(raw);\r\n return Number.isFinite(n) && n > 0 && Number.isInteger(n) ? n : null;\r\n}\r\n\r\n/**\r\n * Rotate the events file: gzip-rename the current contents to\r\n * `<path>.<ISO-ts>.gz`, leaving the original file removed (the next\r\n * append re-creates it). Best-effort: rotation failures are stderr-logged\r\n * and swallowed \u2014 the calling append still completes (the line is just\r\n * written to the non-rotated file).\r\n *\r\n * Returns true on successful rotation, false otherwise.\r\n */\r\nfunction rotateNow(filePath: string): boolean {\r\n try {\r\n if (!statSync(filePath).isFile()) return false;\r\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\r\n const rotatedPath = `${filePath}.${ts}.gz`;\r\n const contents = readFileSync(filePath);\r\n writeFileSync(rotatedPath, gzipSync(contents));\r\n try {\r\n chmodSync(rotatedPath, 0o600);\r\n } catch {\r\n /* Windows / unsupported FS */\r\n }\r\n // Reset the live file by removing it; next appendFileSync re-creates.\r\n unlinkSync(filePath);\r\n return true;\r\n } catch (err) {\r\n const msg = err instanceof Error ? err.message : String(err);\r\n process.stderr.write(`[vo-mcp] events log rotation failed: ${msg}\\n`);\r\n return false;\r\n }\r\n}\r\n\r\n/**\r\n * Prune `<filePath>.*.gz` rotated files down to the newest `keep` entries.\r\n * Best-effort: errors are silently dropped (the worst that happens is\r\n * extra disk usage, not a tool-call failure).\r\n */\r\nfunction pruneRotated(filePath: string, keep: number): void {\r\n try {\r\n const dir = dirname(filePath);\r\n const baseName = basename(filePath);\r\n const prefix = `${baseName}.`;\r\n const rotated: Array<{ full: string; mtimeMs: number }> = [];\r\n for (const entry of readdirSync(dir)) {\r\n if (!entry.startsWith(prefix) || !entry.endsWith('.gz')) continue;\r\n const full = join(dir, entry);\r\n try {\r\n rotated.push({ full, mtimeMs: statSync(full).mtimeMs });\r\n } catch {\r\n /* ignore \u2014 race condition; file vanished */\r\n }\r\n }\r\n rotated.sort((a, b) => b.mtimeMs - a.mtimeMs); // newest first\r\n for (const r of rotated.slice(keep)) {\r\n try {\r\n unlinkSync(r.full);\r\n } catch {\r\n /* ignore */\r\n }\r\n }\r\n } catch {\r\n /* ignore \u2014 best-effort pruning */\r\n }\r\n}\r\n\r\nexport function createFileEventsWriter(opts: FileEventsWriterOptions = {}): EventsWriter {\r\n const filePath = opts.path ?? defaultEventsPath();\r\n const maxBytes =\r\n opts.maxBytes ?? envPositiveInt('VO_MCP_EVENTS_MAX_BYTES') ?? DEFAULT_EVENTS_MAX_BYTES;\r\n const keepRotated =\r\n opts.keepRotated ?? envPositiveInt('VO_MCP_EVENTS_KEEP_ROTATED') ?? DEFAULT_EVENTS_KEEP_ROTATED;\r\n // 0o700 dir: only the operator account can list / open it.\r\n mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });\r\n let permsApplied = false;\r\n // Track size in-process to avoid statSync per append. Initialize from disk\r\n // (handles MCP server restarts where the existing events file has content).\r\n let currentBytes = 0;\r\n try {\r\n currentBytes = statSync(filePath).size;\r\n } catch {\r\n /* file doesn't exist yet \u2014 currentBytes stays 0 */\r\n }\r\n return {\r\n append(event: ConsensusCallEvent): void {\r\n const line = JSON.stringify(event) + '\\n';\r\n const lineBytes = Buffer.byteLength(line, 'utf8');\r\n // Rotate BEFORE writing if the new line would push us over the threshold.\r\n // Edge case: a single line larger than maxBytes still goes into a fresh\r\n // file (rotation only protects against accumulation; one event always\r\n // fits even if it temporarily exceeds the cap).\r\n if (currentBytes > 0 && currentBytes + lineBytes > maxBytes) {\r\n if (rotateNow(filePath)) {\r\n pruneRotated(filePath, keepRotated);\r\n currentBytes = 0;\r\n permsApplied = false; // chmod on the next-created file\r\n }\r\n // If rotation failed, we fall through and write to the existing file\r\n // anyway \u2014 the audit log is best-effort, growing past maxBytes is\r\n // better than dropping events for spurious rotation failures.\r\n }\r\n try {\r\n appendFileSync(filePath, line, 'utf8');\r\n currentBytes += lineBytes;\r\n } catch (err) {\r\n const code = (err as NodeJS.ErrnoException).code;\r\n if (code === 'ENOSPC') {\r\n // Disk full \u2014 drop the event rather than throw. Tool calls MUST\r\n // succeed even when the audit log can't be written; audit-log\r\n // gaps are visible (line counts diverge) but tool failures are\r\n // not (every consensus call would error out).\r\n process.stderr.write(\r\n `[vo-mcp] events log write failed (disk full): event dropped\\n`,\r\n );\r\n return;\r\n }\r\n // Other errors (permission denied, FS read-only, etc.) \u2014 re-throw so\r\n // the operator notices. These are unrecoverable misconfigurations.\r\n throw err;\r\n }\r\n // 0o600 \u2014 events JSONL contains input_excerpt fields with code text\r\n // (already sanitized via sanitizeExcerpt) but still operator-sensitive.\r\n // Best-effort once-per-process; chmod no-ops on Windows.\r\n if (!permsApplied) {\r\n try {\r\n chmodSync(filePath, 0o600);\r\n } catch {\r\n /* ignore \u2014 Windows / unsupported FS */\r\n }\r\n permsApplied = true;\r\n }\r\n },\r\n path(): string {\r\n return filePath;\r\n },\r\n close(): void {\r\n // appendFileSync opens-and-closes per write; nothing to release.\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * In-memory writer used by unit tests. Captures appended events in order.\r\n */\r\nexport interface MemoryEventsWriter extends EventsWriter {\r\n readonly events: ConsensusCallEvent[];\r\n}\r\n\r\nexport function createMemoryEventsWriter(): MemoryEventsWriter {\r\n const events: ConsensusCallEvent[] = [];\r\n return {\r\n events,\r\n append(event: ConsensusCallEvent): void {\r\n events.push(event);\r\n },\r\n path(): string {\r\n return 'memory';\r\n },\r\n close(): void {\r\n /* no-op */\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * `sanitizeExcerpt` PII / secret scrubber \u2014 moved to `@algosuite/vo-arch-defaults`\r\n * in PR-U 2026-05-25 so every KB consumer (vo-mcp, the standalone\r\n * `vo-arch-check` CLI, the future cloud control plane) can sanitize evidence\r\n * excerpts without depending on vo-mcp.\r\n *\r\n * Re-exported here for back-compat with existing import sites\r\n * (`import { sanitizeExcerpt } from '../logging/events-writer.js'`). New\r\n * code should import from `@algosuite/vo-arch-defaults` directly.\r\n */\r\nexport { sanitizeExcerpt, SANITIZE_DEFAULT_MAX_LEN } from '@algosuite/vo-arch-defaults';\r\n", "/**\r\n * Zod schema for v1 architectural-default rules.\r\n *\r\n * Schema v1 is LOCKED per handoff \u00A75 \u2014 DO NOT change field names or shape.\r\n * Add a sibling `rule-v2.ts` and bump `schema_version` if the contract needs\r\n * to evolve; the version registry in `./index.ts` dispatches by version.\r\n *\r\n * Why we still validate at load time even though TS gives us static types:\r\n * - Corpus is JSON on disk \u2192 no compile-time guarantee\r\n * - Tenants edit `vo-arch-defaults.local.json` by hand \u2192 drift is expected\r\n * - A malformed rule that loaded silently would fire (or not) in opaque\r\n * ways; failing loud at load is the only honest behavior\r\n */\r\nimport { z } from 'zod';\r\n\r\nconst EvidenceMatcherKind = z.enum([\r\n 'regex',\r\n 'import-detector',\r\n 'package-json-field',\r\n 'file-size',\r\n 'ast-pattern',\r\n 'custom',\r\n]);\r\n\r\nconst EvidenceMatcher = z\r\n .object({\r\n kind: EvidenceMatcherKind,\r\n pattern: z.string().optional(),\r\n config: z.record(z.string(), z.unknown()).optional(),\r\n description: z.string().min(1),\r\n })\r\n .strict()\r\n .refine(\r\n (m) => m.kind !== 'regex' || (typeof m.pattern === 'string' && m.pattern.length > 0),\r\n { message: 'regex matcher requires non-empty pattern' },\r\n );\r\n\r\nconst ChangeType = z.enum(['new-file', 'edit', 'delete', 'rename', 'any']);\r\n\r\nconst AppliesWhen = z\r\n .object({\r\n stack: z.array(z.string().min(1)).optional(),\r\n change_types: z.array(ChangeType).optional(),\r\n file_globs: z.array(z.string().min(1)).optional(),\r\n not_file_globs: z.array(z.string().min(1)).optional(),\r\n })\r\n .strict();\r\n\r\nconst Reference = z\r\n .object({\r\n type: z.enum(['framework-doc', 'internal-doc', 'pr', 'incident', 'external']),\r\n url: z.string().min(1),\r\n description: z.string().min(1),\r\n })\r\n .strict();\r\n\r\n/** ISO date YYYY-MM-DD; rejects 2026-13-99 etc. via parseable check. */\r\nconst IsoDate = z\r\n .string()\r\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, 'last_verified must be ISO date YYYY-MM-DD')\r\n .refine((s) => {\r\n const d = new Date(s + 'T00:00:00Z');\r\n return !Number.isNaN(d.getTime()) && d.toISOString().startsWith(s);\r\n }, 'last_verified must be a real calendar date');\r\n\r\n/** Rule ID convention: `<category>/<kebab-name>`. */\r\nconst RuleId = z.string().regex(/^[a-z][a-z0-9-]*\\/[a-z][a-z0-9-]*$/, {\r\n message: 'rule_id must be `<category>/<kebab-name>`',\r\n});\r\n\r\nexport const ArchitecturalDefaultRuleSchema = z\r\n .object({\r\n schema_version: z.literal(1),\r\n rule_id: RuleId,\r\n rule_version: z.number().int().positive(),\r\n category: z.string().min(1),\r\n severity: z.enum(['blocker', 'warning', 'info']),\r\n title: z.string().min(1),\r\n rationale: z.string().min(1),\r\n applies_when: AppliesWhen,\r\n evidence_of_violation: z.array(EvidenceMatcher).min(1, {\r\n message: 'rule must declare at least one evidence matcher',\r\n }),\r\n remediation: z.string().min(1),\r\n references: z.array(Reference),\r\n last_verified: IsoDate,\r\n tags: z.array(z.string().min(1)),\r\n })\r\n .strict();\r\n\r\nexport type ArchitecturalDefaultRuleParsed = z.infer<\r\n typeof ArchitecturalDefaultRuleSchema\r\n>;\r\n\r\nexport function parseRule(input: unknown): ArchitecturalDefaultRuleParsed {\r\n return ArchitecturalDefaultRuleSchema.parse(input);\r\n}\r\n\r\nexport function safeParseRule(input: unknown): {\r\n ok: true;\r\n rule: ArchitecturalDefaultRuleParsed;\r\n} | {\r\n ok: false;\r\n error: string;\r\n} {\r\n const r = ArchitecturalDefaultRuleSchema.safeParse(input);\r\n if (r.success) return { ok: true, rule: r.data };\r\n return { ok: false, error: r.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ') };\r\n}\r\n", "/**\r\n * Zod schema for v1 per-tenant overrides.\r\n *\r\n * Per handoff \u00A75: tenant override has THREE knobs:\r\n * - `suppressed_rule_ids`: disable a bundled rule wholesale\r\n * - `modified_rules`: partial fields override (e.g. lower severity)\r\n * - `added_rules`: tenant-specific rules layered on top\r\n *\r\n * `added_rules` are full v1 rules \u2192 reuse the rule schema.\r\n * `modified_rules` are PARTIAL \u2192 use `.deepPartial()` to allow any subset.\r\n *\r\n * Schema v1 is LOCKED per handoff \u00A75.\r\n */\r\nimport { z } from 'zod';\r\nimport { ArchitecturalDefaultRuleSchema } from './rule-v1.js';\r\n\r\nconst PartialRuleSchema = ArchitecturalDefaultRuleSchema.partial();\r\n\r\nexport const TenantOverrideSchema = z\r\n .object({\r\n schema_version: z.literal(1),\r\n suppressed_rule_ids: z.array(z.string().min(1)),\r\n modified_rules: z.record(z.string().min(1), PartialRuleSchema),\r\n added_rules: z.array(ArchitecturalDefaultRuleSchema),\r\n /**\r\n * Per-tenant stack override (added 2026-05-24 \u2014 audit HIGH\r\n * \"DEFAULT_STACK hardcoded for Nexus repo\"). When set, downstream\r\n * consumers (vo-mcp KB pre-filter, vo-arch-check CLI) use this list\r\n * instead of their built-in default. Lets external tenants whose\r\n * repo isn't `firebase+react+pnpm` get useful KB rule matches by\r\n * dropping a `~/.claude/vo-arch-defaults.local.json` with their own\r\n * stack identifiers \u2014 no code change required.\r\n *\r\n * Open string union \u2014 values are not constrained beyond non-empty\r\n * strings so out-of-tree tenants can declare their own\r\n * (e.g. `vercel-edge`, `next-15`, `drizzle-postgres`).\r\n *\r\n * Optional. Undefined / omitted preserves pre-2026-05-24 behavior\r\n * (consumer falls back to its hardcoded default stack).\r\n */\r\n tenant_stack: z.array(z.string().min(1)).optional(),\r\n })\r\n .strict();\r\n\r\nexport type TenantOverrideParsed = z.infer<typeof TenantOverrideSchema>;\r\n\r\nexport function parseOverride(input: unknown): TenantOverrideParsed {\r\n return TenantOverrideSchema.parse(input);\r\n}\r\n\r\nexport function safeParseOverride(input: unknown): {\r\n ok: true;\r\n override: TenantOverrideParsed;\r\n} | {\r\n ok: false;\r\n error: string;\r\n} {\r\n const r = TenantOverrideSchema.safeParse(input);\r\n if (r.success) return { ok: true, override: r.data };\r\n return { ok: false, error: r.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ') };\r\n}\r\n", "/**\r\n * Bundled-corpus loader.\r\n *\r\n * Walks the package's `corpus/` directory (at runtime: `dist/corpus/` \u2014 the\r\n * build step in `scripts/copy-corpus.mjs` mirrors the source tree). Every\r\n * `.json` file is parsed + validated against the v1 schema. A malformed\r\n * rule is a HARD failure with the offending file path \u2014 we never silently\r\n * drop a rule (handoff \u00A7F honesty rule).\r\n *\r\n * The loader is synchronous because:\r\n * - corpus is small (~30 files at v1)\r\n * - called once at MCP server startup / CLI run\r\n * - async would force every consumer to await on a path lookup\r\n */\r\nimport { readdirSync, readFileSync, statSync, existsSync } from 'node:fs';\r\nimport { dirname, join } from 'node:path';\r\nimport { fileURLToPath } from 'node:url';\r\nimport { parseRule, type ArchitecturalDefaultRuleParsed } from '../schema/index.js';\r\n\r\n/**\r\n * Resolve the bundled corpus directory.\r\n *\r\n * Production path: `dist/corpus/` (sibling of the compiled JS). Tests can\r\n * pass an explicit `corpusDir` override.\r\n */\r\nexport function resolveBundledCorpusDir(): string {\r\n // import.meta.url points at `dist/storage/load-bundled.js` after build,\r\n // or `src/storage/load-bundled.ts` under ts-node / vitest. Walk one level\r\n // up to find `corpus/`. Try both layouts.\r\n const here = dirname(fileURLToPath(import.meta.url));\r\n const candidates = [\r\n join(here, '..', 'corpus'), // dist/corpus next to dist/storage\r\n join(here, '..', '..', 'corpus'), // src/corpus next to src/storage (under vitest)\r\n ];\r\n for (const c of candidates) {\r\n if (existsSync(c) && statSync(c).isDirectory()) return c;\r\n }\r\n throw new Error(\r\n `vo-arch-defaults: could not locate bundled corpus directory. Tried: ${candidates.join(', ')}`,\r\n );\r\n}\r\n\r\n/** Recursive walk of all `*.json` files under `dir`. */\r\nfunction walkJson(dir: string, out: string[]): void {\r\n for (const entry of readdirSync(dir)) {\r\n const full = join(dir, entry);\r\n const st = statSync(full);\r\n if (st.isDirectory()) {\r\n walkJson(full, out);\r\n } else if (entry.endsWith('.json')) {\r\n out.push(full);\r\n }\r\n }\r\n}\r\n\r\nexport interface LoadBundledOptions {\r\n /** Override the corpus directory (tests pass fixture paths). */\r\n readonly corpusDir?: string;\r\n}\r\n\r\nexport interface LoadBundledResult {\r\n readonly rules: ReadonlyArray<ArchitecturalDefaultRuleParsed>;\r\n readonly source_paths: ReadonlyArray<string>;\r\n}\r\n\r\n/**\r\n * Load + validate every rule under the corpus directory.\r\n *\r\n * Throws on the FIRST malformed rule with a path-prefixed error message\r\n * so the operator can fix it quickly. There is no \"best effort\" mode \u2014\r\n * a silently dropped rule is a rule that doesn't fire, which is exactly\r\n * the failure mode this whole gate exists to prevent.\r\n */\r\nexport function loadBundledCorpus(opts: LoadBundledOptions = {}): LoadBundledResult {\r\n const dir = opts.corpusDir ?? resolveBundledCorpusDir();\r\n const files: string[] = [];\r\n walkJson(dir, files);\r\n files.sort(); // deterministic order \u2014 helps tests and CLI output\r\n\r\n const rules: ArchitecturalDefaultRuleParsed[] = [];\r\n const seenIds = new Set<string>();\r\n for (const file of files) {\r\n const raw = readFileSync(file, 'utf8');\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(raw);\r\n } catch (err) {\r\n const m = err instanceof Error ? err.message : String(err);\r\n throw new Error(`vo-arch-defaults: invalid JSON in ${file}: ${m}`, { cause: err });\r\n }\r\n try {\r\n const rule = parseRule(parsed);\r\n if (seenIds.has(rule.rule_id)) {\r\n throw new Error(\r\n `vo-arch-defaults: duplicate rule_id '${rule.rule_id}' (second occurrence in ${file})`,\r\n );\r\n }\r\n seenIds.add(rule.rule_id);\r\n rules.push(rule);\r\n } catch (err) {\r\n const m = err instanceof Error ? err.message : String(err);\r\n throw new Error(`vo-arch-defaults: schema validation failed for ${file}: ${m}`, { cause: err });\r\n }\r\n }\r\n return { rules, source_paths: files };\r\n}\r\n", "/**\r\n * Per-tenant override loader.\r\n *\r\n * Reads a single JSON file (default: `~/.claude/vo-arch-defaults.local.json`,\r\n * or the path passed in). Validates against v1 override schema.\r\n *\r\n * Returns `null` when the path doesn't exist \u2014 overrides are OPT-IN.\r\n * Hard-throws on present-but-malformed (honesty rule: a malformed override\r\n * silently dropped would re-enable rules the operator thought were\r\n * suppressed).\r\n */\r\nimport { existsSync, readFileSync } from 'node:fs';\r\nimport { homedir } from 'node:os';\r\nimport { join } from 'node:path';\r\nimport { parseOverride, type TenantOverrideParsed } from '../schema/index.js';\r\n\r\n/** Default override location. Operator-facing: `~/.claude/vo-arch-defaults.local.json`. */\r\nexport function defaultOverridePath(): string {\r\n return join(homedir(), '.claude', 'vo-arch-defaults.local.json');\r\n}\r\n\r\nexport interface LoadOverrideOptions {\r\n readonly path?: string;\r\n}\r\n\r\nexport interface LoadOverrideResult {\r\n readonly override: TenantOverrideParsed | null;\r\n readonly source_path: string | null;\r\n}\r\n\r\nexport function loadTenantOverride(\r\n opts: LoadOverrideOptions = {},\r\n): LoadOverrideResult {\r\n const path = opts.path ?? defaultOverridePath();\r\n if (!existsSync(path)) {\r\n return { override: null, source_path: null };\r\n }\r\n const raw = readFileSync(path, 'utf8');\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(raw);\r\n } catch (err) {\r\n const m = err instanceof Error ? err.message : String(err);\r\n throw new Error(`vo-arch-defaults: invalid JSON in override ${path}: ${m}`, { cause: err });\r\n }\r\n try {\r\n const override = parseOverride(parsed);\r\n return { override, source_path: path };\r\n } catch (err) {\r\n const m = err instanceof Error ? err.message : String(err);\r\n throw new Error(`vo-arch-defaults: override schema validation failed for ${path}: ${m}`, { cause: err });\r\n }\r\n}\r\n", "/**\r\n * Deep-merge bundled corpus + tenant override.\r\n *\r\n * Order of operations (handoff \u00A75):\r\n * 1. Start with bundled rules.\r\n * 2. Drop any rule whose ID is in `suppressed_rule_ids`.\r\n * 3. For each `modified_rules[id]` partial, shallow-merge fields on top\r\n * of the bundled rule (excluding `rule_id` / `schema_version` \u2014 those\r\n * are identity, not configuration).\r\n * 4. Append `added_rules`. If an added rule's ID collides with a\r\n * bundled rule that wasn't already modified/suppressed, the\r\n * added_rule wins (override semantics).\r\n *\r\n * The merge is intentionally shallow on each rule object \u2014 nested fields\r\n * (`applies_when`, `evidence_of_violation`, etc.) are REPLACED wholesale\r\n * if the override sets them. Tenants who want to \"add another stack\" to\r\n * `applies_when.stack` must repeat the whole array. This is simpler and\r\n * less surprising than a recursive deep-merge of arrays.\r\n */\r\nimport type {\r\n ArchitecturalDefaultRule,\r\n TenantOverride,\r\n} from '../types.js';\r\n\r\nexport interface MergeResult {\r\n readonly rules: ReadonlyArray<ArchitecturalDefaultRule>;\r\n readonly suppressed_count: number;\r\n readonly modified_count: number;\r\n readonly added_count: number;\r\n}\r\n\r\nconst IMMUTABLE_FIELDS: ReadonlyArray<keyof ArchitecturalDefaultRule> = [\r\n 'schema_version',\r\n 'rule_id',\r\n];\r\n\r\nfunction applyModification(\r\n base: ArchitecturalDefaultRule,\r\n patch: Partial<ArchitecturalDefaultRule>,\r\n): ArchitecturalDefaultRule {\r\n // Strip immutable identity fields from the patch.\r\n const cleanPatch: Partial<ArchitecturalDefaultRule> = { ...patch };\r\n for (const k of IMMUTABLE_FIELDS) {\r\n delete (cleanPatch as Record<string, unknown>)[k];\r\n }\r\n return { ...base, ...cleanPatch };\r\n}\r\n\r\nexport function mergeCorpusWithOverride(\r\n bundled: ReadonlyArray<ArchitecturalDefaultRule>,\r\n override: TenantOverride | null,\r\n): MergeResult {\r\n if (override === null) {\r\n return {\r\n rules: bundled,\r\n suppressed_count: 0,\r\n modified_count: 0,\r\n added_count: 0,\r\n };\r\n }\r\n\r\n const suppressed = new Set(override.suppressed_rule_ids);\r\n let suppressedCount = 0;\r\n let modifiedCount = 0;\r\n\r\n const afterFilter: ArchitecturalDefaultRule[] = [];\r\n for (const rule of bundled) {\r\n if (suppressed.has(rule.rule_id)) {\r\n suppressedCount += 1;\r\n continue;\r\n }\r\n const patch = override.modified_rules[rule.rule_id];\r\n if (patch !== undefined) {\r\n afterFilter.push(applyModification(rule, patch));\r\n modifiedCount += 1;\r\n } else {\r\n afterFilter.push(rule);\r\n }\r\n }\r\n\r\n // Append added_rules. If an ID already exists (only possible when the\r\n // tenant added a rule that ISN'T in the bundled corpus and ISN'T\r\n // suppressed \u2014 i.e. brand-new), it's appended. If the ID DOES collide\r\n // with a still-present bundled rule, the added_rule replaces the\r\n // bundled one (consistent with override-semantics).\r\n const byId = new Map<string, ArchitecturalDefaultRule>(\r\n afterFilter.map((r) => [r.rule_id, r]),\r\n );\r\n let addedCount = 0;\r\n for (const rule of override.added_rules) {\r\n if (suppressed.has(rule.rule_id)) {\r\n // Operator suppressed it AND added it \u2014 suppression wins. Honest\r\n // behavior: prefer the more restrictive setting.\r\n continue;\r\n }\r\n byId.set(rule.rule_id, rule);\r\n addedCount += 1;\r\n }\r\n\r\n return {\r\n rules: Array.from(byId.values()),\r\n suppressed_count: suppressedCount,\r\n modified_count: modifiedCount,\r\n added_count: addedCount,\r\n };\r\n}\r\n", "/**\r\n * Stack-applicability filter.\r\n *\r\n * Rule applies when:\r\n * - rule.applies_when.stack is undefined or empty \u2192 stack-agnostic, always TRUE\r\n * - otherwise \u2192 at least one stack in the caller's `stacks` is in the rule's set\r\n */\r\nimport type { ArchitecturalDefaultRule, Stack } from '../types.js';\r\n\r\nexport function appliesToStack(\r\n rule: ArchitecturalDefaultRule,\r\n stacks: ReadonlyArray<Stack>,\r\n): boolean {\r\n const required = rule.applies_when.stack;\r\n if (!required || required.length === 0) return true;\r\n if (stacks.length === 0) return false;\r\n const want = new Set(required);\r\n for (const s of stacks) {\r\n if (want.has(s)) return true;\r\n }\r\n return false;\r\n}\r\n", "/**\r\n * Change-type-applicability filter.\r\n *\r\n * Rule applies when:\r\n * - rule.applies_when.change_types is undefined or empty \u2192 applies to all\r\n * - rule.applies_when.change_types contains 'any' \u2192 applies to all\r\n * - rule.applies_when.change_types contains the caller's `change_type` \u2192 TRUE\r\n * - otherwise FALSE\r\n *\r\n * Caller passes a single change_type (the kind of edit being reviewed).\r\n * 'any' on the rule side means \"fires regardless of change shape\" \u2014 for\r\n * caller-side 'any', we expand to ALL kinds.\r\n */\r\nimport type { ArchitecturalDefaultRule, ChangeType } from '../types.js';\r\n\r\nexport function appliesToChangeType(\r\n rule: ArchitecturalDefaultRule,\r\n changeType: ChangeType,\r\n): boolean {\r\n const required = rule.applies_when.change_types;\r\n if (!required || required.length === 0) return true;\r\n if (required.includes('any')) return true;\r\n if (changeType === 'any') return true;\r\n return required.includes(changeType);\r\n}\r\n", "/**\r\n * Minimal glob \u2192 RegExp.\r\n *\r\n * We intentionally do NOT depend on `picomatch` / `minimatch` here:\r\n * - the rule corpus uses a small subset (`**` / `*` / brace alternations)\r\n * - depending on a glob library widens the supply-chain attack surface\r\n * for a package whose whole job is \"say no\"\r\n * - the open shell is meant to be re-implementable by other clients;\r\n * a hand-rolled converter is portable\r\n *\r\n * Supported syntax:\r\n * - `**` matches any number of path segments (including zero)\r\n * - `*` matches any chars except `/`\r\n * - `?` matches any single char except `/`\r\n * - `{a,b}` matches any literal option (added PR-R 2026-05-25). Options\r\n * are LITERAL strings \u2014 no globs / nested braces inside.\r\n * Example: `**\\/*.{ts,tsx}` matches `src/foo.ts` and `src/foo.tsx`.\r\n * Empty `{}` is treated as a literal nothing.\r\n * Unmatched `{` is treated as a literal `{`.\r\n * - everything else is a literal (regex metacharacters escaped)\r\n *\r\n * NOT supported (deliberately): `[abc]` char classes, `!negation`,\r\n * nested braces `{a,{b,c}}`, glob expressions inside braces `{*.ts,foo}`.\r\n * Keep the surface small. If a rule needs more, declare multiple file_globs.\r\n */\r\n\r\nconst REGEX_META = /[.+^${}()|[\\]\\\\]/g;\r\n\r\nexport function globToRegExp(glob: string): RegExp {\r\n let out = '';\r\n let i = 0;\r\n while (i < glob.length) {\r\n const c = glob[i] ?? '';\r\n if (c === '*') {\r\n const next = glob[i + 1];\r\n if (next === '*') {\r\n // ** \u2014 match across segments\r\n // Consume an optional trailing `/`\r\n const after = glob[i + 2];\r\n if (after === '/') {\r\n out += '(?:.*/)?';\r\n i += 3;\r\n } else {\r\n out += '.*';\r\n i += 2;\r\n }\r\n } else {\r\n out += '[^/]*';\r\n i += 1;\r\n }\r\n } else if (c === '?') {\r\n out += '[^/]';\r\n i += 1;\r\n } else if (c === '{') {\r\n // Brace expansion (PR-R 2026-05-25). Find matching `}`; split inner on\r\n // unescaped commas; each option is a LITERAL string (regex-escaped).\r\n // No nested-brace / no glob-in-options support \u2014 keeps the implementation\r\n // tiny and the contract obvious. If `{` is unmatched, fall through as\r\n // literal.\r\n const closeIdx = glob.indexOf('}', i + 1);\r\n if (closeIdx < 0) {\r\n // Unmatched `{` \u2014 treat as literal so the caller sees a regex that\r\n // matches the original glob string itself (defensive: don't silently\r\n // accept malformed input).\r\n out += '\\\\{';\r\n i += 1;\r\n } else {\r\n const inner = glob.slice(i + 1, closeIdx);\r\n // Empty `{}` \u2014 emit nothing, consume to past `}`. Matches the glob\r\n // with the braces dropped entirely.\r\n if (inner.length === 0) {\r\n i = closeIdx + 1;\r\n } else {\r\n const opts = inner.split(',');\r\n const escapedOpts = opts.map((o) => o.replace(REGEX_META, '\\\\$&'));\r\n out += '(?:' + escapedOpts.join('|') + ')';\r\n i = closeIdx + 1;\r\n }\r\n }\r\n } else {\r\n out += c.replace(REGEX_META, '\\\\$&');\r\n i += 1;\r\n }\r\n }\r\n return new RegExp('^' + out + '$');\r\n}\r\n\r\nexport function matchesAnyGlob(\r\n path: string,\r\n globs: ReadonlyArray<string>,\r\n): boolean {\r\n for (const g of globs) {\r\n if (globToRegExp(g).test(path)) return true;\r\n }\r\n return false;\r\n}\r\n", "/**\r\n * File-path-applicability filter.\r\n *\r\n * Rule applies when:\r\n * - applies_when.file_globs is undefined/empty AND not_file_globs is\r\n * undefined/empty \u2192 file-path-agnostic, TRUE\r\n * - otherwise \u2192 AT LEAST ONE caller `file_paths` entry must satisfy BOTH\r\n * (a) include (matches a file_globs entry, OR rule declares no include)\r\n * (b) NOT excluded (doesn't match any not_file_globs entry)\r\n *\r\n * If the caller provides zero file_paths AND the rule declares file_globs,\r\n * the rule doesn't apply (no surface to fire on).\r\n *\r\n * ## Per-file semantics (bug fix 2026-05-25 \u2014 see PR-P)\r\n *\r\n * Pre-fix behavior was a wholesale-drop on any exclude match: if ANY file in\r\n * the diff matched `not_file_globs`, the rule was dropped from the entire\r\n * PR \u2014 even when other files in the diff matched `file_globs` and weren't\r\n * excluded. This failed open on the most common security-rule pattern:\r\n *\r\n * rule `security/no-hardcoded-secrets`:\r\n * file_globs: [\"**\\/*.ts\", \"**\\/*.tsx\", \"**\\/*.js\", \"**\\/*.mjs\", ...]\r\n * not_file_globs: [\"**\\/*.test.ts\", \"**\\/fixtures/**\", \"**\\/__mocks__/**\"]\r\n *\r\n * Failure case under the old semantics: a PR adds a hardcoded `sk_live_...`\r\n * key to `src/foo.ts` AND adds `src/foo.test.ts` to test the new code. The\r\n * test file matched the exclude, the entire rule was dropped, and the\r\n * blocker-severity secret-detection rule never fired on `foo.ts`. PR ships\r\n * with a leaked secret.\r\n *\r\n * The exclude list semantically means \"don't SCAN these files\" (per-file\r\n * scope), not \"skip the entire rule if any of these files are touched\"\r\n * (whole-rule scope). This implementation matches that intent.\r\n *\r\n * The rule-level applies_when filter answers \"does this rule have ANY\r\n * surface to fire on in this diff?\" \u2014 true if at least one included,\r\n * non-excluded file exists. The per-file evidence-matcher loop in\r\n * `run-query.ts` still re-runs `appliesToFiles(rule, [file.path])` per\r\n * file so evidence only fires on the right files. Single-path semantics\r\n * are unchanged: one file that's both included and not excluded \u2192 true;\r\n * one file that's excluded \u2192 false. Same as before.\r\n */\r\nimport type { ArchitecturalDefaultRule } from '../types.js';\r\nimport { matchesAnyGlob } from './glob.js';\r\n\r\nexport function appliesToFiles(\r\n rule: ArchitecturalDefaultRule,\r\n filePaths: ReadonlyArray<string>,\r\n): boolean {\r\n const include = rule.applies_when.file_globs;\r\n const exclude = rule.applies_when.not_file_globs;\r\n const noInclude = !include || include.length === 0;\r\n const noExclude = !exclude || exclude.length === 0;\r\n if (noInclude && noExclude) return true;\r\n if (filePaths.length === 0) {\r\n // No paths to evaluate against. If the rule narrows by include glob,\r\n // it can't apply. If only exclude is declared, default-true.\r\n return noInclude;\r\n }\r\n // Per-file: rule applies if AT LEAST ONE caller file is both included\r\n // (or no include declared) AND not excluded.\r\n for (const p of filePaths) {\r\n const isIncluded = noInclude || (include !== undefined && matchesAnyGlob(p, include));\r\n if (!isIncluded) continue;\r\n const isExcluded =\r\n !noExclude && exclude !== undefined && matchesAnyGlob(p, exclude);\r\n if (isExcluded) continue;\r\n return true;\r\n }\r\n return false;\r\n}\r\n", "/**\r\n * Minimal unified-diff parser.\r\n *\r\n * We need just enough structure to:\r\n * - know which files the diff touches (for `applies_when.file_globs`)\r\n * - know whether each file is new / deleted / modified / renamed\r\n * (for `applies_when.change_types`)\r\n * - feed regex matchers per-file's added lines (so we don't match an\r\n * UNCHANGED Gen1 import that's only in the file for context)\r\n *\r\n * Intentionally hand-rolled \u2014 see `query/glob.ts` for why we avoid pulling\r\n * in a `parse-diff` dep. Format reference: `git diff --no-color` unified.\r\n */\r\n\r\nexport type ParsedChangeType = 'new-file' | 'edit' | 'delete' | 'rename' | 'any';\r\n\r\nexport interface ParsedFile {\r\n readonly path: string;\r\n readonly previous_path: string | null;\r\n readonly change_type: ParsedChangeType;\r\n /**\r\n * Added lines (after-state). Each entry preserves the 1-based line\r\n * number on the new file. Removed lines / context lines are NOT included\r\n * \u2014 evidence matchers should fire only on what the diff is ADDING.\r\n */\r\n readonly added_lines: ReadonlyArray<{ line: number; text: string }>;\r\n}\r\n\r\nexport interface ParsedDiff {\r\n readonly files: ReadonlyArray<ParsedFile>;\r\n}\r\n\r\ninterface MutFile {\r\n path: string;\r\n previous_path: string | null;\r\n change_type: ParsedChangeType;\r\n added_lines: { line: number; text: string }[];\r\n}\r\n\r\n/** Strip the `a/`/`b/` git-diff prefix when present. */\r\nfunction stripPathPrefix(p: string): string {\r\n if (p.startsWith('a/') || p.startsWith('b/')) return p.slice(2);\r\n return p;\r\n}\r\n\r\nexport function parseUnifiedDiff(text: string): ParsedDiff {\r\n const lines = text.split(/\\r?\\n/);\r\n const files: MutFile[] = [];\r\n let current: MutFile | null = null;\r\n let newLine = 0;\r\n\r\n for (let i = 0; i < lines.length; i++) {\r\n const raw = lines[i] ?? '';\r\n if (raw.startsWith('diff --git ')) {\r\n // diff --git a/foo b/foo\r\n const m = /^diff --git (\\S+) (\\S+)$/.exec(raw);\r\n if (m && m[1] && m[2]) {\r\n current = {\r\n path: stripPathPrefix(m[2]),\r\n previous_path: stripPathPrefix(m[1]),\r\n change_type: 'edit', // refined as we see new file mode / deleted file mode / rename\r\n added_lines: [],\r\n };\r\n if (current.previous_path === current.path) current.previous_path = null;\r\n files.push(current);\r\n }\r\n newLine = 0;\r\n continue;\r\n }\r\n if (!current) continue;\r\n if (raw.startsWith('new file mode ')) {\r\n current.change_type = 'new-file';\r\n continue;\r\n }\r\n if (raw.startsWith('deleted file mode ')) {\r\n current.change_type = 'delete';\r\n continue;\r\n }\r\n if (raw.startsWith('rename from ')) {\r\n current.previous_path = raw.slice('rename from '.length).trim();\r\n current.change_type = 'rename';\r\n continue;\r\n }\r\n if (raw.startsWith('rename to ')) {\r\n current.path = raw.slice('rename to '.length).trim();\r\n current.change_type = 'rename';\r\n continue;\r\n }\r\n // hunk header: @@ -a,b +c,d @@\r\n const hunk = /^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/.exec(raw);\r\n if (hunk && hunk[1]) {\r\n newLine = parseInt(hunk[1], 10);\r\n continue;\r\n }\r\n if (raw.startsWith('+++') || raw.startsWith('---')) {\r\n // file header \u2014 already captured from `diff --git`\r\n continue;\r\n }\r\n if (raw.startsWith('+') && !raw.startsWith('+++')) {\r\n current.added_lines.push({ line: newLine, text: raw.slice(1) });\r\n newLine += 1;\r\n continue;\r\n }\r\n if (raw.startsWith('-') && !raw.startsWith('---')) {\r\n // removal \u2014 does not advance new-file line counter\r\n continue;\r\n }\r\n if (raw.startsWith('\\\\')) {\r\n // \"\\"\r\n continue;\r\n }\r\n // context line: advances new-file line counter\r\n if (newLine > 0) newLine += 1;\r\n }\r\n return { files };\r\n}\r\n", "/**\r\n * Regex matcher \u2014 runs `pattern` against every added line of every\r\n * applicable file. Per-file results carry the 1-based new-file line number.\r\n *\r\n * Per handoff \u00A7F: the matcher MUST be honest about what it can and can't\r\n * detect. We compile with `g` so all matches are surfaced; if a rule's\r\n * regex is too broad and false-positives, the rule's authors are expected\r\n * to narrow it.\r\n */\r\nimport type { ParsedFile } from '../diff-parser.js';\r\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\r\n\r\nconst MAX_EXCERPT = 200;\r\n\r\nfunction sanitizeLine(s: string): string {\r\n // Lightweight excerpt cleanup \u2014 strip leading whitespace, cap length.\r\n let out = s.replace(/^\\s+/, '');\r\n if (out.length > MAX_EXCERPT) out = out.slice(0, MAX_EXCERPT) + '\u2026';\r\n return out;\r\n}\r\n\r\nexport function runRegexMatcher(\r\n matcher: EvidenceMatcher,\r\n file: ParsedFile,\r\n): ReadonlyArray<EvidenceHit> {\r\n if (matcher.kind !== 'regex' || !matcher.pattern) return [];\r\n let re: RegExp;\r\n try {\r\n re = new RegExp(matcher.pattern);\r\n } catch {\r\n return [];\r\n }\r\n const hits: EvidenceHit[] = [];\r\n for (const { line, text } of file.added_lines) {\r\n if (re.test(text)) {\r\n hits.push({\r\n matcher_kind: 'regex',\r\n matcher_description: matcher.description,\r\n file: file.path,\r\n line,\r\n excerpt: sanitizeLine(text),\r\n });\r\n }\r\n }\r\n return hits;\r\n}\r\n", "/**\r\n * Import-detector matcher.\r\n *\r\n * `config.from` is the bare module specifier (or regex) the rule is\r\n * concerned with \u2014 e.g. `firebase-functions/v1`, `firebase-functions`,\r\n * or a regex like `^firebase-functions(/v1)?$`.\r\n *\r\n * `config.match` controls whether to fire on:\r\n * - 'exact' \u2014 only literal `from '<module>'`\r\n * - 'prefix' \u2014 fires on `from '<module>/...'` and the bare spec\r\n * - 'regex' \u2014 `config.from` is interpreted as a regex\r\n *\r\n * Fires on `import ... from '<module>'` and `import('<module>')` patterns\r\n * in added lines.\r\n */\r\nimport type { ParsedFile } from '../diff-parser.js';\r\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\r\n\r\n// CodeQL #198: original `[^'\"]*?from\\s+` is polynomial \u2014 the lazy `*?`\r\n// backtracks against the trailing `from\\s+` on inputs where no `from`\r\n// follows. The replacement bounds the wildcard with `{0,200}?` (real\r\n// import statements stay well under 200 chars between `import` and `from`)\r\n// AND excludes `\\n` so a `[^'\"]*?` cross-line match is impossible. This\r\n// keeps every existing test case green (verified by import-detector.test.ts)\r\n// while running in linear time on adversarial input.\r\nconst STATIC_IMPORT = /import\\s+(?:[^'\"\\n]{0,200}?from\\s+)?(['\"])([^'\"]+)\\1/;\r\nconst DYNAMIC_IMPORT = /import\\(\\s*(['\"])([^'\"]+)\\1\\s*\\)/;\r\nconst REQUIRE_CALL = /require\\(\\s*(['\"])([^'\"]+)\\1\\s*\\)/;\r\n\r\nconst MAX_EXCERPT = 200;\r\nfunction sanitize(s: string): string {\r\n let out = s.replace(/^\\s+/, '');\r\n if (out.length > MAX_EXCERPT) out = out.slice(0, MAX_EXCERPT) + '\u2026';\r\n return out;\r\n}\r\n\r\nfunction tryExtractSpec(line: string): string | null {\r\n const m = STATIC_IMPORT.exec(line) ?? DYNAMIC_IMPORT.exec(line) ?? REQUIRE_CALL.exec(line);\r\n if (!m || !m[2]) return null;\r\n return m[2];\r\n}\r\n\r\nfunction specMatches(spec: string, matcher: EvidenceMatcher): boolean {\r\n const cfg = (matcher.config ?? {}) as { from?: unknown; match?: unknown };\r\n const from = typeof cfg.from === 'string' ? cfg.from : null;\r\n const mode = typeof cfg.match === 'string' ? cfg.match : 'exact';\r\n if (from === null) return false;\r\n if (mode === 'regex') {\r\n try {\r\n return new RegExp(from).test(spec);\r\n } catch {\r\n return false;\r\n }\r\n }\r\n if (mode === 'prefix') {\r\n return spec === from || spec.startsWith(from + '/');\r\n }\r\n // exact\r\n return spec === from;\r\n}\r\n\r\nexport function runImportDetector(\r\n matcher: EvidenceMatcher,\r\n file: ParsedFile,\r\n): ReadonlyArray<EvidenceHit> {\r\n if (matcher.kind !== 'import-detector') return [];\r\n const hits: EvidenceHit[] = [];\r\n for (const { line, text } of file.added_lines) {\r\n const spec = tryExtractSpec(text);\r\n if (spec === null) continue;\r\n if (!specMatches(spec, matcher)) continue;\r\n hits.push({\r\n matcher_kind: 'import-detector',\r\n matcher_description: matcher.description,\r\n file: file.path,\r\n line,\r\n excerpt: sanitize(text),\r\n });\r\n }\r\n return hits;\r\n}\r\n", "/**\r\n * File-size matcher.\r\n *\r\n * `config.max_lines` \u2014 the cap; rule fires when the file (per the diff)\r\n * adds enough lines to PROBABLY exceed this. We can't observe the full\r\n * post-state from a diff alone (we don't have the unchanged context\r\n * line count), so this matcher fires when the diff ADDS more than\r\n * `config.max_added_lines` lines (default = `max_lines`) \u2014 i.e. \"this\r\n * single change is bigger than the whole file is supposed to be\".\r\n *\r\n * Honesty note (handoff \u00A7F): this matcher is heuristic. A truly\r\n * accurate check would require the full pre-state line count, which is\r\n * out of scope for a diff-only consumer. The rule rationale should\r\n * document this limitation; the CLI prints `(heuristic)` next to such\r\n * hits.\r\n */\r\nimport type { ParsedFile } from '../diff-parser.js';\r\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\r\n\r\nexport function runFileSizeMatcher(\r\n matcher: EvidenceMatcher,\r\n file: ParsedFile,\r\n): ReadonlyArray<EvidenceHit> {\r\n if (matcher.kind !== 'file-size') return [];\r\n const cfg = (matcher.config ?? {}) as {\r\n max_lines?: unknown;\r\n max_added_lines?: unknown;\r\n };\r\n const maxLines = typeof cfg.max_lines === 'number' ? cfg.max_lines : null;\r\n const explicitMaxAdded =\r\n typeof cfg.max_added_lines === 'number' ? cfg.max_added_lines : null;\r\n const cap = explicitMaxAdded ?? maxLines;\r\n if (cap === null || cap <= 0) return [];\r\n if (file.added_lines.length <= cap) return [];\r\n const sample = file.added_lines[0]?.text ?? '';\r\n return [\r\n {\r\n matcher_kind: 'file-size',\r\n matcher_description: `${matcher.description} (heuristic: ${file.added_lines.length} added lines > cap ${cap})`,\r\n file: file.path,\r\n excerpt: sample.slice(0, 100),\r\n },\r\n ];\r\n}\r\n", "/**\r\n * Package-json-field matcher.\r\n *\r\n * Fires when ANY added line in a `package.json` change contains a\r\n * forbidden value for a given field. Limited heuristic \u2014 we don't try\r\n * to parse incomplete JSON hunks; we look for the field-value pair\r\n * appearing as a literal JSON key/value on an added line.\r\n *\r\n * `config.field` \u2014 required, the JSON key name (e.g. 'packageManager').\r\n * `config.forbidden_prefix` \u2014 fires when value starts with this string.\r\n * `config.forbidden_exact` \u2014 fires when value equals this string.\r\n * `config.forbidden_regex` \u2014 fires when value matches this regex.\r\n *\r\n * Only one of the forbidden_* checks needs to be configured. We DO NOT\r\n * fire on the full file content because diff hunks are partial \u2014 false\r\n * positives on shared keys would be common. The honesty note in the\r\n * rule rationale should call out that this matcher requires the\r\n * change to actually touch the package.json field line.\r\n */\r\nimport type { ParsedFile } from '../diff-parser.js';\r\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\r\n\r\nfunction valueMatches(\r\n value: string,\r\n cfg: {\r\n forbidden_prefix?: unknown;\r\n forbidden_exact?: unknown;\r\n forbidden_regex?: unknown;\r\n },\r\n): boolean {\r\n if (typeof cfg.forbidden_prefix === 'string' && value.startsWith(cfg.forbidden_prefix)) {\r\n return true;\r\n }\r\n if (typeof cfg.forbidden_exact === 'string' && value === cfg.forbidden_exact) {\r\n return true;\r\n }\r\n if (typeof cfg.forbidden_regex === 'string') {\r\n try {\r\n if (new RegExp(cfg.forbidden_regex).test(value)) return true;\r\n } catch {\r\n /* invalid regex in config \u2014 silently skip */\r\n }\r\n }\r\n return false;\r\n}\r\n\r\nexport function runPackageJsonMatcher(\r\n matcher: EvidenceMatcher,\r\n file: ParsedFile,\r\n): ReadonlyArray<EvidenceHit> {\r\n if (matcher.kind !== 'package-json-field') return [];\r\n // Only fire on package.json files.\r\n if (!file.path.endsWith('package.json')) return [];\r\n const cfg = (matcher.config ?? {}) as {\r\n field?: unknown;\r\n forbidden_prefix?: unknown;\r\n forbidden_exact?: unknown;\r\n forbidden_regex?: unknown;\r\n };\r\n const field = typeof cfg.field === 'string' ? cfg.field : null;\r\n if (field === null) return [];\r\n const fieldRe = new RegExp(`\"${field}\"\\\\s*:\\\\s*\"([^\"]+)\"`);\r\n const hits: EvidenceHit[] = [];\r\n for (const { line, text } of file.added_lines) {\r\n const m = fieldRe.exec(text);\r\n if (!m || !m[1]) continue;\r\n if (valueMatches(m[1], cfg)) {\r\n hits.push({\r\n matcher_kind: 'package-json-field',\r\n matcher_description: matcher.description,\r\n file: file.path,\r\n line,\r\n excerpt: text.trim().slice(0, 200),\r\n });\r\n }\r\n }\r\n return hits;\r\n}\r\n", "/**\r\n * Evidence-matcher dispatch.\r\n *\r\n * Routes by `matcher.kind`. Unrecognized kinds (`ast-pattern`, `custom`)\r\n * return an empty hit list \u2014 honest about the limitation rather than\r\n * silently passing. Rules that depend on those kinds must declare it\r\n * in their rationale.\r\n */\r\nimport type { ParsedFile } from '../diff-parser.js';\r\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\r\nimport { runRegexMatcher } from './regex-matcher.js';\r\nimport { runImportDetector } from './import-detector.js';\r\nimport { runFileSizeMatcher } from './file-size.js';\r\nimport { runPackageJsonMatcher } from './package-json.js';\r\n\r\nexport function runEvidenceMatcher(\r\n matcher: EvidenceMatcher,\r\n file: ParsedFile,\r\n): ReadonlyArray<EvidenceHit> {\r\n switch (matcher.kind) {\r\n case 'regex':\r\n return runRegexMatcher(matcher, file);\r\n case 'import-detector':\r\n return runImportDetector(matcher, file);\r\n case 'file-size':\r\n return runFileSizeMatcher(matcher, file);\r\n case 'package-json-field':\r\n return runPackageJsonMatcher(matcher, file);\r\n case 'ast-pattern':\r\n case 'custom':\r\n // Documented limitation: v1 has no built-in AST runner. Rules of\r\n // these kinds rely on external tooling that calls the query API\r\n // and supplies its own evidence. We surface the rule as applicable\r\n // (via applies_when) without inventing fake hits.\r\n return [];\r\n default:\r\n return [];\r\n }\r\n}\r\n", "/**\r\n * `computeStaleRules` \u2014 flag rules whose `last_verified` date has aged out.\r\n *\r\n * Added 2026-05-24 (audit MEDIUM \"no signal that an architectural rule is\r\n * stale\"). The schema already requires every rule to carry an ISO\r\n * `last_verified` date; this helper turns that into an OBSERVATIONAL\r\n * signal callers can surface so operators know a rule needs review.\r\n *\r\n * Important semantics \u2014 DO NOT change without coordinator approval:\r\n *\r\n * - **Stale rules still fire.** A rule that hasn't been verified in 18\r\n * months is more likely to be wrong, not less likely to apply. We do\r\n * NOT drop stale rules from `result.applicable` \u2014 they go through the\r\n * normal stack/change/files filter pipeline like any other rule. The\r\n * staleness signal is a separate `result.stale_rules` array the\r\n * operator can act on independently (e.g. emit a stderr warning, or\r\n * nag during `vo-arch-check --staleness-fail`).\r\n *\r\n * - **Threshold default = 365 days.** Chosen because the v1 corpus was\r\n * last fully reviewed 2026-05-22, so a 365-day default gives operators\r\n * a year before the first \"review me\" signal fires. Caller can pass\r\n * `Infinity` to disable, or any other day count.\r\n *\r\n * - **`now` is injectable for tests.** Defaults to `new Date()` so\r\n * production code calls without ceremony.\r\n *\r\n * - **Pure function \u2014 no I/O.** `findApplicableRules` calls this with\r\n * the post-merge (post-suppression) rule set, so tenant-suppressed\r\n * rules never appear in `stale_rules`.\r\n *\r\n * - **Malformed `last_verified` is impossible at this layer** because\r\n * the Zod schema rejects anything that isn't a real ISO date at load\r\n * time. We still defensively skip un-parseable dates rather than\r\n * throw, so a future schema relaxation can't cascade into a runtime\r\n * crash inside the query path.\r\n */\r\nimport type { ArchitecturalDefaultRule } from '../types.js';\r\n\r\n/** Default staleness threshold. See \"Threshold default\" in the module docstring. */\r\nexport const DEFAULT_STALENESS_THRESHOLD_DAYS = 365;\r\n\r\nexport interface StaleRule {\r\n readonly rule_id: string;\r\n /** Original ISO date from the rule. */\r\n readonly last_verified: string;\r\n /** Calendar days between `last_verified` and `now`. Always > threshold. */\r\n readonly days_stale: number;\r\n}\r\n\r\nexport interface ComputeStaleRulesOptions {\r\n /** Days before a rule is considered stale. Default 365. Pass `Infinity` to disable. */\r\n readonly thresholdDays?: number;\r\n /** Reference date for the calculation. Default `new Date()`. Tests inject a fixed instant. */\r\n readonly now?: Date;\r\n}\r\n\r\n/**\r\n * Return the subset of `rules` whose `last_verified` is older than\r\n * `thresholdDays`. Empty array when nothing is stale, or when the\r\n * threshold is `Infinity`.\r\n *\r\n * Day-count math: `Math.floor((now - last_verified) / 86_400_000)`. This\r\n * is a calendar-day approximation, not a wall-clock calculation \u2014 DST\r\n * boundaries and leap seconds are ignored. Adequate for a >365-day signal.\r\n */\r\nexport function computeStaleRules(\r\n rules: ReadonlyArray<ArchitecturalDefaultRule>,\r\n opts: ComputeStaleRulesOptions = {},\r\n): ReadonlyArray<StaleRule> {\r\n const threshold = opts.thresholdDays ?? DEFAULT_STALENESS_THRESHOLD_DAYS;\r\n // `Infinity` (or any non-finite) \u2192 staleness disabled entirely.\r\n if (!Number.isFinite(threshold)) return [];\r\n // Negative or zero threshold \u2192 would mark everything stale. Treat as\r\n // \"disable\" rather than \"panic the operator with 36 entries on day 1.\"\r\n if (threshold <= 0) return [];\r\n\r\n const now = opts.now ?? new Date();\r\n const nowMs = now.getTime();\r\n if (!Number.isFinite(nowMs)) return [];\r\n\r\n const out: StaleRule[] = [];\r\n for (const rule of rules) {\r\n const verifiedMs = Date.parse(rule.last_verified + 'T00:00:00Z');\r\n if (!Number.isFinite(verifiedMs)) continue;\r\n const daysStale = Math.floor((nowMs - verifiedMs) / 86_400_000);\r\n if (daysStale > threshold) {\r\n out.push({\r\n rule_id: rule.rule_id,\r\n last_verified: rule.last_verified,\r\n days_stale: daysStale,\r\n });\r\n }\r\n }\r\n // Sort most-stale first so operators see worst offenders at the top.\r\n out.sort((a, b) => b.days_stale - a.days_stale);\r\n return out;\r\n}\r\n", "/**\r\n * Main query entry \u2014 `findApplicableRules`.\r\n *\r\n * Pipeline:\r\n * 1. Load bundled corpus.\r\n * 2. Load tenant override (if path provided / default exists).\r\n * 3. Merge \u2192 effective rule list.\r\n * 4. For each rule, apply stack / change_type / file_globs filters.\r\n * 5. If a diff_excerpt was provided, parse it and run evidence matchers\r\n * for each applicable rule \u00D7 touched file.\r\n * 6. Return `QueryResult` (the applicable rules + per-rule hits +\r\n * bookkeeping counts).\r\n *\r\n * Important honesty rule: a rule that is \"applicable\" but produced zero\r\n * evidence hits IS STILL SURFACED in the result \u2014 the senior-architect\r\n * panel may want to consider it. We never drop applicable rules just\r\n * because we couldn't detect evidence.\r\n */\r\nimport type {\r\n ArchitecturalDefaultRule,\r\n EvidenceHit,\r\n QueryHit,\r\n QueryInput,\r\n QueryResult,\r\n TenantOverride,\r\n} from '../types.js';\r\nimport { loadBundledCorpus } from '../storage/load-bundled.js';\r\nimport { loadTenantOverride } from '../storage/load-override.js';\r\nimport { mergeCorpusWithOverride } from '../storage/merge.js';\r\nimport { appliesToStack } from './applies-to-stack.js';\r\nimport { appliesToChangeType } from './applies-to-change.js';\r\nimport { appliesToFiles } from './applies-to-files.js';\r\nimport { parseUnifiedDiff } from '../analyze/diff-parser.js';\r\nimport { runEvidenceMatcher } from '../analyze/evidence-matchers/index.js';\r\nimport { computeStaleRules } from './staleness.js';\r\n\r\nexport interface FindApplicableRulesOptions {\r\n /** Use this in-memory rule list instead of loading from disk. Tests use this. */\r\n readonly rules?: ReadonlyArray<ArchitecturalDefaultRule>;\r\n /** Use this override directly instead of reading from disk. */\r\n readonly override?: TenantOverride | null;\r\n /** Override the corpus directory (passed to bundled loader). */\r\n readonly corpusDir?: string;\r\n /** Override file for the tenant override loader. */\r\n readonly tenantOverridePath?: string;\r\n /**\r\n * Staleness threshold in days for `result.stale_rules`. Default 365.\r\n * Pass `Infinity` to disable the staleness signal entirely. See\r\n * `src/query/staleness.ts` for full semantics. Added 2026-05-24.\r\n */\r\n readonly stalenessThresholdDays?: number;\r\n /** Reference date for the staleness calculation. Default `new Date()`. */\r\n readonly now?: Date;\r\n}\r\n\r\nexport function findApplicableRules(\r\n input: QueryInput,\r\n opts: FindApplicableRulesOptions = {},\r\n): QueryResult {\r\n // --- Load ---------------------------------------------------------\r\n const bundled =\r\n opts.rules !== undefined\r\n ? opts.rules\r\n : loadBundledCorpus({ corpusDir: opts.corpusDir }).rules;\r\n const override =\r\n opts.override !== undefined\r\n ? opts.override\r\n : loadTenantOverride({ path: opts.tenantOverridePath }).override;\r\n const merged = mergeCorpusWithOverride(bundled, override);\r\n\r\n // --- Optional diff parse for evidence -----------------------------\r\n const parsedDiff = input.diff_excerpt\r\n ? parseUnifiedDiff(input.diff_excerpt)\r\n : null;\r\n\r\n // --- Filter + collect --------------------------------------------\r\n // Pre-compute metadata filter Sets for O(1) lookup. `undefined` means\r\n // \"no filter at this level\" (current behavior preserved). Empty array\r\n // produces a Set of size 0 \u2014 matches nothing (deliberate narrow query).\r\n const categoryFilter =\r\n input.categories !== undefined ? new Set<string>(input.categories) : null;\r\n const tagAnyFilter = input.tags_any !== undefined ? new Set<string>(input.tags_any) : null;\r\n\r\n const applicable: QueryHit[] = [];\r\n for (const rule of merged.rules) {\r\n const stackOk = appliesToStack(rule, input.stack);\r\n const changeOk = appliesToChangeType(rule, input.change_type);\r\n const filesOk = appliesToFiles(rule, input.file_paths);\r\n if (!stackOk || !changeOk || !filesOk) continue;\r\n\r\n // Metadata filters (added 2026-05-24). Both run AFTER the stack/change/\r\n // files filters so the existing applicability semantics are unchanged\r\n // for callers that don't pass categories / tags_any.\r\n if (categoryFilter !== null && !categoryFilter.has(rule.category)) continue;\r\n if (tagAnyFilter !== null) {\r\n // Intersect: rule passes if ANY of its tags is in tag_any. Empty\r\n // tag_any \u2192 no intersection possible \u2192 rule filtered out.\r\n let tagHit = false;\r\n for (const t of rule.tags) {\r\n if (tagAnyFilter.has(t)) {\r\n tagHit = true;\r\n break;\r\n }\r\n }\r\n if (!tagHit) continue;\r\n }\r\n\r\n const hits: EvidenceHit[] = [];\r\n if (parsedDiff !== null) {\r\n for (const file of parsedDiff.files) {\r\n // Per-file applicability: a rule with file_globs only fires\r\n // evidence against files that match those globs.\r\n const filePathArr: ReadonlyArray<string> = [file.path];\r\n if (!appliesToFiles(rule, filePathArr)) continue;\r\n for (const matcher of rule.evidence_of_violation) {\r\n const fileHits = runEvidenceMatcher(matcher, file);\r\n for (const h of fileHits) hits.push(h);\r\n }\r\n }\r\n }\r\n applicable.push({\r\n rule,\r\n reason: {\r\n stack_matched: stackOk,\r\n change_type_matched: changeOk,\r\n file_glob_matched: filesOk,\r\n },\r\n evidence: hits,\r\n });\r\n }\r\n\r\n // Staleness signal \u2014 observational only, computed over the merged\r\n // (post-suppression) rule set so suppressed rules never show up. Default\r\n // threshold lives in `staleness.ts` (365 days). Caller can disable by\r\n // passing `Infinity`.\r\n const stale_rules = computeStaleRules(merged.rules, {\r\n ...(opts.stalenessThresholdDays !== undefined\r\n ? { thresholdDays: opts.stalenessThresholdDays }\r\n : {}),\r\n ...(opts.now !== undefined ? { now: opts.now } : {}),\r\n });\r\n\r\n return {\r\n applicable,\r\n considered_count: merged.rules.length,\r\n suppressed_count: merged.suppressed_count,\r\n stale_rules,\r\n };\r\n}\r\n", "/**\r\n * Compute a stable version hash of the loaded rule corpus.\r\n *\r\n * Consumers (notably the vo-mcp content-hash cache) mix this hash into the\r\n * cache-key namespace so that adding / modifying / removing a rule\r\n * invalidates any cached envelope whose verdict was computed without\r\n * knowledge of the changed rule. Pure rule_id+rule_version pairs (NOT\r\n * file contents) are hashed: whitespace edits to rule JSON don't bust\r\n * the cache; bumping a rule_version or adding a rule does.\r\n *\r\n * The hash is intentionally SHORT (16 hex chars) \u2014 embedded in a cache\r\n * key namespace string that's already opaque; full sha256 length adds\r\n * no real collision resistance for the universe of corpus snapshots.\r\n *\r\n * Pure function \u2014 no I/O. Caller supplies the already-loaded rules.\r\n */\r\nimport { createHash } from 'node:crypto';\r\nimport type { ArchitecturalDefaultRule } from '../types.js';\r\n\r\n/**\r\n * Hash the corpus version. Input is the rules array from `loadBundledCorpus`\r\n * (already validated). Order-insensitive: rules are sorted by rule_id before\r\n * hashing so two different file-system orderings of the same corpus yield\r\n * the same hash.\r\n *\r\n * Returns a 16-char lowercase hex string (first 64 bits of sha256). Stable\r\n * across processes + platforms.\r\n *\r\n * Empty rule list \u2192 returns the well-known zero-corpus hash (the sha256\r\n * of the empty string, first 16 chars). This avoids the namespace flipping\r\n * between \"no corpus\" and \"empty corpus\" representations.\r\n */\r\nexport function computeCorpusVersionHash(\r\n rules: ReadonlyArray<Pick<ArchitecturalDefaultRule, 'rule_id' | 'rule_version'>>,\r\n): string {\r\n // Sort by rule_id (stable, locale-independent string compare).\r\n const tuples = rules\r\n .map((r) => `${r.rule_id}@${r.rule_version}`)\r\n .sort();\r\n const hash = createHash('sha256');\r\n for (const tuple of tuples) {\r\n hash.update(tuple);\r\n hash.update('\\n'); // separator \u2014 prevents 'a@1' + 'b@2' colliding with 'a@1b@2'\r\n }\r\n return hash.digest('hex').slice(0, 16);\r\n}\r\n", "/**\r\n * `resolveStack` \u2014 pick the effective stack list for a KB query.\r\n *\r\n * Added 2026-05-24 for audit HIGH \"DEFAULT_STACK hardcoded for Nexus repo\".\r\n * Consumers (vo-mcp KB pre-filter, vo-arch-check CLI) call this once at\r\n * startup with the tenant override + their built-in fallback. When the\r\n * tenant has dropped a `~/.claude/vo-arch-defaults.local.json` with\r\n * `tenant_stack: [...]`, that list wins; otherwise the consumer's fallback\r\n * applies (preserves pre-2026-05-24 behavior for tenants that haven't\r\n * configured one).\r\n *\r\n * Pure function \u2014 no I/O. The override loader (`loadTenantOverride`) is\r\n * the caller's responsibility; this helper just picks between the two\r\n * resolved values. Empty `tenant_stack: []` is honored as-is (means\r\n * \"match only stack-agnostic rules\" \u2014 the consumer might want this).\r\n */\r\nimport type { Stack, TenantOverride } from '../types.js';\r\n\r\nexport function resolveStack(\r\n override: TenantOverride | null,\r\n fallback: ReadonlyArray<Stack>,\r\n): ReadonlyArray<Stack> {\r\n if (override !== null && override.tenant_stack !== undefined) {\r\n return override.tenant_stack;\r\n }\r\n return fallback;\r\n}\r\n", "/**\r\n * PII / secret scrubber for excerpts that ship to third-party model providers\r\n * or land in the on-disk audit log.\r\n *\r\n * **Location rationale (PR-U 2026-05-25).** This used to live in vo-mcp's\r\n * `logging/events-writer.ts`. It moved here so every KB consumer (current +\r\n * future) can sanitize evidence excerpts without taking a dependency on\r\n * vo-mcp. The KB pre-filter (`architecture-review-kb-prefilter.ts`,\r\n * `kb-metadata-prefilter.ts`) needs this BEFORE inlining matched-rule\r\n * evidence into a senior-architect prompt \u2014 `security/no-hardcoded-secrets`\r\n * fires on lines that contain real secrets, and we must not echo those\r\n * verbatim to third-party model providers.\r\n *\r\n * vo-mcp's `events-writer.ts` re-exports `sanitizeExcerpt` from this module\r\n * for back-compat with existing import sites; downstream callers don't need\r\n * to migrate.\r\n *\r\n * ## Patterns scrubbed\r\n *\r\n * - JWTs (three base64url segments joined by `.`)\r\n * - AWS access keys (`AKIA` / `ASIA` prefix + 16 chars)\r\n * - Google API keys (`AIza` + 35 chars)\r\n * - GitHub tokens (`ghp_` / `gho_` / `ghu_` / `ghs_` / `ghr_` + 36+ chars)\r\n * - Stripe secret keys (`sk_live_` / `sk_test_` + 20+ chars; `pk_` NOT\r\n * scrubbed because publishable keys are intentionally public)\r\n * - Generic `sk-` prefixed keys (Anthropic / OpenAI style)\r\n * - Bearer tokens\r\n * - Emails\r\n * - US SSN format (ddd-dd-dddd)\r\n * - File paths revealing usernames (`/Users/<name>/`,\r\n * `C:\\Users\\<name>\\`, `/home/<name>/`)\r\n *\r\n * ## What is kept\r\n *\r\n * - Code text \u2014 we want to debug failures; tool inputs and matched-rule\r\n * evidence ARE code text.\r\n *\r\n * Returns a string at most `maxLen` chars long. Default per handoff \u00A76: 200.\r\n */\r\n\r\n/** Default excerpt length cap \u2014 handoff \u00A76. */\r\nexport const SANITIZE_DEFAULT_MAX_LEN = 200;\r\n\r\nexport function sanitizeExcerpt(raw: string, maxLen: number = SANITIZE_DEFAULT_MAX_LEN): string {\r\n let s = raw;\r\n // JWTs first \u2014 broad pattern, run before narrower base64-ish patterns\r\n s = s.replace(/eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{5,}/g, '[REDACTED_JWT]');\r\n // AWS access keys \u2014 well-defined fixed-length prefix family\r\n s = s.replace(/\\b(?:AKIA|ASIA)[A-Z0-9]{16}\\b/g, '[REDACTED_AWS_KEY]');\r\n // Google API keys \u2014 fixed prefix + 35 char body\r\n s = s.replace(/\\bAIza[0-9A-Za-z_-]{35}\\b/g, '[REDACTED_GOOGLE_KEY]');\r\n // GitHub tokens \u2014 gh[pousr]_ + 36+ char body\r\n s = s.replace(/\\bgh[pousr]_[A-Za-z0-9]{36,}\\b/g, '[REDACTED_GITHUB_TOKEN]');\r\n // Stripe secret keys \u2014 sk_live_ / sk_test_ + 20+ chars\r\n s = s.replace(/\\bsk_(?:live|test)_[A-Za-z0-9_-]{20,}/g, '[REDACTED_STRIPE_KEY]');\r\n // Generic sk- (Anthropic/OpenAI style) \u2014 runs AFTER Stripe so sk_live_ doesn't\r\n // double-match (different separator: underscore vs hyphen)\r\n s = s.replace(/sk-[A-Za-z0-9]{16,}/g, '[REDACTED_KEY]');\r\n // Bearer\r\n s = s.replace(/Bearer\\s+[A-Za-z0-9_\\-.]{16,}/g, 'Bearer [REDACTED]');\r\n // Emails\r\n s = s.replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g, '[REDACTED_EMAIL]');\r\n // US SSN format (ddd-dd-dddd). Word boundaries avoid matching the middle of\r\n // longer digit runs (e.g. credit-card-ish 12-345-6789 segments).\r\n s = s.replace(/\\b\\d{3}-\\d{2}-\\d{4}\\b/g, '[REDACTED_SSN]');\r\n // File paths revealing usernames. Keeps the directory-type hint (/Users/ vs\r\n // /home/) so debugging context isn't fully destroyed; replaces only the\r\n // username segment. Cross-platform: matches forward AND back slashes.\r\n // Word-boundary front-anchor avoids matching mid-token (e.g. 'getUsers/foo').\r\n s = s.replace(/(?<![A-Za-z0-9])([\\\\/])(Users|home)\\1[^\\\\/\\s'\"`]+/g, '$1$2$1[USER]');\r\n // Truncate\r\n if (s.length > maxLen) s = s.slice(0, maxLen) + '\u2026';\r\n return s;\r\n}\r\n", "/**\r\n * Local mode \u2014 no cloud control plane.\r\n *\r\n * `isCloudConnected()` returns false. `tenant_id` and `operator_id`\r\n * default to `'local'`.\r\n */\r\nexport interface LocalModeContext {\r\n readonly mode: 'local';\r\n readonly tenantId: 'local';\r\n readonly operatorId: 'local';\r\n isCloudConnected(): boolean;\r\n}\r\n\r\nexport function createLocalMode(): LocalModeContext {\r\n return {\r\n mode: 'local',\r\n tenantId: 'local',\r\n operatorId: 'local',\r\n isCloudConnected(): boolean {\r\n return false;\r\n },\r\n };\r\n}\r\n", "/**\r\n * Tool: `vo_check_assertion_strength`\r\n *\r\n * Phase 1 status: **implemented** against the stub ratchet client.\r\n * Real thresholds come from `@algosuite/ratchets-generic` in Phase 2.\r\n *\r\n * Contract:\r\n * input: { source: string, file_path?: string, language?: string }\r\n * output: ToolResultEnvelope<AssertionStrengthPayload>\r\n *\r\n * Behavior:\r\n * 1. Hash the canonicalized input. If the cache has a hit, return it\r\n * (with `cache.hit = true`) WITHOUT re-running the ratchet.\r\n * 2. Otherwise call the ratchet client, build the envelope, write it\r\n * to the cache, and return it (with `cache.hit = false`).\r\n * 3. EVERY call also writes a consensus-call event line (gate #7).\r\n * For non-consensus tools, `per_model_verdicts` is empty by design.\r\n */\r\nimport type { ToolDeps, ToolResultEnvelope, AssertionStrengthPayload } from '../types.js';\r\nimport {\r\n assertWithinByteCap,\r\n buildBaseEvent,\r\n bytesOf,\r\n invalidParams,\r\n jsonContent,\r\n type ToolInputSchema,\r\n} from './common.js';\r\n\r\nexport const TOOL_NAME = 'vo_check_assertion_strength';\r\n\r\n/** Static-ratchet tool \u2014 not consensus-engine routed. */\r\nconst GATE_TYPE = 'ratchet' as const;\r\n\r\n/** 512 KB \u2014 typical test file is <10 KB; a 500 KB ceiling catches accidental whole-file/whole-repo pastes. */\r\nconst MAX_SOURCE_BYTES = 512 * 1024;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n source: {\r\n type: 'string',\r\n description: 'Full test file body to analyze.',\r\n },\r\n file_path: {\r\n type: 'string',\r\n description: 'Optional path hint for nicer findings messages.',\r\n },\r\n language: {\r\n type: 'string',\r\n enum: ['ts', 'tsx', 'js', 'jsx', 'py'],\r\n description: 'Optional language hint.',\r\n },\r\n },\r\n required: ['source'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Analyzes a test file's assertions and returns a strength score (0-100), an overall verdict (strong/weak/hollow), and per-assertion findings. Hollow patterns flagged include toBeDefined()-only checks, toBeTruthy/toBeFalsy against literals, and tautological self-comparisons. Strong patterns rewarded include toBe, toEqual, toMatchObject, toContain, toHaveBeenCalledWith, toStrictEqual. Use BEFORE accepting a generated or hand-written test, to catch tests that 'pass' without verifying product truth. Open-shell ratchet \u2014 institutional thresholds load from a closed ratchet library; the scoring shape is stable across versions.\";\r\n\r\ninterface ToolInput {\r\n readonly source: string;\r\n readonly file_path?: string;\r\n readonly language?: string;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['source'] !== 'string') return false;\r\n if (o['file_path'] !== undefined && typeof o['file_path'] !== 'string') return false;\r\n if (o['language'] !== undefined && typeof o['language'] !== 'string') return false;\r\n return true;\r\n}\r\n\r\nexport async function handleCheckAssertionStrength(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n // Accepted for ToolHandler signature parity with the other handlers\r\n // (2026-05-25 audit HIGH \u2014 MCP cancellation). This tool only calls the\r\n // ratchet stub, which is synchronous and short \u2014 cancellation has no\r\n // meaningful effect mid-call. Eslint-prefixed underscore marks the\r\n // parameter as deliberately unused.\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Required: { source: string }. Optional: file_path, language.',\r\n );\r\n }\r\n assertWithinByteCap(TOOL_NAME, 'source', rawInput.source, MAX_SOURCE_BYTES);\r\n const key = deps.cache.keyFor(TOOL_NAME, rawInput);\r\n const excerpt = rawInput.source.slice(0, 300);\r\n\r\n const inputSizeBytes = bytesOf(rawInput.source);\r\n const cached = deps.cache.get<ToolResultEnvelope<AssertionStrengthPayload>>(key);\r\n if (cached) {\r\n const envelope: ToolResultEnvelope<AssertionStrengthPayload> = {\r\n ...cached.value,\r\n cache: { hit: true, key },\r\n };\r\n deps.events.append({\r\n ...buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n }),\r\n cache_hit: true,\r\n });\r\n return jsonContent(envelope);\r\n }\r\n\r\n const result = await deps.ratchets.checkAssertionStrength({\r\n source: rawInput.source,\r\n ...(rawInput.file_path !== undefined ? { filePath: rawInput.file_path } : {}),\r\n ...(rawInput.language !== undefined ? { language: rawInput.language } : {}),\r\n });\r\n\r\n const payload: AssertionStrengthPayload = {\r\n score: result.score,\r\n max_score: result.maxScore,\r\n verdict: result.verdict,\r\n per_assertion_findings: result.findings,\r\n summary: result.summary,\r\n };\r\n\r\n const envelope: ToolResultEnvelope<AssertionStrengthPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n\r\n // Store the canonical (non-hit) version so subsequent gets reflect cache.hit=true.\r\n deps.cache.set(key, envelope);\r\n\r\n deps.events.append(\r\n buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n }),\r\n );\r\n\r\n return jsonContent(envelope);\r\n}\r\n", "/**\r\n * KB pre-filter for `vo_architecture_review` (Gate #5 vo-arch-defaults wire-up).\r\n *\r\n * Extracted from `architecture-review.ts` 2026-05-24 to keep the main tool\r\n * file under the 400-line cap (CLAUDE.md MANDATORY: File Size Limits).\r\n *\r\n * Three concerns:\r\n * 1. Look up which architectural-defaults rules apply to a diff\r\n * (`findRulesForDiff`).\r\n * 2. Sort + cap the matched rules so a wide-touch diff doesn't blow the\r\n * prompt budget (`prepareKBRules`, `MAX_KB_RULES = 10`).\r\n * 3. Render the matched rules as a prompt fragment, sanitizing any PII\r\n * from evidence excerpts before they ship to third-party models\r\n * (`formatRulesForPrompt`).\r\n *\r\n * All three are exported so the architecture-review-kb test file can unit-test\r\n * them directly.\r\n */\r\nimport {\r\n findApplicableRules,\r\n loadTenantOverride,\r\n parseUnifiedDiff,\r\n resolveStack,\r\n // sanitizeExcerpt moved into vo-arch-defaults in PR-U 2026-05-25 \u2014 direct\r\n // import here removes the cross-package indirection through vo-mcp's\r\n // events-writer re-export. Same function, single source.\r\n sanitizeExcerpt,\r\n type QueryHit,\r\n} from '@algosuite/vo-arch-defaults';\r\n\r\n/**\r\n * Default stack profile for the Nexus repo. Open string union; out-of-tree\r\n * tenants override this list via `tenant_stack` in their\r\n * `~/.claude/vo-arch-defaults.local.json` \u2014 see audit HIGH 2026-05-24.\r\n * Keep this list narrow: extra stack entries cost evidence-matcher CPU per\r\n * call but never suppress rules incorrectly (stack is OR-intersect).\r\n */\r\nexport const DEFAULT_STACK: ReadonlyArray<string> = [\r\n 'node',\r\n 'node-pnpm-monorepo',\r\n 'typescript-strict',\r\n 'firebase-cloud-functions',\r\n 'react-frontend',\r\n];\r\n\r\n/**\r\n * Resolve the effective stack for KB queries. Tenant `~/.claude/\r\n * vo-arch-defaults.local.json` with `tenant_stack: [...]` wins; otherwise\r\n * `DEFAULT_STACK` applies.\r\n *\r\n * Cached at module level \u2014 the tenant override file is read once per MCP\r\n * server process. Server restart picks up changes (the common operator\r\n * workflow). Tests use `__resetEffectiveStackCacheForTests` to invalidate\r\n * between fixture swaps.\r\n */\r\nlet _cachedEffectiveStack: ReadonlyArray<string> | null = null;\r\n\r\nexport function getEffectiveStack(): ReadonlyArray<string> {\r\n if (_cachedEffectiveStack === null) {\r\n try {\r\n const override = loadTenantOverride().override;\r\n _cachedEffectiveStack = resolveStack(override, DEFAULT_STACK);\r\n } catch {\r\n // Override file unreadable / malformed \u2192 log via the same channel\r\n // findRulesForDiff uses; fall back to DEFAULT_STACK so the KB still\r\n // works against the bundled-corpus assumed stack.\r\n process.stderr.write(\r\n `[vo-mcp] vo-arch-defaults tenant override unreadable; falling back to DEFAULT_STACK\\n`,\r\n );\r\n _cachedEffectiveStack = DEFAULT_STACK;\r\n }\r\n }\r\n return _cachedEffectiveStack;\r\n}\r\n\r\n/** Test-only: invalidate the cached effective-stack so the next call re-reads. */\r\nexport function __resetEffectiveStackCacheForTests(): void {\r\n _cachedEffectiveStack = null;\r\n}\r\n\r\n/**\r\n * Maximum architectural-rules surfaced into the consensus prompt. The v1 corpus\r\n * is ~36 rules; a wide-touch diff can match a dozen+ at once. Cap to bound\r\n * prompt token cost and keep the panel focused on the highest-severity items.\r\n * Lower-severity rules truncated \u2192 surfaced as `kb_rules_truncated` on the\r\n * envelope so operators can tune diff scope.\r\n */\r\nexport const MAX_KB_RULES = 10;\r\n\r\n/** Severity ordering \u2014 lower number = higher priority (surfaced first). */\r\nconst SEVERITY_RANK: Record<string, number> = {\r\n blocker: 0,\r\n warning: 1,\r\n info: 2,\r\n};\r\n\r\n/** Result of a KB lookup \u2014 `null` error means lookup succeeded, even if rules is `[]`. */\r\nexport interface KBLookupResult {\r\n readonly rules: ReadonlyArray<QueryHit>;\r\n /** Human-readable error if the KB threw; `null` on success. */\r\n readonly error: string | null;\r\n}\r\n\r\n/**\r\n * Look up architectural-defaults rules that apply to this diff.\r\n *\r\n * Catches all errors (corpus load failure, override parse failure,\r\n * malformed diff) \u2014 KB unavailability must NOT block the architecture\r\n * review itself; the engine still runs without rule context. But UNLIKE\r\n * the original silent-swallow implementation (audit HIGH 2026-05-23), this\r\n * variant SURFACES the error: stderr line for operator visibility + an\r\n * `error` field on the return value so callers can flag the envelope.\r\n */\r\nexport function findRulesForDiff(diff: string): KBLookupResult {\r\n try {\r\n const parsed = parseUnifiedDiff(diff);\r\n const filePaths = parsed.files.map((f) => f.path);\r\n if (filePaths.length === 0) return { rules: [], error: null };\r\n const result = findApplicableRules({\r\n stack: getEffectiveStack(),\r\n change_type: 'any',\r\n file_paths: filePaths,\r\n diff_excerpt: diff,\r\n });\r\n return { rules: result.applicable, error: null };\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n // Operator-visible diagnostic. cli.ts already uses stderr for warnings,\r\n // so this lands in the same place a sysadmin would look.\r\n process.stderr.write(`[vo-mcp] vo-arch-defaults KB unavailable: ${message}\\n`);\r\n return { rules: [], error: message };\r\n }\r\n}\r\n\r\n/**\r\n * Sort rules by severity (blocker \u2192 warning \u2192 info \u2192 unknown), with rule_id\r\n * as a stable tiebreaker, then cap to `MAX_KB_RULES`. Returns the top-N rules\r\n * and the count of rules dropped (for `kb_rules_truncated` surfacing).\r\n */\r\nexport function prepareKBRules(hits: ReadonlyArray<QueryHit>): {\r\n topRules: ReadonlyArray<QueryHit>;\r\n truncated: number;\r\n} {\r\n const sorted = [...hits].sort((a, b) => {\r\n const sa = SEVERITY_RANK[a.rule.severity] ?? 99;\r\n const sb = SEVERITY_RANK[b.rule.severity] ?? 99;\r\n if (sa !== sb) return sa - sb;\r\n return a.rule.rule_id.localeCompare(b.rule.rule_id);\r\n });\r\n const topRules = sorted.slice(0, MAX_KB_RULES);\r\n const truncated = Math.max(0, sorted.length - topRules.length);\r\n return { topRules, truncated };\r\n}\r\n\r\n/**\r\n * Render matched rules as a prompt fragment. Returns empty string when no\r\n * rules matched \u2014 we deliberately don't emit a \"[no rules matched]\" line,\r\n * to keep prompt token cost minimal on diffs the KB doesn't cover.\r\n *\r\n * Each evidence excerpt is run through `sanitizeExcerpt` (events-writer's\r\n * PII scrubber) BEFORE inlining into the prompt: rules like\r\n * `security/no-hardcoded-secrets` fire on lines that contain real secrets,\r\n * and we must not echo those verbatim to third-party model providers.\r\n * (Audit HIGH 2026-05-23 \u2014 \"PII redaction on KB evidence excerpts.\")\r\n */\r\nexport function formatRulesForPrompt(\r\n hits: ReadonlyArray<QueryHit>,\r\n truncated: number,\r\n /**\r\n * Domain label inserted into the section header. Default 'ARCHITECTURAL'\r\n * preserves the architecture-review prompt header. Other tools pass their\r\n * own label (e.g. 'TESTING', 'SECURITY') so the panel sees a category\r\n * cue in the prompt block.\r\n */\r\n domainLabel: string = 'ARCHITECTURAL',\r\n): string {\r\n if (hits.length === 0) return '';\r\n const lines: string[] = ['', `---${domainLabel} RULES MATCHED FROM KB---`];\r\n for (const hit of hits) {\r\n const evidenceTag =\r\n hit.evidence.length > 0\r\n ? ` (${hit.evidence.length} evidence hit${hit.evidence.length === 1 ? '' : 's'} from mechanical detector)`\r\n : '';\r\n lines.push(\r\n `- [${hit.rule.severity}] ${hit.rule.rule_id}: ${hit.rule.title}${evidenceTag}`,\r\n );\r\n lines.push(` Rationale: ${hit.rule.rationale}`);\r\n if (hit.evidence.length > 0) {\r\n for (const e of hit.evidence.slice(0, 3)) {\r\n const loc = e.line !== undefined ? `${e.file}:${e.line}` : e.file;\r\n lines.push(` - ${loc} \u2014 ${sanitizeExcerpt(e.excerpt)}`);\r\n }\r\n }\r\n }\r\n if (truncated > 0) {\r\n lines.push(\r\n `... and ${truncated} more lower-severity rule${truncated === 1 ? '' : 's'} truncated to bound prompt size.`,\r\n );\r\n }\r\n lines.push('---END KB RULES---');\r\n return lines.join('\\n');\r\n}\r\n", "/**\r\n * KB pre-filter for tools that don't have a diff input \u2014 `vo_check_hollow_test`\r\n * and `vo_verify_answer` deep mode. Generalizes the pattern from\r\n * `architecture-review-kb-prefilter.ts` for the metadata-driven case.\r\n *\r\n * Architecture-review uses `findRulesForDiff` because it has a diff to parse +\r\n * run evidence matchers against. Hollow-test (sees a test source) and verify-\r\n * answer (sees expected/observed values) don't have a diff \u2014 they want to\r\n * surface rules by `category` (and optionally `tags`) regardless of file paths.\r\n *\r\n * Reuses `prepareKBRules` + `formatRulesForPrompt` + `KBLookupResult` from the\r\n * architecture-review helper so both pre-filter flavors render identically.\r\n * Uses the same DEFAULT_STACK so stack-gated rules surface consistently.\r\n */\r\nimport {\r\n loadBundledCorpus,\r\n loadTenantOverride,\r\n mergeCorpusWithOverride,\r\n type QueryHit,\r\n} from '@algosuite/vo-arch-defaults';\r\nimport { type KBLookupResult } from './architecture-review-kb-prefilter.js';\r\n\r\n/**\r\n * Options for the metadata-driven KB lookup.\r\n *\r\n * Either `category` or `tagsAny` (or both) MUST be provided \u2014 without at least\r\n * one filter, every rule in the corpus would surface (~30+ at v1) and blow\r\n * the prompt budget. Tools call with hardcoded values:\r\n *\r\n * `vo_check_hollow_test` \u2192 `{ category: 'testing' }`\r\n * `vo_verify_answer` deep \u2192 `{ category: 'security' }` (or `{ tagsAny: ['source-grounded'] }` for narrower)\r\n */\r\nexport interface FindRulesByMetadataOptions {\r\n /** Restrict to rules whose `category` equals this value. */\r\n readonly category?: string;\r\n /** Restrict to rules whose `tags` intersect this list (any-of semantics). */\r\n readonly tagsAny?: ReadonlyArray<string>;\r\n}\r\n\r\n/**\r\n * Look up KB rules by metadata (category and/or tags). Mirrors\r\n * `findRulesForDiff` graceful-degrade contract: catches all errors, surfaces\r\n * via stderr + `error` field on the return so the caller can flag the\r\n * envelope (`kb_unavailable: true`).\r\n *\r\n * Bypasses the stack / change_type / file_globs filters of\r\n * `findApplicableRules` \u2014 those gates are designed for diff-driven queries.\r\n * Metadata-driven queries (hollow-test, verify-answer) don't have a diff;\r\n * filtering by stack/files would incorrectly drop rules whose `applies_when`\r\n * narrows to specific file paths (e.g. testing rules with\r\n * `file_globs: ['**\\/*.test.ts']`).\r\n *\r\n * Loads the corpus + tenant override directly + filters by category + tags\r\n * with the same any-of semantics as `QueryInput.categories` / `tags_any`.\r\n */\r\nexport function findRulesByMetadata(opts: FindRulesByMetadataOptions): KBLookupResult {\r\n // Defensive: require at least one filter so the prompt doesn't get every\r\n // rule in the corpus.\r\n if (opts.category === undefined && opts.tagsAny === undefined) {\r\n return {\r\n rules: [],\r\n error: 'findRulesByMetadata called without category or tagsAny \u2014 refusing to surface every rule',\r\n };\r\n }\r\n try {\r\n const bundled = loadBundledCorpus().rules;\r\n const override = loadTenantOverride().override;\r\n const merged = mergeCorpusWithOverride(bundled, override);\r\n\r\n const tagAnyFilter = opts.tagsAny !== undefined ? new Set<string>(opts.tagsAny) : null;\r\n const hits: QueryHit[] = [];\r\n for (const rule of merged.rules) {\r\n if (opts.category !== undefined && rule.category !== opts.category) continue;\r\n if (tagAnyFilter !== null) {\r\n let tagHit = false;\r\n for (const t of rule.tags) {\r\n if (tagAnyFilter.has(t)) {\r\n tagHit = true;\r\n break;\r\n }\r\n }\r\n if (!tagHit) continue;\r\n }\r\n hits.push({\r\n rule,\r\n // No applies-context for metadata queries; report all matched=true so\r\n // downstream KBLookupResult consumers don't NPE on the field.\r\n reason: { stack_matched: true, change_type_matched: true, file_glob_matched: true },\r\n evidence: [], // No diff \u2192 no evidence matchers run.\r\n });\r\n }\r\n return { rules: hits, error: null };\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n process.stderr.write(`[vo-mcp] vo-arch-defaults KB unavailable: ${message}\\n`);\r\n return { rules: [], error: message };\r\n }\r\n}\r\n", "/**\r\n * Tool: `vo_check_hollow_test` \u2014 Phase 2 Lane D-1.\r\n *\r\n * Detects 'hollow' test patterns where a test passes without verifying\r\n * product behavior. Runs the consensus engine (gate `plan-review`,\r\n * majority-vote synthesizer, 3-model cheap panel per Lane B gate config)\r\n * to grade the supplied test source for hollow-ness.\r\n *\r\n * Behavior parallels `vo_check_assertion_strength` (static-analysis\r\n * ratchet) but uses LLM judgment for patterns the static analyzer can't\r\n * detect \u2014 e.g. mocks that return whatever the test expects, fixtures\r\n * substituting for real outputs, tests that exercise no real code path.\r\n *\r\n * Contract (mirrors `consensus-judgment.ts`):\r\n * 1. Cache lookup on canonicalized input. Hit \u2192 return cached envelope\r\n * with cache.hit=true, still emit event (replay).\r\n * 2. Engine.run() with a hollow-test detection prompt. Miss \u2192 cache\r\n * and return real verdict + emit enriched event.\r\n * 3. Engine unavailable / throws \u2192 fall back to `verdict: 'unimplemented'`\r\n * envelope with `degraded_mode: true` (transient errors aren't cached).\r\n */\r\nimport type {\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n HollowTestPayload,\r\n ConsensusCallEvent,\r\n} from '../types.js';\r\nimport type { QueryHit } from '@algosuite/vo-arch-defaults';\r\nimport {\r\n assertWithinByteCap,\r\n buildBaseEvent,\r\n bytesOf,\r\n invalidParams,\r\n jsonContent,\r\n toEventPerModelVerdicts,\r\n toEventSynthesizedVerdict,\r\n type ToolInputSchema,\r\n} from './common.js';\r\nimport {\r\n formatRulesForPrompt,\r\n prepareKBRules,\r\n} from './architecture-review-kb-prefilter.js';\r\nimport { findRulesByMetadata } from './kb-metadata-prefilter.js';\r\n\r\nexport const TOOL_NAME = 'vo_check_hollow_test';\r\n\r\n/** This tool routes to the Phase 2 `plan-review` gate (majority-vote, panel=3, cheap). */\r\nconst GATE_TYPE = 'plan-review' as const;\r\n\r\n/** 512 KB cap \u2014 same shape as check-assertion-strength: bounds a single test file. */\r\nconst MAX_SOURCE_BYTES = 512 * 1024;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n source: { type: 'string', description: 'Full test file body to analyze.' },\r\n file_path: { type: 'string', description: 'Optional path hint for nicer findings messages.' },\r\n },\r\n required: ['source'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Detects 'hollow' test patterns where a test passes without verifying product behavior \u2014 e.g. mocks that return whatever the test expects, assertions on local fixtures rather than real outputs, tests that exercise no real code path. Returns a consensus verdict (pass/fail/uncertain) with per-model reasoning. Complements vo_check_assertion_strength (static ratchet); this tool uses LLM judgment for patterns static analysis can't reach. Falls back to 'unimplemented' when no engine credentials are configured; the call is always logged.\";\r\n\r\ninterface ToolInput {\r\n readonly source: string;\r\n readonly file_path?: string;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['source'] !== 'string') return false;\r\n if (o['file_path'] !== undefined && typeof o['file_path'] !== 'string') return false;\r\n return true;\r\n}\r\n\r\n/**\r\n * Build the user prompt sent to each model. The system_prompt is supplied\r\n * by the engine's gate config; this function shapes the user-facing input.\r\n *\r\n * KB pre-filter (audit HIGH \u2014 generalized from architecture-review):\r\n * matched testing-category rules are inlined as deterministic context so\r\n * the panel reasons WITH the rule catalog instead of re-deriving it.\r\n */\r\nfunction buildPrompt(\r\n input: ToolInput,\r\n kbRules: ReadonlyArray<QueryHit>,\r\n kbTruncated: number,\r\n): string {\r\n const pathHint = input.file_path !== undefined ? `File: ${input.file_path}\\n` : '';\r\n const rulesBlock = formatRulesForPrompt(kbRules, kbTruncated, 'TESTING');\r\n return (\r\n `${pathHint}Analyze the following test source for HOLLOW patterns \u2014 tests that pass without verifying product truth. ` +\r\n `Hollow patterns include: assertions on locally-defined fixtures (not real product output), mocks ` +\r\n `that return the expected value verbatim, toBeDefined()/toBeTruthy() against literal values, ` +\r\n `tests that exercise NO real product code path, tautological self-comparisons (expect(x).toBe(x)).\\n\\n` +\r\n `Respond with verdict 'fail' if the test is HOLLOW (does not verify product truth), ` +\r\n `'pass' if the test is genuinely verifying product behavior, ` +\r\n `or 'uncertain' if the determination cannot be made from the source alone.${rulesBlock}\\n\\n` +\r\n `---TEST SOURCE---\\n${input.source}\\n---END---`\r\n );\r\n}\r\n\r\nexport async function handleCheckHollowTest(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(TOOL_NAME, 'invalid input. Required: { source: string }.');\r\n }\r\n assertWithinByteCap(TOOL_NAME, 'source', rawInput.source, MAX_SOURCE_BYTES);\r\n const key = deps.cache.keyFor(TOOL_NAME, rawInput);\r\n const excerpt = rawInput.source.slice(0, 300);\r\n const inputSizeBytes = bytesOf(rawInput.source);\r\n\r\n // Cache hit \u2014 return cached envelope verbatim, still emit event.\r\n const cached = deps.cache.get<ToolResultEnvelope<HollowTestPayload>>(key);\r\n if (cached !== null) {\r\n const replayEvent: ConsensusCallEvent = {\r\n ...buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n }),\r\n per_model_verdicts: cached.value.payload.per_model_verdicts,\r\n synthesized_verdict: cached.value.payload.synthesized_verdict ?? null,\r\n consensus_confidence: cached.value.payload.synthesized_verdict?.confidence ?? null,\r\n consensus_engine_version: cached.value.payload.engine_version ?? null,\r\n cache_hit: true,\r\n };\r\n deps.events.append(replayEvent);\r\n return jsonContent({ ...cached.value, cache: { hit: true, key } });\r\n }\r\n\r\n // KB pre-filter \u2014 pull testing-category rules from vo-arch-defaults.\r\n // findRulesByMetadata catches all errors and surfaces them via stderr +\r\n // the `error` field; engine still runs without rule context on failure.\r\n const kbResult = findRulesByMetadata({ category: 'testing' });\r\n const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);\r\n\r\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n });\r\n\r\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\r\n let engineThrew = false;\r\n try {\r\n engineResult = await deps.consensus.run({\r\n gate_type: GATE_TYPE,\r\n prompt: buildPrompt(rawInput, topRules, kbTruncated),\r\n ...(signal !== undefined ? { signal } : {}),\r\n });\r\n } catch (err) {\r\n engineThrew = true;\r\n const message = err instanceof Error ? err.message : String(err);\r\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\r\n }\r\n\r\n // 2026-05-25 audit HIGH \u2014 MCP cancellation. See consensus-judgment.ts\r\n // for the full rationale. Emit audit event then throw AbortError.\r\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\r\n deps.events.append({\r\n ...baseEvent,\r\n downstream_outcome: {\r\n status: 'cancelled',\r\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\r\n },\r\n });\r\n const e = new Error('Request cancelled by client (notifications/cancelled)');\r\n e.name = 'AbortError';\r\n throw e;\r\n }\r\n\r\n if (!engineResult.ok) {\r\n const payload: HollowTestPayload = {\r\n verdict: 'unimplemented',\r\n reason: engineResult.reason,\r\n per_model_verdicts: [],\r\n gate_type: GATE_TYPE,\r\n ...(engineThrew ? { degraded_mode: true } : {}),\r\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\r\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\r\n };\r\n const envelope: ToolResultEnvelope<HollowTestPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n // Do NOT cache !ok envelopes (HP2-1 hardening, parity with\r\n // vo_consensus_judgment). Caching unimplemented envelopes makes the\r\n // unwired\u2192wired transition invisible to the cache \u2014 every prompt the\r\n // operator already asked would short-circuit to 'unimplemented' even\r\n // after credentials are configured.\r\n deps.events.append(baseEvent);\r\n return jsonContent(envelope);\r\n }\r\n\r\n const perModelForEvent = toEventPerModelVerdicts(engineResult.per_model_verdicts);\r\n const synthForEvent = toEventSynthesizedVerdict(engineResult.synthesized_verdict);\r\n\r\n const enrichedEvent: ConsensusCallEvent = {\r\n ...baseEvent,\r\n per_model_verdicts: perModelForEvent,\r\n synthesized_verdict: synthForEvent,\r\n consensus_confidence: engineResult.synthesized_verdict.confidence,\r\n duration_ms: engineResult.duration_ms,\r\n consensus_engine_version: engineResult.engine_version,\r\n };\r\n\r\n const payload: HollowTestPayload = {\r\n verdict: engineResult.synthesized_verdict.verdict,\r\n reason:\r\n engineResult.synthesized_verdict.dissent_summary ??\r\n `panel agreed (${engineResult.per_model_verdicts.length} models, engine=${engineResult.engine_version})`,\r\n per_model_verdicts: perModelForEvent,\r\n synthesized_verdict: synthForEvent,\r\n engine_version: engineResult.engine_version,\r\n degraded: engineResult.degraded,\r\n gate_type: GATE_TYPE,\r\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\r\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\r\n };\r\n const envelope: ToolResultEnvelope<HollowTestPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n deps.cache.set(key, envelope);\r\n deps.events.append(enrichedEvent);\r\n return jsonContent(envelope);\r\n}\r\n", "/**\r\n * Tool: `vo_verify_answer` \u2014 Phase 2 Lane D-1.\r\n *\r\n * Compares an expected value against an observed value via consensus\r\n * fan-out \u2014 semantic equivalence rather than literal equality. The engine\r\n * judges whether `observed` matches `expected` despite formatting drift,\r\n * unit conversions, paraphrase, etc.\r\n *\r\n * Gate routing (Lane B):\r\n * - default \u2192 `mid-exec-verify` (weighted synthesizer, panel=3, cheap)\r\n * - when caller sets `deep: true` \u2192 `final-deep-verify` (deliberation\r\n * synthesizer, panel=4, expensive, max 2 rounds)\r\n *\r\n * Falls back to `verdict: 'unimplemented'` envelope when engine unavailable.\r\n */\r\nimport type {\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n VerifyAnswerPayload,\r\n ConsensusCallEvent,\r\n} from '../types.js';\r\nimport type { QueryHit } from '@algosuite/vo-arch-defaults';\r\nimport {\r\n assertWithinByteCap,\r\n buildBaseEvent,\r\n bytesOf,\r\n invalidParams,\r\n jsonContent,\r\n toEventPerModelVerdicts,\r\n toEventSynthesizedVerdict,\r\n type ToolInputSchema,\r\n} from './common.js';\r\nimport {\r\n formatRulesForPrompt,\r\n prepareKBRules,\r\n} from './architecture-review-kb-prefilter.js';\r\nimport { findRulesByMetadata } from './kb-metadata-prefilter.js';\r\n\r\nexport const TOOL_NAME = 'vo_verify_answer';\r\n\r\n/** Default gate \u2014 quick comparison. */\r\nconst SHALLOW_GATE = 'mid-exec-verify' as const;\r\n/** Deep gate \u2014 when caller passes `deep: true`. */\r\nconst DEEP_GATE = 'final-deep-verify' as const;\r\n\r\n/** 256 KB per side \u2014 well above any normal expected/observed but below the model context window. */\r\nconst MAX_VALUE_BYTES = 256 * 1024;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n expected: {\r\n description: 'The expected value (string, number, or structured JSON).',\r\n },\r\n observed: {\r\n description: 'The observed value to compare against expected.',\r\n },\r\n domain: {\r\n type: 'string',\r\n description: 'Domain hint for semantic comparison (e.g. tax, code, prose).',\r\n },\r\n deep: {\r\n type: 'boolean',\r\n description:\r\n 'When true, routes to the final-deep-verify gate (deliberation synthesizer, 4-model expensive panel, max 2 rounds). Default false \u2192 mid-exec-verify gate (weighted, 3-model cheap panel).',\r\n },\r\n },\r\n required: ['expected', 'observed'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n 'Compares an expected value against an observed value via consensus fan-out and returns whether they are semantically equivalent (handles formatting drift, unit conversions, paraphrase). Default routes to the mid-exec-verify gate (cheap 3-model panel); pass deep:true to escalate to final-deep-verify (4-model deliberation). Returns per-model verdicts and a synthesized pass/fail/uncertain. Falls back to \"unimplemented\" when engine credentials are absent; the call is always logged.';\r\n\r\ninterface ToolInput {\r\n readonly expected: unknown;\r\n readonly observed: unknown;\r\n readonly domain?: string;\r\n readonly deep?: boolean;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (!('expected' in o) || !('observed' in o)) return false;\r\n if (o['domain'] !== undefined && typeof o['domain'] !== 'string') return false;\r\n if (o['deep'] !== undefined && typeof o['deep'] !== 'boolean') return false;\r\n return true;\r\n}\r\n\r\nfunction safeStringify(v: unknown): string {\r\n try {\r\n return JSON.stringify(v);\r\n } catch {\r\n return String(v);\r\n }\r\n}\r\n\r\n/**\r\n * Build the user prompt \u2014 frames expected vs observed for semantic comparison.\r\n *\r\n * KB pre-filter (audit HIGH \u2014 generalized from architecture-review):\r\n * for DEEP-mode runs only, matched security-category rules are inlined\r\n * as deterministic context so the panel reasons WITH the source-grounded\r\n * + secrets-handling catalog. Shallow runs skip KB lookup to stay fast.\r\n */\r\nfunction buildPrompt(\r\n input: ToolInput,\r\n kbRules: ReadonlyArray<QueryHit>,\r\n kbTruncated: number,\r\n): string {\r\n const domain = input.domain !== undefined ? ` (domain: ${input.domain})` : '';\r\n const rulesBlock = formatRulesForPrompt(kbRules, kbTruncated, 'SECURITY');\r\n return (\r\n `Judge whether the OBSERVED value is semantically equivalent to the EXPECTED value${domain}. ` +\r\n `Treat formatting drift (whitespace, casing, currency symbols, unit notation) and paraphrase as ` +\r\n `equivalent; treat numerically different values, wrong units, or wrong factual content as NOT equivalent.\\n\\n` +\r\n `Respond with verdict 'pass' when equivalent, 'fail' when not equivalent, or 'uncertain' when the ` +\r\n `determination requires information not present in the inputs.${rulesBlock}\\n\\n` +\r\n `---EXPECTED---\\n${safeStringify(input.expected)}\\n---OBSERVED---\\n${safeStringify(input.observed)}\\n---END---`\r\n );\r\n}\r\n\r\nexport async function handleVerifyAnswer(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(TOOL_NAME, 'invalid input. Required: { expected, observed }.');\r\n }\r\n // Per-side cap on the stringified value. Stringify once and reuse for the\r\n // size check + the cache excerpt below; safeStringify is identity for\r\n // strings so the byte count matches what the engine sees.\r\n const expectedStr = safeStringify(rawInput.expected);\r\n const observedStr = safeStringify(rawInput.observed);\r\n assertWithinByteCap(TOOL_NAME, 'expected', expectedStr, MAX_VALUE_BYTES);\r\n assertWithinByteCap(TOOL_NAME, 'observed', observedStr, MAX_VALUE_BYTES);\r\n const gateType = rawInput.deep === true ? DEEP_GATE : SHALLOW_GATE;\r\n\r\n // Cache key includes the resolved gate type so deep and shallow runs don't\r\n // collide on the same expected/observed pair.\r\n const cacheInput = {\r\n expected: rawInput.expected,\r\n observed: rawInput.observed,\r\n ...(rawInput.domain !== undefined ? { domain: rawInput.domain } : {}),\r\n gate_type: gateType,\r\n };\r\n const key = deps.cache.keyFor(TOOL_NAME, cacheInput);\r\n const excerpt = safeStringify(rawInput).slice(0, 300);\r\n const inputSizeBytes = bytesOf(expectedStr) + bytesOf(observedStr);\r\n\r\n // KB pre-filter (deep mode only). Shallow mid-exec-verify stays fast +\r\n // skips KB lookup; deep final-deep-verify pulls security-category rules\r\n // (no-hardcoded-secrets, no-env-var-leak, source-grounded-fetch-hardening,\r\n // no-pii-in-logs) so the panel has the deterministic safety catalog.\r\n const isDeep = gateType === DEEP_GATE;\r\n const kbResult = isDeep\r\n ? findRulesByMetadata({ category: 'security' })\r\n : { rules: [] as ReadonlyArray<QueryHit>, error: null };\r\n const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);\r\n\r\n // Cache hit replay.\r\n const cached = deps.cache.get<ToolResultEnvelope<VerifyAnswerPayload>>(key);\r\n if (cached !== null) {\r\n const replayEvent: ConsensusCallEvent = {\r\n ...buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n }),\r\n per_model_verdicts: cached.value.payload.per_model_verdicts,\r\n synthesized_verdict: cached.value.payload.synthesized_verdict ?? null,\r\n consensus_confidence: cached.value.payload.synthesized_verdict?.confidence ?? null,\r\n consensus_engine_version: cached.value.payload.engine_version ?? null,\r\n cache_hit: true,\r\n };\r\n deps.events.append(replayEvent);\r\n return jsonContent({ ...cached.value, cache: { hit: true, key } });\r\n }\r\n\r\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n });\r\n\r\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\r\n let engineThrew = false;\r\n try {\r\n engineResult = await deps.consensus.run({\r\n gate_type: gateType,\r\n prompt: buildPrompt(rawInput, topRules, kbTruncated),\r\n ...(signal !== undefined ? { signal } : {}),\r\n });\r\n } catch (err) {\r\n engineThrew = true;\r\n const message = err instanceof Error ? err.message : String(err);\r\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\r\n }\r\n\r\n // 2026-05-25 audit HIGH \u2014 MCP cancellation. See consensus-judgment.ts.\r\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\r\n deps.events.append({\r\n ...baseEvent,\r\n downstream_outcome: {\r\n status: 'cancelled',\r\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\r\n },\r\n });\r\n const e = new Error('Request cancelled by client (notifications/cancelled)');\r\n e.name = 'AbortError';\r\n throw e;\r\n }\r\n\r\n if (!engineResult.ok) {\r\n const payload: VerifyAnswerPayload = {\r\n verdict: 'unimplemented',\r\n reason: engineResult.reason,\r\n per_model_verdicts: [],\r\n gate_type: gateType,\r\n ...(engineThrew ? { degraded_mode: true } : {}),\r\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\r\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\r\n };\r\n const envelope: ToolResultEnvelope<VerifyAnswerPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n // Do NOT cache !ok envelopes (HP2-1 hardening). See vo_consensus_judgment\r\n // for rationale: caching unwired envelopes makes the unwired\u2192wired\r\n // transition invisible to the cache.\r\n deps.events.append(baseEvent);\r\n return jsonContent(envelope);\r\n }\r\n\r\n const perModelForEvent = toEventPerModelVerdicts(engineResult.per_model_verdicts);\r\n const synthForEvent = toEventSynthesizedVerdict(engineResult.synthesized_verdict);\r\n const enrichedEvent: ConsensusCallEvent = {\r\n ...baseEvent,\r\n per_model_verdicts: perModelForEvent,\r\n synthesized_verdict: synthForEvent,\r\n consensus_confidence: engineResult.synthesized_verdict.confidence,\r\n duration_ms: engineResult.duration_ms,\r\n consensus_engine_version: engineResult.engine_version,\r\n };\r\n\r\n const payload: VerifyAnswerPayload = {\r\n verdict: engineResult.synthesized_verdict.verdict,\r\n reason:\r\n engineResult.synthesized_verdict.dissent_summary ??\r\n `panel agreed (${engineResult.per_model_verdicts.length} models, engine=${engineResult.engine_version})`,\r\n per_model_verdicts: perModelForEvent,\r\n synthesized_verdict: synthForEvent,\r\n engine_version: engineResult.engine_version,\r\n degraded: engineResult.degraded,\r\n gate_type: gateType,\r\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\r\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\r\n };\r\n const envelope: ToolResultEnvelope<VerifyAnswerPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n deps.cache.set(key, envelope);\r\n deps.events.append(enrichedEvent);\r\n return jsonContent(envelope);\r\n}\r\n", "/**\r\n * Accepted `gate_type` values for the consensus protocol surface.\r\n *\r\n * These are part of vo-mcp's OPEN protocol surface \u2014 the public contract a\r\n * caller sees in the `vo_consensus_judgment` tool's `gate_type` enum. They are\r\n * intentionally defined HERE (the open package) rather than imported from the\r\n * closed `@algosuite/consensus-engine`, so the open MCP shell carries no\r\n * dependency on the closed engine. (Was imported from the engine pre-extraction;\r\n * see docs/oss-publication-readiness.md.)\r\n *\r\n * The values MUST stay in lock-step with the engine's gate registry\r\n * (`packages/consensus-engine/src/gates/index.ts`). A future shared\r\n * `@algosuite/consensus-protocol` package could host this single source and be\r\n * imported by both; until then it is a small, stable, duplicated list.\r\n */\r\n\r\n/** Phase-1 legacy gate names (snake_case). */\r\nexport const LEGACY_GATE_TYPES = [\r\n 'test_assertion',\r\n 'architecture_review',\r\n 'factual_grounding',\r\n 'verify_answer',\r\n 'other',\r\n] as const;\r\nexport type LegacyGateType = (typeof LEGACY_GATE_TYPES)[number];\r\n\r\n/** Phase-2 gate names (kebab-case) \u2014 the engine's recognized cost-tiered gates. */\r\nexport const KNOWN_GATE_TYPES = [\r\n 'plan-review',\r\n 'mid-exec-verify',\r\n 'final-deep-verify',\r\n 'architecture-review',\r\n] as const;\r\nexport type Phase2GateType = (typeof KNOWN_GATE_TYPES)[number];\r\n\r\n/** Every gate name the engine recognizes (legacy + Phase 2). */\r\nexport const ALL_RECOGNIZED_GATE_TYPES = [\r\n ...LEGACY_GATE_TYPES,\r\n ...KNOWN_GATE_TYPES,\r\n] as const;\r\nexport type RecognizedGateType = LegacyGateType | Phase2GateType;\r\n\r\nexport function isRecognizedGateType(gateType: string): gateType is RecognizedGateType {\r\n return (ALL_RECOGNIZED_GATE_TYPES as readonly string[]).includes(gateType);\r\n}\r\n", "/**\r\n * Source-grounded input plumbing for the `vo_consensus_judgment` tool.\r\n *\r\n * Extracted from `consensus-judgment.ts` to keep that file under its size cap\r\n * while lighting up the previously-dormant Tier-4 path. Owns:\r\n * - the `source_urls` / `source_grounded` input shape + type guards, and\r\n * - `normalizeSourceGrounded()`, the pure mapping from the flat tool input to\r\n * the engine request fields (`source_urls`, `source_grounded`) plus the\r\n * cache-key fragments that must distinguish a sourceless from a\r\n * source-grounded call over the same prompt.\r\n *\r\n * Everything here is PURE (no I/O) and keeps the sourceless path inert: when no\r\n * non-empty source URL is supplied, `active` is false and the caller forwards\r\n * nothing new to the engine \u2014 byte-for-byte the old behaviour.\r\n */\r\nimport type { ConsensusSourceGroundedConfig } from '../consensus/client.js';\r\n\r\n/** Flat `source_grounded` toggle block accepted on the tool input. */\r\nexport interface ToolSourceGrounded {\r\n readonly enable_citation_grading?: boolean;\r\n readonly escalate_below?: number;\r\n}\r\n\r\nexport function isStringArray(v: unknown): v is readonly string[] {\r\n return Array.isArray(v) && v.every((e) => typeof e === 'string');\r\n}\r\n\r\nexport function isToolSourceGrounded(v: unknown): v is ToolSourceGrounded {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (\r\n o['enable_citation_grading'] !== undefined &&\r\n typeof o['enable_citation_grading'] !== 'boolean'\r\n ) {\r\n return false;\r\n }\r\n if (o['escalate_below'] !== undefined && typeof o['escalate_below'] !== 'number') return false;\r\n return true;\r\n}\r\n\r\n/** Resolved, normalized source-grounded request fields. */\r\nexport interface NormalizedSourceGrounded {\r\n /** True only when at least one non-empty source URL was supplied. */\r\n readonly active: boolean;\r\n /** Trimmed, non-empty source URLs (empty array when inactive). */\r\n readonly sourceUrls: readonly string[];\r\n /** True when sources are active AND the caller opted into citation grading. */\r\n readonly gradingEnabled: boolean;\r\n /**\r\n * The `source_grounded` config to forward to the engine when grading is on.\r\n * Carries NO classifier \u2014 the engine-client supplies the default classifier\r\n * built from a live panel adapter. `undefined` when grading is off.\r\n */\r\n readonly config?: ConsensusSourceGroundedConfig;\r\n /**\r\n * Extra cache-key fragments. Folded into the cache input so a sourceless call\r\n * and a source-grounded call over the same prompt never collide on one entry.\r\n */\r\n readonly cacheFragments: Readonly<Record<string, unknown>>;\r\n}\r\n\r\n/**\r\n * Pure normalizer. Given the raw (already type-validated) source fields,\r\n * produce the engine request fields + cache fragments.\r\n */\r\nexport function normalizeSourceGrounded(input: {\r\n readonly source_urls?: readonly string[];\r\n readonly source_grounded?: ToolSourceGrounded;\r\n}): NormalizedSourceGrounded {\r\n const sourceUrls =\r\n input.source_urls !== undefined\r\n ? input.source_urls.filter((u) => u.trim().length > 0)\r\n : [];\r\n const active = sourceUrls.length > 0;\r\n const gradingEnabled = active && input.source_grounded?.enable_citation_grading === true;\r\n const escalateBelow = input.source_grounded?.escalate_below;\r\n\r\n const config: ConsensusSourceGroundedConfig | undefined = gradingEnabled\r\n ? {\r\n citation_grader: {\r\n enabled: true,\r\n ...(escalateBelow !== undefined ? { escalate_below: escalateBelow } : {}),\r\n },\r\n }\r\n : undefined;\r\n\r\n const cacheFragments: Record<string, unknown> = {};\r\n if (active) cacheFragments['source_urls'] = sourceUrls;\r\n if (gradingEnabled) {\r\n cacheFragments['citation_grading'] = true;\r\n if (escalateBelow !== undefined) cacheFragments['escalate_below'] = escalateBelow;\r\n }\r\n\r\n return {\r\n active,\r\n sourceUrls,\r\n gradingEnabled,\r\n ...(config !== undefined ? { config } : {}),\r\n cacheFragments,\r\n };\r\n}\r\n", "/**\r\n * Tool: `vo_consensus_judgment`\r\n *\r\n * Phase 2 Lane C status:\r\n * - Engine-backed when wired (Lane A's `tryCreateEngineConsensusClientFromEnv`).\r\n * - Phase 2 gate types (`plan-review`, `mid-exec-verify`, `final-deep-verify`,\r\n * `architecture-review`) accepted and forwarded to the engine via the\r\n * gate_type field. Default = `plan-review` when caller omits it.\r\n * - Cache integration uses the existing `deps.cache` (tool response cache \u2014\r\n * V1 launch gate #8). On a hit the cached envelope is returned with\r\n * `cache.hit = true`. The engine itself is not re-invoked.\r\n * - Engine-throw fallback: when the engine client throws unexpectedly OR\r\n * all models err out, the tool returns the unimplemented envelope with\r\n * `degraded_mode: true` so cross-vendor MCP clients always see a\r\n * stable shape.\r\n *\r\n * Event-emit contract (unchanged): exactly one event line per call. The\r\n * cache-hit path also emits an event (with `cache_hit` shape preserved\r\n * via the input_hash and the engine_version/etc populated from the\r\n * cached payload). This keeps the V1 gate #7 dataset moat intact for\r\n * cache replays.\r\n */\r\nimport type {\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n ConsensusJudgmentPayload,\r\n ConsensusCallEvent,\r\n} from '../types.js';\r\nimport {\r\n assertWithinByteCap,\r\n buildBaseEvent,\r\n bytesOf,\r\n invalidParams,\r\n jsonContent,\r\n toEventPerModelVerdicts,\r\n toEventSynthesizedVerdict,\r\n type ToolInputSchema,\r\n} from './common.js';\r\n// The accepted gate_type set is the OPEN protocol surface \u2014 defined in vo-mcp\r\n// (gate-types.ts), NOT imported from the closed @algosuite/consensus-engine, so\r\n// the open MCP shell carries no dependency on the closed engine. The list mirrors\r\n// the engine's gate registry (see gate-types.ts). (Was imported from the engine\r\n// pre-extraction \u2014 see docs/oss-publication-readiness.md.)\r\nimport {\r\n ALL_RECOGNIZED_GATE_TYPES,\r\n type RecognizedGateType,\r\n} from '../consensus/gate-types.js';\r\nimport {\r\n isStringArray,\r\n isToolSourceGrounded,\r\n normalizeSourceGrounded,\r\n type ToolSourceGrounded,\r\n} from './consensus-judgment-source-grounded.js';\r\n\r\nexport const TOOL_NAME = 'vo_consensus_judgment';\r\n\r\n/** 64 KB prompt cap \u2014 keeps a 4-model fan-out under ~256 KB total. */\r\nconst MAX_PROMPT_BYTES = 64 * 1024;\r\n/** 256 KB context cap \u2014 larger than prompt because context is stringified JSON. */\r\nconst MAX_CONTEXT_BYTES = 256 * 1024;\r\n\r\n/**\r\n * Accepted gate_type values. Sourced from `@algosuite/consensus-engine`'s\r\n * `ALL_RECOGNIZED_GATE_TYPES` constant (PR-S 2026-05-25) \u2014 Phase 1 legacy\r\n * names + Phase 2 kebab-case names. Adding a new gate type now happens in\r\n * ONE place (the engine's gates module) instead of two.\r\n */\r\nconst ALL_GATE_TYPES = ALL_RECOGNIZED_GATE_TYPES;\r\ntype AcceptedGateType = RecognizedGateType;\r\n\r\nconst DEFAULT_GATE_TYPE: AcceptedGateType = 'plan-review';\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n prompt: {\r\n type: 'string',\r\n description: 'The judgment prompt \u2014 what you want multiple models to assess.',\r\n },\r\n gate_type: {\r\n type: 'string',\r\n enum: [...ALL_GATE_TYPES],\r\n description:\r\n 'Which gate this judgment serves. Phase 2 kebab-case names (plan-review, mid-exec-verify, final-deep-verify, architecture-review) drive the engine\u2019s synthesizer choice. Phase 1 underscore names back-compat to strict-consensus. Default: plan-review.',\r\n },\r\n context: {\r\n type: 'object',\r\n description: 'Tool-specific context (e.g. source file, diff, observed value).',\r\n additionalProperties: true,\r\n },\r\n source_urls: {\r\n type: 'array',\r\n items: { type: 'string' },\r\n description:\r\n 'Authoritative source URLs to ground the judgment against (Tier-4). When NON-EMPTY, the engine routes to its source-grounded path: it fetches the sources, flags low-confidence retrievals (surfaced as low_confidence_sources), and \u2014 when source_grounded.enable_citation_grading is true \u2014 grades each claim against the cited source text. When absent/empty the call uses the unchanged sourceless consensus path.',\r\n },\r\n source_grounded: {\r\n type: 'object',\r\n description:\r\n 'Optional source-grounded behaviour toggles. Only meaningful when source_urls is non-empty.',\r\n properties: {\r\n enable_citation_grading: {\r\n type: 'boolean',\r\n description:\r\n 'Opt IN to deterministic citation grading (default OFF). A failing grade raises an escalation signal, so this is behaviour-changing. When true, a real claim classifier (a single cheap model call) grades each claim against the cited sources.',\r\n },\r\n escalate_below: {\r\n type: 'number',\r\n description:\r\n 'Uncertain-claim escalation floor [0..1]; an uncertain claim with confidence below this triggers escalation. Engine default 0.85 when omitted.',\r\n },\r\n },\r\n additionalProperties: false,\r\n },\r\n },\r\n required: ['prompt'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Submit a prompt to multiple LLMs and return a synthesized consensus verdict with per-model verdicts visible. Use for high-stakes judgment calls where a single model's verdict is too risky (architecture reviews, weak-vs-strong-assertion calls, factual grounding against authoritative sources). Returns a real synthesized verdict when the closed consensus engine is wired in with credentials; falls back to 'unimplemented' otherwise. The call IS always logged for dataset and unit-economics audit (V1 launch gate #7).\";\r\n\r\ninterface ToolInput {\r\n readonly prompt: string;\r\n readonly gate_type?: string;\r\n readonly context?: Record<string, unknown>;\r\n readonly source_urls?: readonly string[];\r\n readonly source_grounded?: ToolSourceGrounded;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['prompt'] !== 'string') return false;\r\n if (o['gate_type'] !== undefined && typeof o['gate_type'] !== 'string') return false;\r\n if (o['source_urls'] !== undefined && !isStringArray(o['source_urls'])) return false;\r\n if (o['source_grounded'] !== undefined && !isToolSourceGrounded(o['source_grounded'])) return false;\r\n return true;\r\n}\r\n\r\nfunction isAcceptedGateType(v: string): v is AcceptedGateType {\r\n return (ALL_GATE_TYPES as readonly string[]).includes(v);\r\n}\r\n\r\nexport async function handleConsensusJudgment(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n /**\r\n * Cancellation signal threaded from `server.ts` via the MCP SDK's\r\n * `RequestHandlerExtra.signal`. Forwarded to the consensus engine call\r\n * so per-model API fetches abort when the client cancels.\r\n * 2026-05-25 audit HIGH.\r\n */\r\n signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Required: { prompt: string, gate_type?: string }.',\r\n );\r\n }\r\n assertWithinByteCap(TOOL_NAME, 'prompt', rawInput.prompt, MAX_PROMPT_BYTES);\r\n let contextStrForSize = '';\r\n if (rawInput.context !== undefined) {\r\n try {\r\n contextStrForSize = JSON.stringify(rawInput.context);\r\n } catch {\r\n // Non-serializable context (cycles, BigInt, etc.) \u2014 reject early with a clear message.\r\n throw invalidParams(TOOL_NAME, \"input field 'context' is not JSON-serializable.\");\r\n }\r\n assertWithinByteCap(TOOL_NAME, 'context', contextStrForSize, MAX_CONTEXT_BYTES);\r\n }\r\n const rawGate = rawInput.gate_type ?? DEFAULT_GATE_TYPE;\r\n const gateType: AcceptedGateType = isAcceptedGateType(rawGate) ? rawGate : DEFAULT_GATE_TYPE;\r\n const inputSizeBytes = bytesOf(rawInput.prompt) + bytesOf(contextStrForSize);\r\n\r\n // Source-grounded resolution. Sources are only \"active\" when non-empty;\r\n // an empty/absent array leaves the call on the unchanged sourceless path\r\n // (no source_urls / source_grounded forwarded \u2192 engine-client never builds\r\n // a classifier, exactly as before). Citation grading is OFF unless the\r\n // caller opted in via source_grounded.enable_citation_grading.\r\n const sg = normalizeSourceGrounded({\r\n ...(rawInput.source_urls !== undefined ? { source_urls: rawInput.source_urls } : {}),\r\n ...(rawInput.source_grounded !== undefined ? { source_grounded: rawInput.source_grounded } : {}),\r\n });\r\n\r\n // Cache key includes the resolved gate type so different gate routing\r\n // doesn't collide on the same prompt. Source URLs + grading flag are also\r\n // folded in (via sg.cacheFragments) so a sourceless call and a\r\n // source-grounded call over the same prompt do not collide on one entry.\r\n const cacheInput = {\r\n prompt: rawInput.prompt,\r\n gate_type: gateType,\r\n ...(rawInput.context !== undefined ? { context: rawInput.context } : {}),\r\n ...sg.cacheFragments,\r\n };\r\n const key = deps.cache.keyFor(TOOL_NAME, cacheInput);\r\n const excerpt = rawInput.prompt.slice(0, 300);\r\n\r\n // Cache hit short-circuit \u2014 return the cached envelope verbatim (with\r\n // `cache.hit: true`) and still emit an event so the gate #7 dataset\r\n // records the replay. Engine is NOT re-invoked.\r\n const cached = deps.cache.get<ToolResultEnvelope<ConsensusJudgmentPayload>>(key);\r\n if (cached !== null) {\r\n const replayEvent: ConsensusCallEvent = buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n });\r\n // Carry the cached payload's verdict shape into the replay event so\r\n // downstream consumers see consistent verdicts on replays.\r\n const enrichedReplay: ConsensusCallEvent = {\r\n ...replayEvent,\r\n per_model_verdicts: cached.value.payload.per_model_verdicts,\r\n synthesized_verdict: cached.value.payload.synthesized_verdict ?? null,\r\n consensus_confidence: cached.value.payload.synthesized_verdict?.confidence ?? null,\r\n consensus_engine_version: cached.value.payload.engine_version ?? null,\r\n cache_hit: true,\r\n };\r\n deps.events.append(enrichedReplay);\r\n const replayEnvelope: ToolResultEnvelope<ConsensusJudgmentPayload> = {\r\n ...cached.value,\r\n cache: { hit: true, key },\r\n };\r\n return jsonContent(replayEnvelope);\r\n }\r\n\r\n // Run the engine. The engine-client never throws by contract \u2014 but we\r\n // defensively wrap in case a custom client subclass does, and degrade\r\n // gracefully to the unimplemented envelope with degraded_mode=true.\r\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n });\r\n\r\n // Source-grounded forwarding. When sources are present but grading is OFF,\r\n // we still forward source_urls (so retrieval-confidence flagging activates)\r\n // but no source_grounded config \u2014 the engine-client then never builds a\r\n // classifier. When grading is ON, `sg.config` carries `citation_grader:\r\n // { enabled: true }` with NO classifier; the engine-client supplies the\r\n // default classifier built from a live panel adapter (the tool has no model\r\n // handle of its own).\r\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\r\n let engineThrew = false;\r\n try {\r\n engineResult = await deps.consensus.run({\r\n gate_type: gateType,\r\n prompt: rawInput.prompt,\r\n ...(rawInput.context !== undefined ? { caller_context: rawInput.context } : {}),\r\n ...(signal !== undefined ? { signal } : {}),\r\n ...(sg.active ? { source_urls: sg.sourceUrls } : {}),\r\n ...(sg.config !== undefined ? { source_grounded: sg.config } : {}),\r\n });\r\n } catch (err) {\r\n engineThrew = true;\r\n const message = err instanceof Error ? err.message : String(err);\r\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\r\n }\r\n\r\n // Cancellation short-circuit (2026-05-25 audit HIGH). When the engine\r\n // returns `reason: 'cancelled'` it means the SDK fired `extra.signal`\r\n // mid-call. Emit ONE event with `downstream_outcome.status='cancelled'`\r\n // so the audit trail (gate #7) is complete, then re-throw so the\r\n // request-handler error path returns a JSON-RPC error to the (already\r\n // disinterested) client. We do NOT cache cancelled envelopes.\r\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\r\n deps.events.append({\r\n ...baseEvent,\r\n downstream_outcome: {\r\n status: 'cancelled',\r\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\r\n },\r\n });\r\n const e = new Error('Request cancelled by client (notifications/cancelled)');\r\n e.name = 'AbortError';\r\n throw e;\r\n }\r\n\r\n if (!engineResult.ok) {\r\n // Engine unavailable (no credentials, panel-too-small, or throw fallback).\r\n const payload: ConsensusJudgmentPayload = {\r\n verdict: 'unimplemented',\r\n reason: engineResult.reason,\r\n per_model_verdicts: [],\r\n gate_type: gateType,\r\n ...(engineThrew ? { degraded_mode: true } : {}),\r\n };\r\n const envelope: ToolResultEnvelope<ConsensusJudgmentPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n // Do NOT cache !ok envelopes (hardening 2026-05-22 \u2014 finding HP2-1).\r\n //\r\n // Original heuristic was: cache stable failures (no credentials), skip\r\n // transient throws. Problem: BOTH classes are operationally transient\r\n // from the operator's perspective. \"No credentials\" gets resolved by\r\n // setting env vars + restarting the MCP server. If unimplemented\r\n // envelopes were cached on disk (sqlite-backed cache), the operator's\r\n // env-var change would have no effect \u2014 `cache.hit: true` would short-\r\n // circuit before the engine retry on every previously-asked prompt.\r\n //\r\n // Concretely: the cache key includes prompt + gate_type + context but\r\n // NOT the engine availability state, so transition unwired\u2192wired is\r\n // invisible to the cache. Solution: never persist failure envelopes;\r\n // every !ok call re-tries the engine on the next invocation.\r\n //\r\n // Trade-off: tiny perf cost (engine retried on every \"no credentials\"\r\n // call) for correctness when wiring is added. Same applies to\r\n // engine-threw transient errors. `engineThrew` remains in scope as the\r\n // `degraded_mode` flag in the response payload (see line ~205).\r\n deps.events.append(baseEvent);\r\n return jsonContent(envelope);\r\n }\r\n\r\n // Engine ran \u2014 populate the rich payload, cache it, emit enriched event.\r\n const perModelForEvent = toEventPerModelVerdicts(engineResult.per_model_verdicts);\r\n const synthForEvent = toEventSynthesizedVerdict(engineResult.synthesized_verdict);\r\n\r\n const enrichedEvent: ConsensusCallEvent = {\r\n ...baseEvent,\r\n consensus_confidence: engineResult.synthesized_verdict.confidence,\r\n duration_ms: engineResult.duration_ms,\r\n consensus_engine_version: engineResult.engine_version,\r\n per_model_verdicts: perModelForEvent,\r\n synthesized_verdict: synthForEvent,\r\n };\r\n\r\n const payload: ConsensusJudgmentPayload = {\r\n verdict: engineResult.synthesized_verdict.verdict,\r\n reason:\r\n engineResult.synthesized_verdict.dissent_summary ??\r\n `panel agreed (${engineResult.per_model_verdicts.length} models, engine=${engineResult.engine_version})`,\r\n per_model_verdicts: perModelForEvent,\r\n synthesized_verdict: synthForEvent,\r\n engine_version: engineResult.engine_version,\r\n degraded: engineResult.degraded,\r\n gate_type: gateType,\r\n // \u2500\u2500\u2500 Consensus-engine feature outputs (additive; 2026-06-13) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\r\n // Feature 2 (calibrated-confidence) \u2014 ON by default; the engine attaches\r\n // calibrated_confidence + confidence_badge to the synthesized verdict on the\r\n // plain runConsensus path. Surface them at the top level so callers don't\r\n // have to know the engine's internal SynthesizedVerdict shape.\r\n ...(engineResult.synthesized_verdict.calibrated_confidence !== undefined\r\n ? { calibrated_confidence: engineResult.synthesized_verdict.calibrated_confidence }\r\n : {}),\r\n ...(engineResult.synthesized_verdict.confidence_badge !== undefined\r\n ? { confidence_badge: engineResult.synthesized_verdict.confidence_badge }\r\n : {}),\r\n // Feature 1 (agreement-gate) \u2014 fan-out diagnostics (present iff the gate ran).\r\n ...(engineResult.fan_out_diagnostics !== undefined\r\n ? { fan_out_diagnostics: engineResult.fan_out_diagnostics }\r\n : {}),\r\n // Source-grounded Tier-4 outputs (present iff the call was source-grounded).\r\n ...(engineResult.source_grounded === true ? { source_grounded: true } : {}),\r\n ...(engineResult.citation_grade !== undefined\r\n ? { citation_grade: engineResult.citation_grade }\r\n : {}),\r\n ...(engineResult.low_confidence_sources !== undefined\r\n ? { low_confidence_sources: engineResult.low_confidence_sources }\r\n : {}),\r\n // Escalation (from citation grade or human-tiebreak synthesizer).\r\n ...(engineResult.escalation_required !== undefined\r\n ? { escalation_required: engineResult.escalation_required }\r\n : {}),\r\n ...(engineResult.escalation_reason !== undefined\r\n ? { escalation_reason: engineResult.escalation_reason }\r\n : {}),\r\n };\r\n const envelope: ToolResultEnvelope<ConsensusJudgmentPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n\r\n // Cache successful envelopes \u2014 the next identical call short-circuits.\r\n deps.cache.set(key, envelope);\r\n deps.events.append(enrichedEvent);\r\n return jsonContent(envelope);\r\n}\r\n", "/**\r\n * Tool: `vo_architecture_review` \u2014 Phase 2 Lane D-1 + Gate #5 KB wire-up.\r\n *\r\n * Senior-architect review of a proposed diff against the project's\r\n * architectural defaults (naming, boundary discipline, error-handling\r\n * shape, test honesty). Runs the engine on the `architecture-review`\r\n * gate (Lane B: human-tiebreak synthesizer, panel=3, expensive).\r\n *\r\n * KB pre-filter (Gate #5 \u2014 vo-arch-defaults): before invoking the\r\n * consensus engine, query the architectural-defaults KB for rules\r\n * applicable to this diff's stack/files. Matched rules (and any\r\n * mechanical evidence hits) are included in the prompt as additional\r\n * context so the panel reasons WITH the deterministic-rule KB instead\r\n * of re-deriving its content from scratch. KB lookup failures are\r\n * caught and degrade silently \u2014 the engine still runs.\r\n *\r\n * The `human-tiebreak` synthesizer surfaces an ESCALATION SIGNAL when\r\n * panel members disagree \u2014 the verdict still resolves via internal\r\n * majority-vote, but the engine sets `escalation_required` + an\r\n * `escalation_reason`. This tool forwards both via an optional\r\n * `escalation` field on the envelope payload (additive \u2014 schema_version\r\n * stays at 1, fields are optional). Callers can route the escalation to\r\n * a human reviewer; absent escalation field means panel agreed.\r\n *\r\n * Falls back to `verdict: 'unimplemented'` envelope when engine is\r\n * unavailable, matching the consensus-judgment pattern.\r\n */\r\nimport type {\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n ArchitectureReviewPayload,\r\n ConsensusCallEvent,\r\n} from '../types.js';\r\nimport {\r\n assertWithinByteCap,\r\n buildBaseEvent,\r\n bytesOf,\r\n invalidParams,\r\n jsonContent,\r\n toEventPerModelVerdicts,\r\n toEventSynthesizedVerdict,\r\n type ToolInputSchema,\r\n} from './common.js';\r\nimport { sanitizeExcerpt } from '../logging/events-writer.js';\r\nimport type { QueryHit } from '@algosuite/vo-arch-defaults';\r\nimport {\r\n findRulesForDiff,\r\n formatRulesForPrompt,\r\n prepareKBRules,\r\n} from './architecture-review-kb-prefilter.js';\r\n\r\n// Re-export for backwards-compatible test imports.\r\nexport {\r\n findRulesForDiff,\r\n formatRulesForPrompt,\r\n prepareKBRules,\r\n} from './architecture-review-kb-prefilter.js';\r\nexport type { KBLookupResult } from './architecture-review-kb-prefilter.js';\r\n\r\nexport const TOOL_NAME = 'vo_architecture_review';\r\n\r\nconst GATE_TYPE = 'architecture-review' as const;\r\n\r\n/** 1 MB diff cap \u2014 typical PR diff is <100 KB; ceiling rejects whole-repo diffs. */\r\nconst MAX_DIFF_BYTES = 1024 * 1024;\r\n/** 16 KB summary cap \u2014 paragraph-scale text only. */\r\nconst MAX_SUMMARY_BYTES = 16 * 1024;\r\n\r\nconst ACCEPTED_CHANGE_TYPES = [\r\n 'refactor',\r\n 'new_feature',\r\n 'bug_fix',\r\n 'dep_update',\r\n 'config',\r\n 'docs',\r\n 'other',\r\n] as const;\r\ntype AcceptedChangeType = (typeof ACCEPTED_CHANGE_TYPES)[number];\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n diff: {\r\n type: 'string',\r\n description: 'Unified diff of the proposed change.',\r\n },\r\n change_type: {\r\n type: 'string',\r\n enum: [...ACCEPTED_CHANGE_TYPES],\r\n description: 'Classification of the change.',\r\n },\r\n summary: {\r\n type: 'string',\r\n description: 'One-paragraph summary of intent / motivation.',\r\n },\r\n },\r\n required: ['diff', 'change_type'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Senior-architect review of a proposed diff against the project's architectural defaults \u2014 naming conventions, boundary discipline, error-handling shape, test honesty. Runs the architecture-review gate (human-tiebreak synthesizer, expensive 3-model panel). When the panel disagrees, the response includes an `escalation` field recommending operator review (the verdict still resolves via majority-vote \u2014 the engine does NOT block). Falls back to 'unimplemented' when engine credentials are absent; the call is always logged.\";\r\n\r\ninterface ToolInput {\r\n readonly diff: string;\r\n readonly change_type: string;\r\n readonly summary?: string;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['diff'] !== 'string' || typeof o['change_type'] !== 'string') return false;\r\n if (o['summary'] !== undefined && typeof o['summary'] !== 'string') return false;\r\n return true;\r\n}\r\n\r\nfunction isAcceptedChangeType(v: string): v is AcceptedChangeType {\r\n return (ACCEPTED_CHANGE_TYPES as readonly string[]).includes(v);\r\n}\r\n\r\n/** Build the user prompt \u2014 frames the diff for senior-architect-style review. */\r\nfunction buildPrompt(\r\n input: ToolInput,\r\n changeType: AcceptedChangeType,\r\n rules: ReadonlyArray<QueryHit>,\r\n truncated: number,\r\n): string {\r\n const summary = input.summary !== undefined ? `Summary: ${input.summary}\\n` : '';\r\n const rulesBlock = formatRulesForPrompt(rules, truncated);\r\n return (\r\n `Senior architecture review (change type: ${changeType}).\\n${summary}` +\r\n `Review the following diff against architectural defaults: naming conventions, boundary discipline ` +\r\n `(no leaky abstractions across packages), error-handling shape (typed errors, no swallowed throws), ` +\r\n `test honesty (assertions verify product truth \u2014 not mocks-of-mocks).\\n\\n` +\r\n `Respond with verdict 'pass' if the change is sound, 'fail' if it introduces architectural ` +\r\n `regressions or breaks defaults, or 'uncertain' if context outside the diff is needed.${rulesBlock}\\n\\n` +\r\n `---DIFF---\\n${input.diff}\\n---END---`\r\n );\r\n}\r\n\r\nexport async function handleArchitectureReview(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Required: { diff: string, change_type: string }.',\r\n );\r\n }\r\n assertWithinByteCap(TOOL_NAME, 'diff', rawInput.diff, MAX_DIFF_BYTES);\r\n if (rawInput.summary !== undefined) {\r\n assertWithinByteCap(TOOL_NAME, 'summary', rawInput.summary, MAX_SUMMARY_BYTES);\r\n }\r\n // Normalize change_type \u2014 unknown values fall back to 'other'.\r\n const changeType: AcceptedChangeType = isAcceptedChangeType(rawInput.change_type)\r\n ? rawInput.change_type\r\n : 'other';\r\n\r\n const cacheInput = {\r\n diff: rawInput.diff,\r\n change_type: changeType,\r\n ...(rawInput.summary !== undefined ? { summary: rawInput.summary } : {}),\r\n gate_type: GATE_TYPE,\r\n };\r\n const key = deps.cache.keyFor(TOOL_NAME, cacheInput);\r\n const excerpt = rawInput.diff.slice(0, 300);\r\n const inputSizeBytes =\r\n bytesOf(rawInput.diff) + (rawInput.summary !== undefined ? bytesOf(rawInput.summary) : 0);\r\n\r\n // Cache hit replay.\r\n const cached = deps.cache.get<ToolResultEnvelope<ArchitectureReviewPayload>>(key);\r\n if (cached !== null) {\r\n const replayEvent: ConsensusCallEvent = {\r\n ...buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n }),\r\n per_model_verdicts: cached.value.payload.per_model_verdicts,\r\n synthesized_verdict: cached.value.payload.synthesized_verdict ?? null,\r\n consensus_confidence: cached.value.payload.synthesized_verdict?.confidence ?? null,\r\n consensus_engine_version: cached.value.payload.engine_version ?? null,\r\n cache_hit: true,\r\n };\r\n deps.events.append(replayEvent);\r\n return jsonContent({ ...cached.value, cache: { hit: true, key } });\r\n }\r\n\r\n // Gate #5 pre-filter \u2014 pull matching rules from the vo-arch-defaults KB.\r\n // findRulesForDiff catches KB errors but SURFACES them: stderr log fires\r\n // and `kbResult.error` is non-null so we can flag the envelope.\r\n // prepareKBRules sorts by severity + caps at MAX_KB_RULES so the prompt\r\n // stays bounded on wide-touch diffs.\r\n const kbResult = findRulesForDiff(rawInput.diff);\r\n const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);\r\n\r\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n });\r\n\r\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\r\n let engineThrew = false;\r\n try {\r\n engineResult = await deps.consensus.run({\r\n gate_type: GATE_TYPE,\r\n prompt: buildPrompt(rawInput, changeType, topRules, kbTruncated),\r\n ...(signal !== undefined ? { signal } : {}),\r\n });\r\n } catch (err) {\r\n engineThrew = true;\r\n const message = err instanceof Error ? err.message : String(err);\r\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\r\n }\r\n\r\n // 2026-05-25 audit HIGH \u2014 MCP cancellation. See consensus-judgment.ts.\r\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\r\n deps.events.append({\r\n ...baseEvent,\r\n downstream_outcome: {\r\n status: 'cancelled',\r\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\r\n },\r\n });\r\n const e = new Error('Request cancelled by client (notifications/cancelled)');\r\n e.name = 'AbortError';\r\n throw e;\r\n }\r\n\r\n if (!engineResult.ok) {\r\n const payload: ArchitectureReviewPayload = {\r\n verdict: 'unimplemented',\r\n reason: engineResult.reason,\r\n per_model_verdicts: [],\r\n gate_type: GATE_TYPE,\r\n ...(engineThrew ? { degraded_mode: true } : {}),\r\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\r\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\r\n };\r\n const envelope: ToolResultEnvelope<ArchitectureReviewPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n // Do NOT cache !ok envelopes (HP2-1 hardening). See vo_consensus_judgment\r\n // for rationale.\r\n deps.events.append(baseEvent);\r\n return jsonContent(envelope);\r\n }\r\n\r\n const perModelForEvent = toEventPerModelVerdicts(engineResult.per_model_verdicts);\r\n const synthForEvent = toEventSynthesizedVerdict(engineResult.synthesized_verdict);\r\n const enrichedEvent: ConsensusCallEvent = {\r\n ...baseEvent,\r\n per_model_verdicts: perModelForEvent,\r\n synthesized_verdict: synthForEvent,\r\n consensus_confidence: engineResult.synthesized_verdict.confidence,\r\n duration_ms: engineResult.duration_ms,\r\n consensus_engine_version: engineResult.engine_version,\r\n };\r\n\r\n // Escalation: surfaced when the human-tiebreak synthesizer raised the\r\n // signal (panel disagreement). Falls back to inferring from dissent_summary\r\n // if the engine variant didn't populate the dedicated field \u2014 keeps the\r\n // tool robust against engine versions that pre-date the explicit signal.\r\n const escalationRequired =\r\n engineResult.escalation_required === true ||\r\n (engineResult.escalation_required === undefined &&\r\n engineResult.synthesized_verdict.dissent_summary !== null);\r\n const escalationReason =\r\n engineResult.escalation_reason ?? engineResult.synthesized_verdict.dissent_summary ?? '';\r\n\r\n const payload: ArchitectureReviewPayload = {\r\n verdict: engineResult.synthesized_verdict.verdict,\r\n reason:\r\n engineResult.synthesized_verdict.dissent_summary ??\r\n `panel agreed (${engineResult.per_model_verdicts.length} models, engine=${engineResult.engine_version})`,\r\n per_model_verdicts: perModelForEvent,\r\n synthesized_verdict: synthForEvent,\r\n engine_version: engineResult.engine_version,\r\n degraded: engineResult.degraded,\r\n gate_type: GATE_TYPE,\r\n ...(escalationRequired\r\n ? {\r\n escalation: {\r\n required: true as const,\r\n reason: sanitizeExcerpt(\r\n escalationReason.length > 0\r\n ? escalationReason\r\n : 'panel disagreement \u2014 operator review recommended',\r\n ),\r\n },\r\n }\r\n : {}),\r\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\r\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\r\n };\r\n const envelope: ToolResultEnvelope<ArchitectureReviewPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n deps.cache.set(key, envelope);\r\n deps.events.append(enrichedEvent);\r\n return jsonContent(envelope);\r\n}\r\n", "// Zod-validated config loader for the vo-ratchets library.\r\n//\r\n// Two surfaces:\r\n// 1. `resolveConfig(user)` \u2014 apply defaults, validate, return the resolved\r\n// shape every detector consumes.\r\n// 2. `loadConfigFromFile(path)` \u2014 read a `vo-ratchets.config.json` (or\r\n// `.js`/`.mjs` for the future) and resolve it.\r\n//\r\n// The defaults intentionally enable every detector. CI-on-first-install is\r\n// supposed to fail loudly on hollow tests; opt-out is explicit.\r\n\r\nimport { readFile } from 'node:fs/promises';\r\nimport path from 'node:path';\r\nimport { z } from 'zod';\r\nimport type {\r\n RatchetId,\r\n ResolvedRatchetConfig,\r\n UserRatchetConfig,\r\n} from './types.js';\r\n\r\nconst RATCHET_IDS: readonly RatchetId[] = [\r\n 'assertion-strength',\r\n 'hollow-tests',\r\n 'verify-answer',\r\n 'fix-strength',\r\n 'qa-honesty',\r\n] as const;\r\n\r\nconst ratchetIdSchema = z.enum([\r\n 'assertion-strength',\r\n 'hollow-tests',\r\n 'verify-answer',\r\n 'fix-strength',\r\n 'qa-honesty',\r\n]);\r\n\r\nconst thresholdSchema = z.enum(['strict', 'medium', 'permissive']);\r\n\r\nconst userConfigSchema = z\r\n .object({\r\n // zod v4: z.record with ENUM keys validates exhaustively (every key\r\n // required); these are user OVERRIDE maps merged over DEFAULT_CONFIG,\r\n // so partial-by-design \u2014 z.partialRecord restores the v3 semantics.\r\n enabled: z.partialRecord(ratchetIdSchema, z.boolean()).optional(),\r\n thresholds: z.partialRecord(ratchetIdSchema, thresholdSchema).optional(),\r\n allowlist: z\r\n .object({\r\n paths: z.array(z.string()).optional(),\r\n rules: z.array(z.string()).optional(),\r\n })\r\n .optional(),\r\n reportOnly: z.boolean().optional(),\r\n paths: z.array(z.string()).optional(),\r\n })\r\n .strict();\r\n\r\nexport const DEFAULT_CONFIG: ResolvedRatchetConfig = {\r\n enabled: {\r\n 'assertion-strength': true,\r\n 'hollow-tests': true,\r\n 'verify-answer': true,\r\n 'fix-strength': true,\r\n 'qa-honesty': true,\r\n },\r\n thresholds: {\r\n 'assertion-strength': 'medium',\r\n 'hollow-tests': 'medium',\r\n 'verify-answer': 'medium',\r\n 'fix-strength': 'medium',\r\n 'qa-honesty': 'medium',\r\n },\r\n allowlist: {\r\n paths: [],\r\n rules: [],\r\n },\r\n reportOnly: false,\r\n paths: [],\r\n};\r\n\r\n/**\r\n * Validate a user-supplied config object and merge it with defaults.\r\n * Throws a `ZodError` on shape violations \u2014 callers should let it propagate\r\n * so the operator sees the validation message.\r\n */\r\nexport function resolveConfig(\r\n userConfig: UserRatchetConfig | undefined,\r\n): ResolvedRatchetConfig {\r\n const parsed = userConfigSchema.parse(userConfig ?? {});\r\n const enabled: Record<RatchetId, boolean> = {\r\n ...DEFAULT_CONFIG.enabled,\r\n ...(parsed.enabled ?? {}),\r\n };\r\n const thresholds: Record<RatchetId, ResolvedRatchetConfig['thresholds'][RatchetId]> = {\r\n ...DEFAULT_CONFIG.thresholds,\r\n ...(parsed.thresholds ?? {}),\r\n };\r\n const allowlist = {\r\n paths: parsed.allowlist?.paths ?? [],\r\n rules: parsed.allowlist?.rules ?? [],\r\n };\r\n return {\r\n enabled,\r\n thresholds,\r\n allowlist,\r\n reportOnly: parsed.reportOnly ?? DEFAULT_CONFIG.reportOnly,\r\n paths: parsed.paths ?? DEFAULT_CONFIG.paths,\r\n };\r\n}\r\n\r\n/**\r\n * Read a JSON config from disk and resolve it. Missing file is not an error\r\n * \u2014 caller decides how to handle (the CLI treats missing-file as \"use\r\n * defaults\", explicit `--config` flag promotes it to an error).\r\n */\r\nexport async function loadConfigFromFile(\r\n filePath: string,\r\n): Promise<ResolvedRatchetConfig> {\r\n const text = await readFile(filePath, 'utf8');\r\n const parsed: unknown = JSON.parse(text);\r\n return resolveConfig(parsed as UserRatchetConfig);\r\n}\r\n\r\n/**\r\n * Resolve `only=<ratchet>` semantics. When `only` is set, every other\r\n * detector is force-disabled in the returned config; the `only` detector\r\n * is forced enabled. Used by the CLI's `--only` flag and the `runRatchets`\r\n * programmatic API.\r\n */\r\nexport function applyOnlyFilter(\r\n config: ResolvedRatchetConfig,\r\n only: RatchetId | undefined,\r\n): ResolvedRatchetConfig {\r\n if (!only) return config;\r\n const enabled: Record<RatchetId, boolean> = {\r\n 'assertion-strength': false,\r\n 'hollow-tests': false,\r\n 'verify-answer': false,\r\n 'fix-strength': false,\r\n 'qa-honesty': false,\r\n };\r\n enabled[only] = true;\r\n return { ...config, enabled };\r\n}\r\n\r\n/** Resolve a path-like config value against the run's cwd. */\r\nexport function resolvePathAgainst(cwd: string, target: string): string {\r\n return path.isAbsolute(target) ? target : path.resolve(cwd, target);\r\n}\r\n\r\nexport const __test = {\r\n RATCHET_IDS,\r\n userConfigSchema,\r\n};\r\n", "// Generic project file walker.\n//\n// Detectors share this: walk the tree, skip the obvious junk directories,\n// return forward-slash-normalized relative paths so the resulting findings\n// match across Windows and POSIX consumers (the cloud control plane runs on\n// Linux; the desktop MCP runs on Windows).\n\nimport { readdir, readFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nconst SKIP_DIRS = new Set<string>([\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n 'out',\n 'lib',\n '.next',\n '.nuxt',\n '.svelte-kit',\n '.turbo',\n 'coverage',\n '.coverage',\n '.cache',\n '.parcel-cache',\n '.vscode',\n '.idea',\n '.claude',\n '.agent-worktrees',\n 'test-results',\n]);\n\n/** Normalize a path to forward slashes \u2014 Windows-safe. */\nexport function toForwardSlashes(p: string): string {\n return p.replace(/\\\\/g, '/');\n}\n\n/**\n * Walk `cwd` recursively, returning relative paths (forward-slash) that pass\n * the supplied filter. The walker itself skips the `SKIP_DIRS` set; the\n * filter applies on top of that.\n *\n * Yields no directories, just files. Symlinks are not followed \u2014 keeps the\n * walk simple and safe under untrusted-project usage (cloud control plane\n * will scan customer code).\n */\nexport async function walkFiles(\n cwd: string,\n filter: (relativePath: string) => boolean,\n): Promise<string[]> {\n const results: string[] = [];\n await walkDir(cwd, cwd, filter, results);\n results.sort();\n return results;\n}\n\nasync function walkDir(\n root: string,\n current: string,\n filter: (relativePath: string) => boolean,\n results: string[],\n): Promise<void> {\n let entries;\n try {\n entries = await readdir(current, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const absolute = path.join(current, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n await walkDir(root, absolute, filter, results);\n continue;\n }\n if (!entry.isFile()) continue;\n const relative = toForwardSlashes(path.relative(root, absolute));\n if (!filter(relative)) continue;\n results.push(relative);\n }\n}\n\n/**\n * Read a file relative to `cwd` and return its content as UTF-8. Returns\n * an empty string if the file is unreadable \u2014 detectors then treat it as\n * \"no findings\" rather than crashing the run.\n */\nexport async function readRelative(\n cwd: string,\n relativePath: string,\n): Promise<string> {\n try {\n return await readFile(path.resolve(cwd, relativePath), 'utf8');\n } catch {\n return '';\n }\n}\n\n/**\n * Match a path against a list of simple glob-ish patterns. Supports `*`\n * (single segment, except `/`) and `**` (any segments). No brace expansion \u2014\n * keep it predictable.\n */\nexport function pathMatchesAny(\n relativePath: string,\n patterns: readonly string[],\n): boolean {\n for (const pattern of patterns) {\n if (matchGlob(relativePath, pattern)) return true;\n }\n return false;\n}\n\nfunction matchGlob(input: string, pattern: string): boolean {\n const regexSource = globToRegex(pattern);\n return new RegExp(`^${regexSource}$`).test(input);\n}\n\nfunction globToRegex(pattern: string): string {\n // Two-pass: convert `**` to a placeholder, escape other regex metachars,\n // then convert `*` to single-segment wildcard, finally restore `**`.\n const doubleStarToken = '\u0000DOUBLE_STAR\u0000';\n const singleStarToken = '\u0000SINGLE_STAR\u0000';\n let work = pattern.replace(/\\*\\*/g, doubleStarToken).replace(/\\*/g, singleStarToken);\n // CodeQL #194: include `?` (regex metachar \u2014 previously passed through and\n // silently turned `?` glob into the \"0-or-1\" regex quantifier) and `/`\n // (folds the separate line-128 pass back into a single escape step).\n work = work.replace(/[.+?^${}()|[\\]\\\\/]/g, (m) => `\\\\${m}`);\n work = work.replace(new RegExp(singleStarToken, 'g'), '[^/]*');\n work = work.replace(new RegExp(doubleStarToken, 'g'), '.*');\n return work;\n}\n\nexport const __test = {\n SKIP_DIRS,\n globToRegex,\n};\n", "// Shared test-file matchers.\r\n//\r\n// Detectors use these to decide whether a given path is a test file at all.\r\n// Centralized so the regex behavior is consistent across detectors and the\r\n// CLI integration tests can stub it without redeclaring patterns.\r\n\r\nimport { toForwardSlashes } from './walk.js';\r\n\r\nconst TEST_FILE_RE = /(?:^|\\/)[^/]+\\.(?:test|spec)\\.(?:ts|tsx|js|jsx|mjs|cjs)$/u;\r\nconst TEST_TSX_RE = /\\.test\\.tsx$/u;\r\n\r\n/** True when `filePath` looks like a Vitest/Jest test file. */\r\nexport function isTestFile(filePath: string): boolean {\r\n return TEST_FILE_RE.test(toForwardSlashes(filePath));\r\n}\r\n\r\n/** True when `filePath` is a React-component-style test (matters for hollow-tests Rule b). */\r\nexport function isComponentTestFile(filePath: string): boolean {\r\n return TEST_TSX_RE.test(toForwardSlashes(filePath));\r\n}\r\n\r\n/**\r\n * Mask string and comment content so regex passes don't false-positive on\r\n * sample code inside docstrings. Returns a string the same length as the\r\n * input, with quoted/commented characters replaced by spaces (preserving\r\n * line breaks so line numbers still line up).\r\n */\r\nexport function maskStringsAndComments(content: string): string {\r\n let out = '';\r\n let quote: string | null = null;\r\n let escaped = false;\r\n let lineComment = false;\r\n let blockComment = false;\r\n const text = String(content || '');\r\n\r\n for (let index = 0; index < text.length; index += 1) {\r\n const char = text[index] ?? '';\r\n const next = text[index + 1] ?? '';\r\n\r\n if (lineComment) {\r\n if (char === '\\r' || char === '\\n') {\r\n out += char;\r\n lineComment = false;\r\n } else {\r\n out += ' ';\r\n }\r\n continue;\r\n }\r\n if (blockComment) {\r\n if (char === '*' && next === '/') {\r\n out += ' ';\r\n index += 1;\r\n blockComment = false;\r\n } else if (char === '\\r' || char === '\\n') {\r\n out += char;\r\n } else {\r\n out += ' ';\r\n }\r\n continue;\r\n }\r\n if (!quote) {\r\n if (char === '/' && next === '/') {\r\n out += ' ';\r\n index += 1;\r\n lineComment = true;\r\n continue;\r\n }\r\n if (char === '/' && next === '*') {\r\n out += ' ';\r\n index += 1;\r\n blockComment = true;\r\n continue;\r\n }\r\n if (char === '\"' || char === \"'\" || char === '`') {\r\n quote = char;\r\n out += char;\r\n continue;\r\n }\r\n out += char;\r\n continue;\r\n }\r\n // Inside a quoted string.\r\n if (char === '\\r' || char === '\\n') {\r\n out += char;\r\n if (quote !== '`') {\r\n quote = null;\r\n escaped = false;\r\n }\r\n continue;\r\n }\r\n if (escaped) {\r\n out += ' ';\r\n escaped = false;\r\n continue;\r\n }\r\n if (char === '\\\\') {\r\n out += ' ';\r\n escaped = true;\r\n continue;\r\n }\r\n if (char === quote) {\r\n out += char;\r\n quote = null;\r\n continue;\r\n }\r\n out += ' ';\r\n }\r\n return out;\r\n}\r\n\r\n/** Count regex matches without mutating the regex's lastIndex state. */\r\nexport function countMatches(content: string, regex: RegExp): number {\r\n const re = new RegExp(regex.source, regex.flags);\r\n return (String(content || '').match(re) || []).length;\r\n}\r\n\r\nexport const __test = {\r\n TEST_FILE_RE,\r\n TEST_TSX_RE,\r\n};\r\n", "// assertion-strength detector \u2014 flags tests that \"pass\" without verifying\r\n// the unit under test produces the right answer.\r\n//\r\n// Pattern set (per handoff \u00A75.1, ported from Algosuite's\r\n// scripts/ci/check-assertion-strength-ratchet-core.mjs + test-strength-inventory):\r\n//\r\n// trivial-only every expect() matcher in the file is trivial\r\n// (toBeDefined / toBeTruthy / toBeNull / toBeUndefined /\r\n// toBe(true|false) / not.toThrow).\r\n// to-have-been-called `expect(fn).toHaveBeenCalled()` with no\r\n// `toHaveBeenCalledWith` or value-comparing matcher\r\n// anywhere in the same test file.\r\n// not-to-throw-only file's only assertion-style line is\r\n// `expect(() => fn()).not.toThrow()`.\r\n// mock-calls-length-only every assertion targets `.mock.calls.length`\r\n// and nothing else.\r\n// literal-true `expect(true)` or `assert(true)` \u2014 passes by\r\n// construction.\r\n//\r\n// Allowlist marker (file-scope, anywhere before the first import):\r\n// // vo-ratchets-allow-assertion-strength: <reason>\r\n//\r\n// Eats own dog food: every test of this detector under test/detectors/ uses\r\n// value-comparing matchers (toEqual / toBe(<value>) / toHaveLength) \u2014 never\r\n// trivial-only.\r\n\r\nimport type {\r\n Detector,\r\n DetectorResult,\r\n DetectorRunInput,\r\n RatchetFinding,\r\n RatchetSeverity,\r\n RatchetThreshold,\r\n} from '../types.js';\r\nimport {\r\n countMatches,\r\n isTestFile,\r\n maskStringsAndComments,\r\n} from '../util/test-files.js';\r\nimport { readRelative, walkFiles } from '../util/walk.js';\r\n\r\nconst RATCHET_ID = 'assertion-strength' as const;\r\n\r\nconst ALLOW_MARKER_RE = /\\/\\/\\s*vo-ratchets-allow-assertion-strength\\s*:\\s*\\S/iu;\r\nconst ANY_MATCHER_RE = /\\.(?:not\\s*\\.\\s*)?to[A-Z]\\w*\\s*\\(/gu;\r\nconst TRIVIAL_MATCHER_RE = new RegExp(\r\n '\\\\.(?:toBeDefined|toBeTruthy|toBeFalsy|toBeNull|toBeUndefined)\\\\s*\\\\(\\\\s*\\\\)'\r\n + '|\\\\.toBe\\\\s*\\\\(\\\\s*(?:true|false)\\\\s*\\\\)'\r\n + '|\\\\.(?:not\\\\s*\\\\.\\\\s*)?toThrow\\\\s*\\\\(\\\\s*\\\\)',\r\n 'gu',\r\n);\r\nconst TO_HAVE_BEEN_CALLED_RE = /\\.toHaveBeenCalled\\s*\\(\\s*\\)/u;\r\nconst TO_HAVE_BEEN_CALLED_WITH_RE = /\\.toHaveBeenCalledWith\\s*\\(/u;\r\nconst MOCK_CALLS_LENGTH_RE = /\\.mock\\.calls\\.length\\b/u;\r\nconst NOT_TO_THROW_RE = /\\.not\\s*\\.\\s*toThrow\\s*\\(/u;\r\nconst LITERAL_TRUE_RE = /\\b(?:expect|assert(?:\\.ok)?)\\s*\\(\\s*true\\s*\\)/u;\r\n\r\ninterface RuleHit {\r\n rule: string;\r\n message: string;\r\n severity: RatchetSeverity;\r\n}\r\n\r\nexport function hasAllowMarker(content: string): boolean {\r\n return ALLOW_MARKER_RE.test(content);\r\n}\r\n\r\nexport function inspectFile({\r\n filePath,\r\n content,\r\n threshold,\r\n}: {\r\n filePath: string;\r\n content: string;\r\n threshold: RatchetThreshold;\r\n}): RuleHit[] {\r\n if (!isTestFile(filePath)) return [];\r\n if (hasAllowMarker(content)) return [];\r\n\r\n const masked = maskStringsAndComments(content);\r\n const hits: RuleHit[] = [];\r\n\r\n // literal-true: catches `expect(true)` no matter the threshold.\r\n if (LITERAL_TRUE_RE.test(masked)) {\r\n hits.push({\r\n rule: 'literal-true',\r\n severity: 'error',\r\n message:\r\n 'Found `expect(true)` or `assert(true)`. Literal-true assertions pass '\r\n + 'by construction and prove no behavior. Replace with a value-comparing '\r\n + 'matcher against the actual output.',\r\n });\r\n }\r\n\r\n // mock-calls-length-only: every assertion targets `.mock.calls.length`.\r\n const totalMatchers = countMatches(masked, ANY_MATCHER_RE);\r\n if (totalMatchers > 0 && countMockCallsLengthAssertions(masked) === totalMatchers) {\r\n hits.push({\r\n rule: 'mock-calls-length-only',\r\n severity: 'error',\r\n message:\r\n 'Every assertion in this file checks `.mock.calls.length`. Verifying '\r\n + 'a mock was called N times without verifying the arguments or the '\r\n + 'effect on the unit under test does not prove behavior. Add at '\r\n + 'least one assertion against the system\\'s actual output.',\r\n });\r\n }\r\n\r\n // not-to-throw-only: the only assertion is `.not.toThrow()`.\r\n if (totalMatchers > 0 && isNotToThrowOnly(masked, totalMatchers)) {\r\n hits.push({\r\n rule: 'not-to-throw-only',\r\n severity: 'error',\r\n message:\r\n 'The only assertion is `expect(...).not.toThrow()`. \"Doesn\\'t crash\" '\r\n + 'is a smoke check, not a behavioral test. Verify the return value '\r\n + 'or side effect, not just the absence of an exception.',\r\n });\r\n }\r\n\r\n // to-have-been-called without -With: weakest threshold tolerates one\r\n // bare call followed by separate behavioral assertions; medium and strict\r\n // require pairing with `toHaveBeenCalledWith` or a value-comparing matcher\r\n // somewhere in the file.\r\n if (shouldFlagBareToHaveBeenCalled(masked, threshold)) {\r\n hits.push({\r\n rule: 'to-have-been-called',\r\n severity: threshold === 'permissive' ? 'warning' : 'error',\r\n message:\r\n '`toHaveBeenCalled()` appears without any `toHaveBeenCalledWith(...)` '\r\n + 'or value-comparing assertion in this file. Verifying that a mock '\r\n + 'fired tells you nothing about whether it was called correctly. '\r\n + 'Pair with `toHaveBeenCalledWith(...)` or assert on the resulting '\r\n + 'state.',\r\n });\r\n }\r\n\r\n // trivial-only: every expect matcher is trivial.\r\n if (totalMatchers > 0) {\r\n const trivialMatchers = countMatches(masked, TRIVIAL_MATCHER_RE);\r\n if (trivialMatchers === totalMatchers && threshold !== 'permissive') {\r\n hits.push({\r\n rule: 'trivial-only',\r\n severity: 'error',\r\n message:\r\n `All ${totalMatchers} expect() matcher${totalMatchers === 1 ? '' : 's'} `\r\n + 'are trivial (toBeDefined / toBeTruthy / toBeFalsy / toBeNull / '\r\n + 'toBeUndefined / toBe(true|false) / not.toThrow). These prove the '\r\n + 'file imported and ran but verify no behavior. Add at least one '\r\n + 'value-comparing matcher: toEqual, toStrictEqual, toBe(<value>), '\r\n + 'toMatch, toMatchObject, toContain, toHaveLength, toHaveProperty.',\r\n });\r\n }\r\n }\r\n\r\n return hits;\r\n}\r\n\r\nfunction countMockCallsLengthAssertions(masked: string): number {\r\n let count = 0;\r\n for (const line of masked.split(/\\r?\\n/u)) {\r\n if (!MOCK_CALLS_LENGTH_RE.test(line)) continue;\r\n if (!/\\.(?:not\\s*\\.\\s*)?to[A-Z]\\w*\\s*\\(/u.test(line)) continue;\r\n count += 1;\r\n }\r\n return count;\r\n}\r\n\r\nfunction isNotToThrowOnly(masked: string, totalMatchers: number): boolean {\r\n if (!NOT_TO_THROW_RE.test(masked)) return false;\r\n const notToThrowCount = countMatches(masked, /\\.not\\s*\\.\\s*toThrow\\s*\\(/gu);\r\n return notToThrowCount === totalMatchers;\r\n}\r\n\r\nfunction shouldFlagBareToHaveBeenCalled(\r\n masked: string,\r\n threshold: RatchetThreshold,\r\n): boolean {\r\n if (!TO_HAVE_BEEN_CALLED_RE.test(masked)) return false;\r\n if (TO_HAVE_BEEN_CALLED_WITH_RE.test(masked)) return false;\r\n // Value-comparing matcher anywhere in the file is enough proof.\r\n const totalMatchers = countMatches(masked, ANY_MATCHER_RE);\r\n const trivialMatchers = countMatches(masked, TRIVIAL_MATCHER_RE);\r\n const valueComparing = totalMatchers - trivialMatchers\r\n - countMatches(masked, /\\.toHaveBeenCalled\\s*\\(\\s*\\)/gu);\r\n if (threshold === 'permissive') return false;\r\n if (threshold === 'medium' && valueComparing >= 1) return false;\r\n return true;\r\n}\r\n\r\nexport const assertionStrengthDetector: Detector = {\r\n id: RATCHET_ID,\r\n stub: false,\r\n async run(input: DetectorRunInput): Promise<DetectorResult> {\r\n const threshold = input.config.thresholds[RATCHET_ID];\r\n const files = await walkFiles(input.cwd, (rel) => isTestFile(rel));\r\n const findings: RatchetFinding[] = [];\r\n for (const file of files) {\r\n const content = await readRelative(input.cwd, file);\r\n for (const hit of inspectFile({ filePath: file, content, threshold })) {\r\n findings.push({\r\n ratchet: RATCHET_ID,\r\n rule: hit.rule,\r\n file,\r\n severity: hit.severity,\r\n message: hit.message,\r\n });\r\n }\r\n }\r\n return {\r\n ratchet: RATCHET_ID,\r\n ran: true,\r\n findings,\r\n filesScanned: files.length,\r\n stub: false,\r\n };\r\n },\r\n};\r\n\r\nexport const __test = {\r\n ALLOW_MARKER_RE,\r\n ANY_MATCHER_RE,\r\n TRIVIAL_MATCHER_RE,\r\n TO_HAVE_BEEN_CALLED_RE,\r\n TO_HAVE_BEEN_CALLED_WITH_RE,\r\n MOCK_CALLS_LENGTH_RE,\r\n NOT_TO_THROW_RE,\r\n LITERAL_TRUE_RE,\r\n};\r\n", "// fix-strength detector \u2014 flags fix-PRs whose tests replace strong literal\r\n// assertions with comparator-only ones.\r\n//\r\n// Ported from scripts/ci/check-fix-strength.mjs +\r\n// scripts/ci/check-verify-answer-core.mjs (the actual source-of-truth lives\r\n// in `compareStrengthBetweenVersions`). The honest framing:\r\n//\r\n// - A \"fix\" PR claims to fix a real bug.\r\n// - The trivial way for an automated fixer to make a failing test pass is\r\n// to WEAKEN the assertion (replace `.toBe(14750)` with `.toBeGreaterThan(0)`).\r\n// - The static gate (`verify-answer`) catches NEW comparator-only lines,\r\n// but not the deletion of the strong line that was replaced.\r\n//\r\n// This detector compares pre and post versions of every changed test\r\n// file. If pre had strong assertions and post has fewer strong + more\r\n// weak ones, the PR is flagged.\r\n//\r\n// Important honesty note on the port:\r\n// The stub's preamble described \"checkout parent commit + run test + assert\r\n// it fails.\" That is NOT what the Algosuite source-of-truth actually does\r\n// \u2014 the real check is a comparator-counting check that does NOT execute\r\n// tests. Running tests at two commits would be much more expensive AND\r\n// would require the detector to know how to invoke the project's test\r\n// runner. The pattern-counting check captures the actual signal (strong\r\n// matchers deleted, weak matchers added) without spawning a test runner.\r\n//\r\n// Cost note:\r\n// Even the comparator-counting check needs the pre-version of each changed\r\n// file. The default implementation uses `git show <baseRef>:<path>` which\r\n// spawns one `git` process per changed file. For projects with hundreds of\r\n// changed test files this is non-trivial. Set `disabled: true` in the\r\n// config or rely on the threshold = 'permissive' downgrade to keep runs\r\n// fast in interactive (MCP) contexts.\r\n//\r\n// Behavior:\r\n// The detector is OFF unless the config supplies a `gitContext` via a\r\n// side-channel \u2014 by default `run` walks no files and returns an empty\r\n// result with `ran: true` and a sentinel diagnostic. Callers that DO want\r\n// the git-aware behavior should call `inspectChanges(...)` directly (the\r\n// cloud control plane wires it in this way).\r\n//\r\n// Allowlist markers:\r\n// File-scope (anywhere before the first import):\r\n// // vo-ratchets-allow-fix-strength: <reason>\r\n// In either pre or post source (single line within 3 lines of the change):\r\n// // fix-strength-allow: <reason>\r\n\r\nimport { spawnSync } from 'node:child_process';\r\nimport type {\r\n Detector,\r\n DetectorResult,\r\n DetectorRunInput,\r\n RatchetFinding,\r\n RatchetSeverity,\r\n RatchetThreshold,\r\n} from '../types.js';\r\nimport { isTestFile } from '../util/test-files.js';\r\n\r\nconst RATCHET_ID = 'fix-strength' as const;\r\n\r\nconst FILE_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-fix-strength\\s*:\\s*\\S/iu;\r\nconst ALLOW_MARKER_RE = /\\b(?:fix-strength-allow|verify-answer-allow)\\s*:\\s*\\S/iu;\r\n\r\n// Strong matchers: pin a specific expected value or run through a\r\n// `verify*`-family helper that has its own pinning contract.\r\nconst DEFAULT_STRONG_PATTERNS: ReadonlyArray<RegExp> = [\r\n /\\.toBe\\s*\\(\\s*(?:-?\\d+(?:\\.\\d+)?|true|false|null|undefined|\"[^\"\\n]*\"|'[^'\\n]*'|`[^`\\n]*`)/u,\r\n /\\.toEqual\\s*\\(\\s*(?:-?\\d+(?:\\.\\d+)?|true|false|null|undefined|\"[^\"\\n]*\"|'[^'\\n]*'|`[^`\\n]*`|\\[|\\{)/u,\r\n /\\.toStrictEqual\\s*\\(/u,\r\n /\\.toBeCloseTo\\s*\\(/u,\r\n /\\.toMatchObject\\s*\\(/u,\r\n /\\.toMatchInlineSnapshot\\s*\\(/u,\r\n];\r\n\r\n// Weak matchers: prove the system returned something but don't pin a value.\r\nconst DEFAULT_WEAK_PATTERNS: ReadonlyArray<RegExp> = [\r\n /\\.toBeGreaterThan\\s*\\(/u,\r\n /\\.toBeGreaterThanOrEqual\\s*\\(/u,\r\n /\\.toBeLessThan\\s*\\(/u,\r\n /\\.toBeLessThanOrEqual\\s*\\(/u,\r\n /\\.toBeTruthy\\s*\\(\\s*\\)/u,\r\n /\\.toBeFalsy\\s*\\(\\s*\\)/u,\r\n];\r\n\r\n/** Counts of strong / weak matchers in a file version. */\r\nexport interface VersionCounts {\r\n strong: number;\r\n weak: number;\r\n}\r\n\r\nexport type Verdict =\r\n | 'ok'\r\n | 'no-strong-pre'\r\n | 'strong-removed'\r\n | 'strength-decreased';\r\n\r\nexport interface FileComparison {\r\n filePath: string;\r\n verdict: Verdict;\r\n pre: VersionCounts;\r\n post: VersionCounts;\r\n reason?: string;\r\n}\r\n\r\nexport interface ExtraPatternConfig {\r\n /** Bare identifier helpers that count as strong (e.g. `verifyAnswer`). */\r\n extraStrongHelpers?: readonly string[];\r\n /** Extra regex patterns that count as strong. */\r\n extraStrongPatterns?: readonly RegExp[];\r\n /** Extra regex patterns that count as weak. */\r\n extraWeakPatterns?: readonly RegExp[];\r\n}\r\n\r\nfunction buildHelperPatterns(names: readonly string[]): RegExp[] {\r\n const result: RegExp[] = [];\r\n for (const name of names) {\r\n if (!/^[A-Za-z_$][\\w$]*$/u.test(name)) continue;\r\n result.push(new RegExp(`\\\\b${name}\\\\s*\\\\(`, 'u'));\r\n }\r\n return result;\r\n}\r\n\r\nfunction countMatchesInSource(\r\n source: string,\r\n patterns: readonly RegExp[],\r\n): number {\r\n if (typeof source !== 'string' || !source) return 0;\r\n let total = 0;\r\n const lines = source.split(/\\r?\\n/u);\r\n for (const line of lines) {\r\n const stripped = line.trim();\r\n if (!stripped) continue;\r\n if (stripped.startsWith('//') || stripped.startsWith('*')) continue;\r\n for (const re of patterns) {\r\n const matches = line.match(new RegExp(re.source, `${re.flags}g`));\r\n if (matches) total += matches.length;\r\n }\r\n }\r\n return total;\r\n}\r\n\r\nexport function countStrong(\r\n source: string,\r\n extras: ExtraPatternConfig = {},\r\n): number {\r\n const patterns = [\r\n ...DEFAULT_STRONG_PATTERNS,\r\n ...buildHelperPatterns(extras.extraStrongHelpers ?? []),\r\n ...(extras.extraStrongPatterns ?? []),\r\n ];\r\n return countMatchesInSource(source, patterns);\r\n}\r\n\r\nexport function countWeak(\r\n source: string,\r\n extras: ExtraPatternConfig = {},\r\n): number {\r\n const patterns = [\r\n ...DEFAULT_WEAK_PATTERNS,\r\n ...(extras.extraWeakPatterns ?? []),\r\n ];\r\n return countMatchesInSource(source, patterns);\r\n}\r\n\r\nexport interface CompareInput extends ExtraPatternConfig {\r\n filePath: string;\r\n preSource: string;\r\n postSource: string;\r\n}\r\n\r\n/**\r\n * Compare strong/weak assertion counts between pre- and post-fix versions\r\n * of a single file. Pure function \u2014 no I/O.\r\n */\r\nexport function compareStrengthBetweenVersions(\r\n input: CompareInput,\r\n): FileComparison {\r\n const { filePath, preSource, postSource } = input;\r\n const preStrong = countStrong(preSource, input);\r\n const preWeak = countWeak(preSource, input);\r\n const postStrong = countStrong(postSource, input);\r\n const postWeak = countWeak(postSource, input);\r\n const pre: VersionCounts = { strong: preStrong, weak: preWeak };\r\n const post: VersionCounts = { strong: postStrong, weak: postWeak };\r\n\r\n if (FILE_ALLOW_RE.test(preSource) || FILE_ALLOW_RE.test(postSource)) {\r\n return { filePath, verdict: 'ok', pre, post, reason: 'file-allow-marker' };\r\n }\r\n const sources = `${preSource ?? ''}\\n${postSource ?? ''}`;\r\n if (ALLOW_MARKER_RE.test(sources)) {\r\n return { filePath, verdict: 'ok', pre, post, reason: 'line-allow-marker' };\r\n }\r\n\r\n if (preStrong === 0) {\r\n return { filePath, verdict: 'no-strong-pre', pre, post };\r\n }\r\n const strongLost = preStrong > postStrong;\r\n const weakRose = postWeak > preWeak;\r\n if (strongLost && weakRose) {\r\n return { filePath, verdict: 'strength-decreased', pre, post };\r\n }\r\n if (strongLost && !weakRose) {\r\n return { filePath, verdict: 'strong-removed', pre, post };\r\n }\r\n return { filePath, verdict: 'ok', pre, post };\r\n}\r\n\r\nexport interface InspectChangesInput extends ExtraPatternConfig {\r\n /** Changed files (relative paths, forward-slashed). */\r\n changedFiles: readonly string[];\r\n /** Async reader returning the source at the base ref. Empty string if missing. */\r\n readBase(filePath: string): Promise<string>;\r\n /** Async reader returning the source at the head ref. */\r\n readHead(filePath: string): Promise<string>;\r\n /** Threshold \u2014 controls severity of `strong-removed`. */\r\n threshold: RatchetThreshold;\r\n}\r\n\r\ninterface InspectionFinding {\r\n rule: string;\r\n file: string;\r\n severity: RatchetSeverity;\r\n message: string;\r\n}\r\n\r\n/**\r\n * Run the comparator on every changed file and produce findings. The caller\r\n * supplies the file list and the per-ref readers \u2014 `run` (below) shows the\r\n * default `git show` wiring.\r\n */\r\nexport async function inspectChanges(\r\n input: InspectChangesInput,\r\n): Promise<InspectionFinding[]> {\r\n const findings: InspectionFinding[] = [];\r\n for (const file of input.changedFiles) {\r\n if (!isTestFile(file)) continue;\r\n const [pre, post] = await Promise.all([\r\n input.readBase(file),\r\n input.readHead(file),\r\n ]);\r\n const verdict = compareStrengthBetweenVersions({\r\n filePath: file,\r\n preSource: pre,\r\n postSource: post,\r\n // Conditional-spread the optional `extra*` overrides so\r\n // exactOptionalPropertyTypes is satisfied (undefined isn't assignable\r\n // to `readonly T[]` when the property is non-optional in the target).\r\n ...(input.extraStrongHelpers !== undefined ? { extraStrongHelpers: input.extraStrongHelpers } : {}),\r\n ...(input.extraStrongPatterns !== undefined ? { extraStrongPatterns: input.extraStrongPatterns } : {}),\r\n ...(input.extraWeakPatterns !== undefined ? { extraWeakPatterns: input.extraWeakPatterns } : {}),\r\n });\r\n if (verdict.verdict === 'strength-decreased') {\r\n findings.push({\r\n rule: 'strength-decreased',\r\n file,\r\n severity: input.threshold === 'permissive' ? 'warning' : 'error',\r\n message:\r\n `${file} weakened: pre had ${verdict.pre.strong} strong / `\r\n + `${verdict.pre.weak} weak; post has ${verdict.post.strong} strong / `\r\n + `${verdict.post.weak} weak. A fix must not replace strong literal-pinned `\r\n + 'assertions with comparator-only ones. Restore the strong assertion, '\r\n + 'pin a new expected value, or add `// fix-strength-allow: <reason>`.',\r\n });\r\n continue;\r\n }\r\n if (verdict.verdict === 'strong-removed') {\r\n findings.push({\r\n rule: 'strong-removed',\r\n file,\r\n severity: input.threshold === 'strict' ? 'error' : 'warning',\r\n message:\r\n `${file} dropped strong assertion(s) without compensation: pre `\r\n + `${verdict.pre.strong} -> post ${verdict.post.strong}. If this `\r\n + 'was intentional (test moved/split, fixture corrected), add '\r\n + '`// fix-strength-allow: <reason>`.',\r\n });\r\n }\r\n }\r\n return findings;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Default `run` implementation \u2014 git-aware. Off unless the caller passes\r\n// `extras['fix-strength']` config or env vars to point at the base/head refs.\r\n// ---------------------------------------------------------------------------\r\n\r\nfunction readGitFile(cwd: string, ref: string, filePath: string): string {\r\n const result = spawnSync('git', ['show', `${ref}:${filePath}`], {\r\n cwd,\r\n encoding: 'utf8',\r\n maxBuffer: 16 * 1024 * 1024,\r\n });\r\n if (result.status !== 0) return '';\r\n return result.stdout || '';\r\n}\r\n\r\nfunction listChangedFiles(cwd: string, baseRef: string, headRef: string): string[] {\r\n const result = spawnSync(\r\n 'git',\r\n ['diff', '--name-only', '--diff-filter=AMR', `${baseRef}...${headRef}`],\r\n { cwd, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 },\r\n );\r\n if (result.status !== 0) return [];\r\n return (result.stdout || '')\r\n .split(/\\r?\\n/u)\r\n .map((line) => line.trim())\r\n .filter(Boolean);\r\n}\r\n\r\nexport const fixStrengthDetector: Detector = {\r\n id: RATCHET_ID,\r\n stub: false,\r\n async run(input: DetectorRunInput): Promise<DetectorResult> {\r\n const threshold = input.config.thresholds[RATCHET_ID];\r\n // Off-by-default per the cost note: git checkout-equivalent work is\r\n // expensive enough that we require explicit opt-in via env vars. Other\r\n // callers can call `inspectChanges` directly.\r\n const baseRef = process.env['VO_RATCHETS_FIX_STRENGTH_BASE'] || '';\r\n const headRef = process.env['VO_RATCHETS_FIX_STRENGTH_HEAD'] || '';\r\n if (!baseRef || !headRef) {\r\n return {\r\n ratchet: RATCHET_ID,\r\n ran: false,\r\n findings: [],\r\n filesScanned: 0,\r\n stub: false,\r\n };\r\n }\r\n const changedFiles = listChangedFiles(input.cwd, baseRef, headRef);\r\n const findings = await inspectChanges({\r\n changedFiles,\r\n readBase: async (file) => readGitFile(input.cwd, baseRef, file),\r\n readHead: async (file) => readGitFile(input.cwd, headRef, file),\r\n threshold,\r\n });\r\n const ratchetFindings: RatchetFinding[] = findings.map((f) => ({\r\n ratchet: RATCHET_ID,\r\n rule: f.rule,\r\n file: f.file,\r\n severity: f.severity,\r\n message: f.message,\r\n }));\r\n return {\r\n ratchet: RATCHET_ID,\r\n ran: true,\r\n findings: ratchetFindings,\r\n filesScanned: changedFiles.filter(isTestFile).length,\r\n stub: false,\r\n };\r\n },\r\n};\r\n\r\nexport const __test = {\r\n FILE_ALLOW_RE,\r\n ALLOW_MARKER_RE,\r\n DEFAULT_STRONG_PATTERNS,\r\n DEFAULT_WEAK_PATTERNS,\r\n};\r\n", "// hollow-tests detector \u2014 flags test files that exist for coverage credit\r\n// but verify nothing meaningful.\r\n//\r\n// Patterns (per handoff \u00A75.2, ported from Algosuite's check-hollow-tests-core.mjs):\r\n//\r\n// empty-test-body `it('foo', () => {})` \u2014 body has no assertions\r\n// and no mock setup.\r\n// it-count-floor file has fewer than 4 it()/test() blocks\r\n// (skip/todo excluded unless allow-marked).\r\n// Strictness controls the floor: strict=4,\r\n// medium=3, permissive=1.\r\n// no-interaction `.test.tsx` file without userEvent / fireEvent /\r\n// `await user.*` \u2014 component test that never\r\n// clicks anything. Strictness gates this rule:\r\n// permissive turns it off.\r\n// swallowing-catch body wrapped in try/catch where the catch\r\n// returns true or swallows silently.\r\n//\r\n// Allowlist marker (file-scope, on a line above the first import):\r\n// // vo-ratchets-allow-hollow-tests: <reason>\r\n//\r\n// Allowlist marker (skip-scope, immediately above an .it.skip / .test.skip /\r\n// .it.todo / .test.todo line):\r\n// // vo-ratchets-allow-hollow-tests-skip: <reason>\r\n\r\nimport type {\r\n Detector,\r\n DetectorResult,\r\n DetectorRunInput,\r\n RatchetFinding,\r\n RatchetSeverity,\r\n RatchetThreshold,\r\n} from '../types.js';\r\nimport {\r\n countMatches,\r\n isComponentTestFile,\r\n isTestFile,\r\n maskStringsAndComments,\r\n} from '../util/test-files.js';\r\nimport { readRelative, walkFiles } from '../util/walk.js';\r\n\r\nconst RATCHET_ID = 'hollow-tests' as const;\r\n\r\nconst FILE_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-hollow-tests\\s*:\\s*\\S/iu;\r\nconst SKIP_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-hollow-tests-skip\\s*:\\s*\\S/iu;\r\n\r\nconst IT_COUNTING_RE = /(?<![.\\w])(?:it|test)(?:\\.only)?\\s*\\(/gu;\r\nconst IT_SKIP_RE = /(?<![.\\w])(?:it|test)\\s*\\.\\s*(?:skip|todo)\\s*\\(/u;\r\nconst USER_EVENT_RE = /\\buserEvent\\s*\\.\\s*\\w+\\s*\\(/u;\r\nconst FIRE_EVENT_RE = /\\bfireEvent\\s*\\.\\s*\\w+\\s*\\(/u;\r\nconst AWAIT_USER_RE = /\\bawait\\s+user\\s*\\.\\s*\\w+\\s*\\(/u;\r\nconst ANY_MATCHER_RE = /\\.(?:not\\s*\\.\\s*)?to[A-Z]\\w*\\s*\\(/gu;\r\nconst SWALLOWING_CATCH_RE = /catch\\s*(?:\\([^)]*\\))?\\s*\\{\\s*(?:\\/\\/[^\\n]*\\n\\s*)?\\}/u;\r\nconst CATCH_RETURN_TRUE_RE = /catch\\s*(?:\\([^)]*\\))?\\s*\\{\\s*return\\s+true\\s*;?\\s*\\}/u;\r\nconst EMPTY_IT_RE =\r\n /(?<![.\\w])(?:it|test)\\s*\\(\\s*['\"`][^'\"`\\n]+['\"`]\\s*,\\s*(?:async\\s*)?(?:\\(\\s*\\)\\s*=>|function\\s*\\(\\s*\\))\\s*\\{\\s*\\}\\s*\\)/gu;\r\n\r\ninterface RuleHit {\r\n rule: string;\r\n message: string;\r\n severity: RatchetSeverity;\r\n}\r\n\r\nconst FLOOR_BY_THRESHOLD: Record<RatchetThreshold, number> = {\r\n strict: 4,\r\n medium: 3,\r\n permissive: 1,\r\n};\r\n\r\nexport function hasFileScopeAllowMarker(content: string): boolean {\r\n const lines = String(content || '').split(/\\r?\\n/u);\r\n let firstImport = -1;\r\n for (let index = 0; index < lines.length; index += 1) {\r\n if (/^\\s*import\\s/u.test(lines[index] || '')) {\r\n firstImport = index;\r\n break;\r\n }\r\n }\r\n const cap = firstImport >= 0 ? firstImport : Math.min(20, lines.length - 1);\r\n for (let index = 0; index <= cap; index += 1) {\r\n if (FILE_ALLOW_RE.test(lines[index] || '')) return true;\r\n }\r\n return false;\r\n}\r\n\r\nexport function countAllowedSkips(content: string): number {\r\n const lines = String(content || '').split(/\\r?\\n/u);\r\n let count = 0;\r\n for (let index = 0; index < lines.length; index += 1) {\r\n if (!IT_SKIP_RE.test(lines[index] || '')) continue;\r\n const previous = lines[index - 1] || '';\r\n if (SKIP_ALLOW_RE.test(previous)) count += 1;\r\n }\r\n return count;\r\n}\r\n\r\nexport function inspectFile({\r\n filePath,\r\n content,\r\n threshold,\r\n}: {\r\n filePath: string;\r\n content: string;\r\n threshold: RatchetThreshold;\r\n}): RuleHit[] {\r\n if (!isTestFile(filePath)) return [];\r\n if (hasFileScopeAllowMarker(content)) return [];\r\n\r\n const masked = maskStringsAndComments(content);\r\n const hits: RuleHit[] = [];\r\n\r\n // empty-test-body \u2014 surface BEFORE the count floor so empty stubs are\r\n // called out specifically.\r\n const emptyBodyCount = countMatches(masked, EMPTY_IT_RE);\r\n if (emptyBodyCount > 0) {\r\n hits.push({\r\n rule: 'empty-test-body',\r\n severity: 'error',\r\n message:\r\n `Found ${emptyBodyCount} it()/test() block${emptyBodyCount === 1 ? '' : 's'} `\r\n + 'with an empty body. An empty test passes by construction and verifies '\r\n + 'nothing. Either implement the test or remove the placeholder; if you '\r\n + 'genuinely want a planned-but-not-yet-written marker, use `.todo()`.',\r\n });\r\n }\r\n\r\n // it-count-floor \u2014 strictness-gated.\r\n const floor = FLOOR_BY_THRESHOLD[threshold];\r\n const itCount = countMatches(masked, IT_COUNTING_RE);\r\n const allowedSkipCount = countAllowedSkips(content);\r\n const effectiveCount = itCount + allowedSkipCount;\r\n if (effectiveCount < floor && itCount + allowedSkipCount > 0) {\r\n const skipNote = allowedSkipCount > 0\r\n ? ` (+ ${allowedSkipCount} allow-marked skip${allowedSkipCount === 1 ? '' : 's'})`\r\n : '';\r\n hits.push({\r\n rule: 'it-count-floor',\r\n severity: threshold === 'strict' ? 'error' : 'warning',\r\n message:\r\n `Found ${itCount} it()/test() block${itCount === 1 ? '' : 's'}${skipNote}. `\r\n + `Threshold (${threshold}) requires ${floor}+ tests, including at least `\r\n + 'one interaction case and one edge/error case. To exempt this file, '\r\n + 'add `// vo-ratchets-allow-hollow-tests: <why>` above the first import.',\r\n });\r\n }\r\n\r\n // no-interaction \u2014 only for .test.tsx, and only at strict/medium.\r\n if (isComponentTestFile(filePath) && threshold !== 'permissive') {\r\n const hasInteraction =\r\n USER_EVENT_RE.test(masked)\r\n || FIRE_EVENT_RE.test(masked)\r\n || AWAIT_USER_RE.test(masked);\r\n const hasAnyMatcher = countMatches(masked, ANY_MATCHER_RE) > 0;\r\n if (!hasInteraction && hasAnyMatcher) {\r\n hits.push({\r\n rule: 'no-interaction',\r\n severity: 'warning',\r\n message:\r\n '.test.tsx file has no userEvent / fireEvent / `await user.*` call. '\r\n + 'Component tests must verify behavior, not just render. Use '\r\n + '`const user = userEvent.setup()` then `await user.click(...)`, '\r\n + 'or `fireEvent.X(...)` for events userEvent does not support.',\r\n });\r\n }\r\n }\r\n\r\n // swallowing-catch \u2014 wraps body in try/catch and swallows or returns true.\r\n if (\r\n SWALLOWING_CATCH_RE.test(masked)\r\n || CATCH_RETURN_TRUE_RE.test(masked)\r\n ) {\r\n hits.push({\r\n rule: 'swallowing-catch',\r\n severity: 'error',\r\n message:\r\n 'Test body contains an empty `catch {}` or `catch { return true; }`. '\r\n + 'Swallowing exceptions turns \"the system threw\" into a passing test. '\r\n + 'Either assert on the expected error (`expect(() => ...).toThrow(...)`)'\r\n + ' or let the exception bubble.',\r\n });\r\n }\r\n\r\n return hits;\r\n}\r\n\r\nexport const hollowTestsDetector: Detector = {\r\n id: RATCHET_ID,\r\n stub: false,\r\n async run(input: DetectorRunInput): Promise<DetectorResult> {\r\n const threshold = input.config.thresholds[RATCHET_ID];\r\n const files = await walkFiles(input.cwd, (rel) => isTestFile(rel));\r\n const findings: RatchetFinding[] = [];\r\n for (const file of files) {\r\n const content = await readRelative(input.cwd, file);\r\n for (const hit of inspectFile({ filePath: file, content, threshold })) {\r\n findings.push({\r\n ratchet: RATCHET_ID,\r\n rule: hit.rule,\r\n file,\r\n severity: hit.severity,\r\n message: hit.message,\r\n });\r\n }\r\n }\r\n return {\r\n ratchet: RATCHET_ID,\r\n ran: true,\r\n findings,\r\n filesScanned: files.length,\r\n stub: false,\r\n };\r\n },\r\n};\r\n\r\nexport const __test = {\r\n FILE_ALLOW_RE,\r\n SKIP_ALLOW_RE,\r\n IT_COUNTING_RE,\r\n IT_SKIP_RE,\r\n USER_EVENT_RE,\r\n FIRE_EVENT_RE,\r\n AWAIT_USER_RE,\r\n ANY_MATCHER_RE,\r\n SWALLOWING_CATCH_RE,\r\n CATCH_RETURN_TRUE_RE,\r\n EMPTY_IT_RE,\r\n FLOOR_BY_THRESHOLD,\r\n};\r\n", "// qa-honesty detector \u2014 flags tests that \"pass on weak evidence.\"\r\n//\r\n// Ported from scripts/ci/check-vo-qa-honesty-core.mjs (the catch-all rule\r\n// pack \u2014 patterns that don't fit cleanly under assertion-strength or\r\n// hollow-tests but represent real institutional pain). Per handoff \u00A75.5.\r\n//\r\n// What the source-of-truth is hand-tuned for:\r\n// The Algosuite implementation hard-coded a few things that the generic\r\n// port deliberately strips:\r\n// a. The file glob limited inspection to `scripts/virtual-office/qa/testers/`.\r\n// Generic port: file selection is project-agnostic \u2014 any `.test.*` /\r\n// `.spec.*` file plus any `.mjs` file matching `tester` in its name\r\n// (the Algosuite tester convention). Strict consumers should override\r\n// with explicit `paths` config.\r\n// b. The allow-marker classes were Algosuite-internal symptoms\r\n// (`vo-qa-allow-permission-denied`, `vo-qa-allow-quota`, etc.).\r\n// Generic port: the marker family stays \u2014 the words \"PERMISSION_DENIED\"\r\n// and \"quota\" are generic CS terms, not Algosuite-specific. Consumers\r\n// without those error codes simply won't trigger the rules.\r\n// c. The \"verify-named-resource\" rule that whitelisted Algosuite-specific\r\n// resource names (`classId`, `studentId`, `pollId`, etc.) is dropped.\r\n// It was a denylist of failure modes specific to Algosuite's data\r\n// model; reproducing it generically would either be too broad\r\n// (every variable name) or too narrow (a single project's nouns).\r\n//\r\n// Rules ported (each fires once per offending line):\r\n//\r\n// reporter-pass-without-evidence test calls `reporter.pass(...)` with no\r\n// prior assertion / readback in scope.\r\n// catch-return-true catch block where the error is logged\r\n// or swallowed and `return true` follows\r\n// \u2014 INVALID_ARGUMENT errors become\r\n// passing tests.\r\n// render-text-only React render() followed only by\r\n// `toBeInTheDocument` / `toHaveTextContent`\r\n// and no user interaction. (gated by\r\n// componentTest file class.)\r\n// fire-event-without-user-event `fireEvent.X(...)` with no `userEvent`\r\n// anywhere in the same file.\r\n// loose-truthy-result `expect(result).toBeTruthy()` where\r\n// `result` was just assigned from a\r\n// callable under test \u2014 proves the\r\n// callable returned something, not the\r\n// right something.\r\n// invalid-argument-pass-risk catch path or guard branch where\r\n// INVALID_ARGUMENT errors are paired\r\n// with `return true`.\r\n// snapshot-heavy file whose ONLY assertions are\r\n// `toMatchSnapshot` / `toMatchInlineSnapshot`.\r\n//\r\n// Allowlist marker (file-scope, before the first import):\r\n// // vo-ratchets-allow-qa-honesty: <reason>\r\n//\r\n// Allowlist marker (line-scope, on the line or up to 3 above):\r\n// // qa-honesty-allow: <reason>\r\n// // vo-qa-allow-<class>: <reason> (Algosuite-compatible)\r\n//\r\n// Edge cases handled:\r\n// - `*.smoke.test.tsx` files are exempt from render-text-only (intentional\r\n// smoke tests; matches Algosuite source-of-truth behavior).\r\n// - Snapshot-heavy is suppressed if any value-comparing matcher exists\r\n// anywhere in the file.\r\n\r\nimport type {\r\n Detector,\r\n DetectorResult,\r\n DetectorRunInput,\r\n RatchetFinding,\r\n RatchetSeverity,\r\n RatchetThreshold,\r\n} from '../types.js';\r\nimport {\r\n isComponentTestFile,\r\n isTestFile,\r\n maskStringsAndComments,\r\n} from '../util/test-files.js';\r\nimport { readRelative, walkFiles } from '../util/walk.js';\r\n\r\nconst RATCHET_ID = 'qa-honesty' as const;\r\n\r\n/**\r\n * Linear-time predicate equivalent to the former /tester[^/]*\\.mjs$/u regex.\r\n * Used by `inspectFile` (single-file check) and the detector's directory\r\n * walker (rel-path filter). CodeQL #195 + #196 flagged the regex as ReDoS-\r\n * prone because `[^/]*` followed by an anchored `\\.mjs$` suffix backtracks\r\n * quadratically on long non-`.mjs` filenames. Pure string ops are linear\r\n * and produce exactly the same result for this specific predicate.\r\n */\r\nfunction isTesterMjsPath(filePath: string): boolean {\r\n const lc = filePath.toLowerCase();\r\n if (!lc.endsWith('.mjs')) return false;\r\n const slash = lc.lastIndexOf('/');\r\n const basename = slash >= 0 ? lc.slice(slash + 1) : lc;\r\n return basename.includes('tester');\r\n}\r\n\r\n// File-scope and line-scope allow markers.\r\nconst FILE_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-qa-honesty\\s*:\\s*\\S/iu;\r\nconst LINE_ALLOW_GENERIC_RE = /\\b(?:qa-honesty-allow|vo-qa-allow-[a-z0-9-]+)\\s*:\\s*\\S/iu;\r\n\r\n// Rule patterns.\r\nconst REPORTER_PASS_RE = /\\breporter\\s*\\.\\s*pass\\s*\\(/u;\r\nconst ASSERTION_RE = /\\b(?:expect|assert(?:\\.ok|Slow|Quality)?)\\s*\\(/u;\r\n// CodeQL #197: original pattern had nested quantifiers\r\n// (`(?:[^{}\\n]*\\n?\\s*)*`) \u2014 `\\s*` inside the outer `*` is the classic\r\n// exponential-backtracking shape. The replacement uses a single lazy\r\n// `[^{}]*?` (no inner quantifier, no nesting) that still matches across\r\n// newlines because `[^{}]` does not exclude `\\n`. It stops at the next\r\n// brace, so a real catch block returning `true` between `{` and `}` is\r\n// matched; non-matching blocks fail in linear time.\r\nconst CATCH_RETURN_TRUE_RE =\r\n /catch\\s*(?:\\([^)]*\\))?\\s*\\{[^{}]*?\\breturn\\s+true\\b/u;\r\nconst RENDER_RE = /\\brender\\s*\\(/u;\r\nconst USER_EVENT_RE = /\\buserEvent\\s*\\.\\s*\\w+\\s*\\(/u;\r\nconst FIRE_EVENT_RE = /\\bfireEvent\\s*\\.\\s*\\w+\\s*\\(/u;\r\nconst TO_BE_IN_DOCUMENT_RE = /\\.toBeInTheDocument\\s*\\(\\s*\\)/u;\r\nconst TO_HAVE_TEXT_CONTENT_RE = /\\.toHaveTextContent\\s*\\(/u;\r\nconst TRUTHY_RE = /\\bexpect\\s*\\(\\s*([A-Za-z_$][\\w$]*)\\s*\\)\\s*\\.\\s*toBeTruthy\\s*\\(\\s*\\)/u;\r\nconst CALLABLE_RESULT_ASSIGN_RE =\r\n /\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*await\\s+[A-Za-z_$][\\w$.]*\\s*\\(/u;\r\nconst INVALID_ARGUMENT_RETURN_TRUE_RE = /\\bINVALID_ARGUMENT\\b[^;\\n]*return\\s+true/iu;\r\nconst SNAPSHOT_MATCHER_RE = /\\.(?:toMatchSnapshot|toMatchInlineSnapshot)\\s*\\(/u;\r\nconst VALUE_COMPARING_MATCHER_RE =\r\n /\\.(?:toBe|toEqual|toStrictEqual|toBeCloseTo|toContain|toHaveLength|toMatch|toMatchObject|toHaveProperty)\\s*\\(/u;\r\nconst SMOKE_TEST_RE = /\\.smoke\\.test\\.(?:ts|tsx|js|jsx|mjs|cjs)$/u;\r\n\r\ninterface RuleHit {\r\n rule: string;\r\n message: string;\r\n severity: RatchetSeverity;\r\n line?: number;\r\n}\r\n\r\nexport interface InspectFileOptions {\r\n filePath: string;\r\n content: string;\r\n threshold: RatchetThreshold;\r\n}\r\n\r\nexport function hasFileAllowMarker(content: string): boolean {\r\n return FILE_ALLOW_RE.test(content);\r\n}\r\n\r\nexport function hasLineAllowMarkerNearby(\r\n lines: readonly string[],\r\n index: number,\r\n): boolean {\r\n const start = Math.max(0, index - 3);\r\n for (let cursor = start; cursor <= index; cursor += 1) {\r\n if (LINE_ALLOW_GENERIC_RE.test(lines[cursor] ?? '')) return true;\r\n }\r\n return false;\r\n}\r\n\r\nexport function inspectFile({\r\n filePath,\r\n content,\r\n threshold,\r\n}: InspectFileOptions): RuleHit[] {\r\n // qa-honesty also applies to Tier 3 sentinel `.mjs` files that aren't\r\n // *.test.ts. We accept any test file OR any `.mjs` whose name contains\r\n // `tester` (a common project convention for Tier 3 driver files).\r\n // CodeQL #195: replaced /tester[^/]*\\.mjs$/ \u2014 the `[^/]*` followed by an\r\n // anchored suffix can backtrack quadratically on long non-`.mjs` filenames.\r\n // String ops are linear and equivalent for this exact predicate.\r\n const isTesterMjs = isTesterMjsPath(filePath);\r\n if (!isTestFile(filePath) && !isTesterMjs) return [];\r\n if (hasFileAllowMarker(content)) return [];\r\n\r\n const masked = maskStringsAndComments(content);\r\n const maskedLines = masked.split(/\\r?\\n/u);\r\n const rawLines = content.split(/\\r?\\n/u);\r\n const hits: RuleHit[] = [];\r\n\r\n // reporter-pass-without-evidence \u2014 flagged once per `reporter.pass(...)` line\r\n // when no `expect(` or `assert(` appears in the file at all.\r\n const fileHasAssertion = ASSERTION_RE.test(masked);\r\n for (let i = 0; i < maskedLines.length; i += 1) {\r\n const maskedLine = maskedLines[i] ?? '';\r\n if (!REPORTER_PASS_RE.test(maskedLine)) continue;\r\n if (fileHasAssertion) continue;\r\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\r\n hits.push({\r\n rule: 'reporter-pass-without-evidence',\r\n severity: 'error',\r\n line: i + 1,\r\n message:\r\n '`reporter.pass(...)` called but no `expect(...)` / `assert(...)` appears '\r\n + 'anywhere in this file. Reporting success without proving it is a fake '\r\n + 'green. Add at least one value-comparing assertion before the pass call.',\r\n });\r\n }\r\n\r\n // catch-return-true \u2014 entire-file regex (multi-line shape).\r\n if (CATCH_RETURN_TRUE_RE.test(masked)) {\r\n // Locate the first line that contains the `return true` to give a useful\r\n // line number.\r\n let firstHit: number | undefined;\r\n for (let i = 0; i < maskedLines.length; i += 1) {\r\n if (/\\breturn\\s+true\\b/u.test(maskedLines[i] ?? '')) {\r\n firstHit = i + 1;\r\n break;\r\n }\r\n }\r\n const hitLine = firstHit ?? 1;\r\n if (firstHit === undefined || !hasLineAllowMarkerNearby(rawLines, firstHit - 1)) {\r\n hits.push({\r\n rule: 'catch-return-true',\r\n severity: 'error',\r\n line: hitLine,\r\n message:\r\n 'A catch block returns `true`. Swallowing exceptions turns errors '\r\n + '(including INVALID_ARGUMENT) into passing tests. Either assert on '\r\n + 'the expected error or let the exception bubble. To exempt this '\r\n + 'block, add `// qa-honesty-allow: <reason>`.',\r\n });\r\n }\r\n }\r\n\r\n // render-text-only \u2014 only for component test files (`.test.tsx`), and only\r\n // when threshold !== 'permissive'. Also skip `*.smoke.test.tsx`.\r\n if (\r\n isComponentTestFile(filePath)\r\n && !SMOKE_TEST_RE.test(filePath)\r\n && threshold !== 'permissive'\r\n ) {\r\n const hasRender = RENDER_RE.test(masked);\r\n const hasInteraction = USER_EVENT_RE.test(masked) || FIRE_EVENT_RE.test(masked);\r\n const onlyTextMatchers =\r\n (TO_BE_IN_DOCUMENT_RE.test(masked) || TO_HAVE_TEXT_CONTENT_RE.test(masked))\r\n && !VALUE_COMPARING_MATCHER_RE.test(masked);\r\n if (hasRender && !hasInteraction && onlyTextMatchers) {\r\n hits.push({\r\n rule: 'render-text-only',\r\n severity: 'warning',\r\n message:\r\n 'render() followed by `toBeInTheDocument` / `toHaveTextContent` and '\r\n + 'nothing else \u2014 Tier-1 smoke parading as Tier-2 verification. Either '\r\n + 'add user interaction (`await user.click(...)`) and a value-comparing '\r\n + 'matcher, or rename the file to `*.smoke.test.tsx` to mark it as '\r\n + 'intentional smoke.',\r\n });\r\n }\r\n }\r\n\r\n // fire-event-without-user-event \u2014 fireEvent.X(...) with no userEvent.\r\n if (FIRE_EVENT_RE.test(masked) && !USER_EVENT_RE.test(masked)) {\r\n // Locate first fireEvent line.\r\n let firstLine: number | undefined;\r\n for (let i = 0; i < maskedLines.length; i += 1) {\r\n if (FIRE_EVENT_RE.test(maskedLines[i] ?? '')) {\r\n firstLine = i + 1;\r\n break;\r\n }\r\n }\r\n if (\r\n firstLine === undefined\r\n || !hasLineAllowMarkerNearby(rawLines, firstLine - 1)\r\n ) {\r\n hits.push({\r\n rule: 'fire-event-without-user-event',\r\n severity: threshold === 'strict' ? 'error' : 'warning',\r\n ...(firstLine ? { line: firstLine } : {}),\r\n message:\r\n '`fireEvent.X(...)` used without any `userEvent.X(...)` in the same '\r\n + 'file. userEvent models real human interaction (focus, debounce, '\r\n + 'pointer events); fireEvent short-circuits that. Prefer userEvent '\r\n + 'unless the event genuinely cannot be modeled (in which case add '\r\n + 'a `// qa-honesty-allow: <reason>` marker).',\r\n });\r\n }\r\n }\r\n\r\n // loose-truthy-result \u2014 `expect(result).toBeTruthy()` where `result` was\r\n // assigned from an awaited call earlier in the file.\r\n const callableVars = new Set<string>();\r\n for (const line of maskedLines) {\r\n const match = CALLABLE_RESULT_ASSIGN_RE.exec(line);\r\n if (match) callableVars.add(match[1] ?? '');\r\n }\r\n for (let i = 0; i < maskedLines.length; i += 1) {\r\n const maskedLine = maskedLines[i] ?? '';\r\n const truthyMatch = TRUTHY_RE.exec(maskedLine);\r\n if (!truthyMatch) continue;\r\n const variable = truthyMatch[1];\r\n if (!variable || !callableVars.has(variable)) continue;\r\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\r\n hits.push({\r\n rule: 'loose-truthy-result',\r\n severity: 'error',\r\n line: i + 1,\r\n message:\r\n `\\`expect(${variable}).toBeTruthy()\\` where \\`${variable}\\` was assigned `\r\n + 'from an awaited call. Truthy proves the callable returned something, '\r\n + 'not the right something. Assert on specific fields or values from '\r\n + 'the result.',\r\n });\r\n }\r\n\r\n // invalid-argument-pass-risk \u2014 `INVALID_ARGUMENT ... return true` patterns.\r\n // Hybrid approach: maskStringsAndComments() blanks out string-literal\r\n // contents (so `'INVALID_ARGUMENT'` is gone from maskedLines), but we need\r\n // the literal as the search signal. Read rawLines but STRIP line-comments\r\n // (everything from `//` onward) before matching, so a comment that\r\n // mentions both INVALID_ARGUMENT and return true doesn't fire (see test\r\n // \"does not flag a file that has comments containing INVALID_ARGUMENT\").\r\n // Block-comment edge cases are not handled here; switch to per-line\r\n // masked + raw-string side-channel if a real false-positive emerges.\r\n for (let i = 0; i < rawLines.length; i += 1) {\r\n const rawLine = rawLines[i] ?? '';\r\n const slashIdx = rawLine.indexOf('//');\r\n const codeOnly = slashIdx >= 0 ? rawLine.slice(0, slashIdx) : rawLine;\r\n if (!INVALID_ARGUMENT_RETURN_TRUE_RE.test(codeOnly)) continue;\r\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\r\n hits.push({\r\n rule: 'invalid-argument-pass-risk',\r\n severity: 'error',\r\n line: i + 1,\r\n message:\r\n 'INVALID_ARGUMENT error path returns `true`. This turns a validation '\r\n + 'failure into a passing test \u2014 the system rejected the call but '\r\n + 'the test claims pass. Assert on the rejection explicitly.',\r\n });\r\n }\r\n\r\n // snapshot-heavy \u2014 file's only assertions are snapshot matchers.\r\n const snapshotMatchers = (masked.match(/\\.(?:toMatchSnapshot|toMatchInlineSnapshot)\\s*\\(/gu) || []).length;\r\n const valueMatchers = (masked.match(/\\.(?:toBe|toEqual|toStrictEqual|toBeCloseTo|toContain|toHaveLength|toMatch|toMatchObject|toHaveProperty)\\s*\\(/gu) || []).length;\r\n if (\r\n snapshotMatchers > 0\r\n && valueMatchers === 0\r\n && threshold !== 'permissive'\r\n ) {\r\n hits.push({\r\n rule: 'snapshot-heavy',\r\n severity: 'warning',\r\n message:\r\n `Every assertion in this file is a snapshot (${snapshotMatchers} `\r\n + 'toMatchSnapshot / toMatchInlineSnapshot). Snapshots detect drift '\r\n + 'but rarely prove the verified product answer. Pair with at least '\r\n + 'one value-comparing matcher (toEqual / toBe / toMatchObject).',\r\n });\r\n }\r\n\r\n return hits;\r\n}\r\n\r\nexport const qaHonestyDetector: Detector = {\r\n id: RATCHET_ID,\r\n stub: false,\r\n async run(input: DetectorRunInput): Promise<DetectorResult> {\r\n const threshold = input.config.thresholds[RATCHET_ID];\r\n const files = await walkFiles(input.cwd, (rel) => {\r\n if (isTestFile(rel)) return true;\r\n // tester *.mjs files (Tier 3 sentinel convention). CodeQL #196: same\r\n // ReDoS-prone pattern as #195 \u2014 replaced with linear string ops.\r\n return isTesterMjsPath(rel);\r\n });\r\n const findings: RatchetFinding[] = [];\r\n for (const file of files) {\r\n const content = await readRelative(input.cwd, file);\r\n for (const hit of inspectFile({ filePath: file, content, threshold })) {\r\n findings.push({\r\n ratchet: RATCHET_ID,\r\n rule: hit.rule,\r\n file,\r\n severity: hit.severity,\r\n message: hit.message,\r\n ...(typeof hit.line === 'number' ? { line: hit.line } : {}),\r\n });\r\n }\r\n }\r\n return {\r\n ratchet: RATCHET_ID,\r\n ran: true,\r\n findings,\r\n filesScanned: files.length,\r\n stub: false,\r\n };\r\n },\r\n};\r\n\r\nexport const __test = {\r\n FILE_ALLOW_RE,\r\n LINE_ALLOW_GENERIC_RE,\r\n REPORTER_PASS_RE,\r\n CATCH_RETURN_TRUE_RE,\r\n RENDER_RE,\r\n USER_EVENT_RE,\r\n FIRE_EVENT_RE,\r\n TO_BE_IN_DOCUMENT_RE,\r\n TRUTHY_RE,\r\n CALLABLE_RESULT_ASSIGN_RE,\r\n INVALID_ARGUMENT_RETURN_TRUE_RE,\r\n SNAPSHOT_MATCHER_RE,\r\n VALUE_COMPARING_MATCHER_RE,\r\n};\r\n", "// verify-answer detector \u2014 flags Tier 2/3 assertions that don't pin an\r\n// expected value.\r\n//\r\n// Ported from scripts/ci/check-verify-answer-core.mjs (the source-of-truth\r\n// in the Algosuite tree). The \"honest\" view per handoff \u00A75.3 is: a test\r\n// claims to verify the product produced the right answer. Comparator-only\r\n// matchers (`expect(x).toBeGreaterThan(0)`, `expect(x).toBeTruthy()`) prove\r\n// the system returned SOMETHING but don't carry the right answer, so when\r\n// they fail the orchestrator/fixer has no expected value to compare against.\r\n//\r\n// Rule set (each one fires once per offending line):\r\n//\r\n// comparator-only a `.toBeGreaterThan(...)`, `.toBeLessThan(...)`,\r\n// `.toBeTruthy()`, `.toBeFalsy()` (and the\r\n// ...OrEqual variants) appears without ANY pinning\r\n// matcher (`.toBe(<lit>)`, `.toEqual(<lit>)`,\r\n// `.toMatchObject(...)`, `.toStrictEqual(...)`,\r\n// `.toBeCloseTo(...)`, `.toMatchInlineSnapshot(...)`)\r\n// or `verify*()` family helper on the same line.\r\n// call-succeeded-only a test body contains `await <expr>()` followed\r\n// by `expect(true).toBe(true)` or no assertion at\r\n// all \u2014 claims verification but only proves the\r\n// call didn't throw. (Per handoff \u00A75.3 \u2014 this is\r\n// the strongest \"honest claim, weak evidence\" signal.)\r\n//\r\n// Pinning matchers and verifyAnswer-style helpers are configurable via\r\n// `extraPinningHelpers` (the Algosuite tree adds `verifyAnswer`, `verifyMatch`,\r\n// `gradeAIOutput`, etc. \u2014 generic projects ship with the base set only).\r\n//\r\n// Allowlist marker (file-scope, anywhere before the first import):\r\n// // vo-ratchets-allow-verify-answer: <reason>\r\n//\r\n// Allowlist marker (single line, on or up to 3 lines above the offending line):\r\n// // verify-answer-allow: <reason>\r\n\r\nimport type {\r\n Detector,\r\n DetectorResult,\r\n DetectorRunInput,\r\n RatchetFinding,\r\n RatchetSeverity,\r\n RatchetThreshold,\r\n} from '../types.js';\r\nimport { isTestFile, maskStringsAndComments } from '../util/test-files.js';\r\nimport { readRelative, walkFiles } from '../util/walk.js';\r\n\r\nconst RATCHET_ID = 'verify-answer' as const;\r\n\r\nconst FILE_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-verify-answer\\s*:\\s*\\S/iu;\r\nconst LINE_ALLOW_RE = /\\bverify-answer-allow\\s*:\\s*\\S/iu;\r\n\r\nconst COMPARATOR_PATTERNS: ReadonlyArray<{ re: RegExp; name: string }> = [\r\n { re: /\\.toBeGreaterThan\\s*\\(/u, name: 'toBeGreaterThan' },\r\n { re: /\\.toBeGreaterThanOrEqual\\s*\\(/u, name: 'toBeGreaterThanOrEqual' },\r\n { re: /\\.toBeLessThan\\s*\\(/u, name: 'toBeLessThan' },\r\n { re: /\\.toBeLessThanOrEqual\\s*\\(/u, name: 'toBeLessThanOrEqual' },\r\n { re: /\\.toBeTruthy\\s*\\(\\s*\\)/u, name: 'toBeTruthy' },\r\n { re: /\\.toBeFalsy\\s*\\(\\s*\\)/u, name: 'toBeFalsy' },\r\n];\r\n\r\n// Pinning matchers \u2014 a comparator-only finding is suppressed if any of these\r\n// appears on the same line (chained matcher / multi-expectation per line).\r\nconst DEFAULT_PINNING_PATTERNS: ReadonlyArray<RegExp> = [\r\n /\\.toBe\\s*\\(\\s*(?:-?\\d+(?:\\.\\d+)?|true|false|null|undefined|\"[^\"\\n]*\"|'[^'\\n]*'|`[^`\\n]*`)/u,\r\n /\\.toEqual\\s*\\(\\s*(?:-?\\d+(?:\\.\\d+)?|true|false|null|undefined|\"[^\"\\n]*\"|'[^'\\n]*'|`[^`\\n]*`|\\[|\\{)/u,\r\n /\\.toStrictEqual\\s*\\(/u,\r\n /\\.toBeCloseTo\\s*\\(/u,\r\n /\\.toMatchObject\\s*\\(/u,\r\n /\\.toMatchInlineSnapshot\\s*\\(/u,\r\n];\r\n\r\n// The \"call-succeeded-only\" rule's defining shape: an `expect(true).toBe(true)`\r\n// or `assert(true)` line appears in a file that ALSO contains at least one\r\n// `await ...(...)` callsite \u2014 the test executed something but verifies nothing.\r\nconst LITERAL_TRUE_ASSERT_RE = /\\b(?:expect|assert(?:\\.ok)?)\\s*\\(\\s*true\\s*\\)\\s*(?:\\.\\s*toBe\\s*\\(\\s*true\\s*\\)\\s*)?/u;\r\nconst AWAIT_CALL_RE = /\\bawait\\s+[A-Za-z_$][\\w$.]*\\s*\\(/u;\r\n\r\ninterface RuleHit {\r\n rule: string;\r\n message: string;\r\n severity: RatchetSeverity;\r\n line?: number;\r\n}\r\n\r\nexport interface InspectFileOptions {\r\n filePath: string;\r\n content: string;\r\n threshold: RatchetThreshold;\r\n /**\r\n * Extra helper names that count as pinning the expected value (e.g.\r\n * Algosuite's `verifyAnswer`, `gradeAIOutput`). Each entry is a bare\r\n * identifier; the detector compiles `\\b<name>\\s*\\(` for matching.\r\n */\r\n extraPinningHelpers?: readonly string[];\r\n}\r\n\r\nexport function hasFileAllowMarker(content: string): boolean {\r\n return FILE_ALLOW_RE.test(content);\r\n}\r\n\r\nfunction lineHasPinning(\r\n line: string,\r\n extraHelpers: readonly string[],\r\n): boolean {\r\n for (const pattern of DEFAULT_PINNING_PATTERNS) {\r\n if (pattern.test(line)) return true;\r\n }\r\n for (const helper of extraHelpers) {\r\n // Defensive: skip malformed identifiers so a user-supplied bad value\r\n // doesn't blow up regex compilation.\r\n if (!/^[A-Za-z_$][\\w$]*$/u.test(helper)) continue;\r\n if (new RegExp(`\\\\b${helper}\\\\s*\\\\(`, 'u').test(line)) return true;\r\n }\r\n return false;\r\n}\r\n\r\nfunction hasLineAllowMarkerNearby(lines: readonly string[], index: number): boolean {\r\n const start = Math.max(0, index - 3);\r\n for (let cursor = start; cursor <= index; cursor += 1) {\r\n if (LINE_ALLOW_RE.test(lines[cursor] ?? '')) return true;\r\n }\r\n return false;\r\n}\r\n\r\nexport function inspectFile({\r\n filePath,\r\n content,\r\n threshold,\r\n extraPinningHelpers = [],\r\n}: InspectFileOptions): RuleHit[] {\r\n if (!isTestFile(filePath)) return [];\r\n if (hasFileAllowMarker(content)) return [];\r\n\r\n const hits: RuleHit[] = [];\r\n const masked = maskStringsAndComments(content);\r\n const maskedLines = masked.split(/\\r?\\n/u);\r\n const rawLines = content.split(/\\r?\\n/u);\r\n\r\n // comparator-only: scan line by line.\r\n for (let i = 0; i < maskedLines.length; i += 1) {\r\n const maskedLine = maskedLines[i] ?? '';\r\n if (!maskedLine.trim()) continue;\r\n let matchedName: string | null = null;\r\n for (const { re, name } of COMPARATOR_PATTERNS) {\r\n if (re.test(maskedLine)) {\r\n matchedName = name;\r\n break;\r\n }\r\n }\r\n if (!matchedName) continue;\r\n if (lineHasPinning(maskedLine, extraPinningHelpers)) continue;\r\n // Allow-marker is checked against raw lines (markers live inside comments\r\n // that the mask strips).\r\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\r\n\r\n hits.push({\r\n rule: 'comparator-only',\r\n severity: threshold === 'permissive' ? 'warning' : 'error',\r\n line: i + 1,\r\n message:\r\n `Comparator-only matcher \\`.${matchedName}\\` does not pin an expected value. `\r\n + 'A failure here can\\'t tell the orchestrator the right answer. Use '\r\n + '`.toBe(<literal>)`, `.toEqual(<literal>)`, `.toBeCloseTo(<value>)`, or '\r\n + '`.toMatchObject(...)` instead. To exempt this line, add '\r\n + '`// verify-answer-allow: <reason>` on the line above.',\r\n });\r\n }\r\n\r\n // call-succeeded-only: any `expect(true).toBe(true)` / `assert(true)` style\r\n // line in a file that also issues a call. Fires once per offending line.\r\n const fileHasAwaitCall = AWAIT_CALL_RE.test(masked);\r\n if (fileHasAwaitCall && threshold !== 'permissive') {\r\n for (let i = 0; i < maskedLines.length; i += 1) {\r\n const maskedLine = maskedLines[i] ?? '';\r\n if (!LITERAL_TRUE_ASSERT_RE.test(maskedLine)) continue;\r\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\r\n hits.push({\r\n rule: 'call-succeeded-only',\r\n severity: 'error',\r\n line: i + 1,\r\n message:\r\n 'Found `expect(true).toBe(true)` / `assert(true)` in a file that '\r\n + 'issues a call. The test proves the callable invocation didn\\'t '\r\n + 'throw \u2014 not that it produced the right answer. Replace with a '\r\n + 'value-comparing matcher against the actual output.',\r\n });\r\n }\r\n }\r\n\r\n return hits;\r\n}\r\n\r\nexport const verifyAnswerDetector: Detector = {\r\n id: RATCHET_ID,\r\n stub: false,\r\n async run(input: DetectorRunInput): Promise<DetectorResult> {\r\n const threshold = input.config.thresholds[RATCHET_ID];\r\n const files = await walkFiles(input.cwd, (rel) => isTestFile(rel));\r\n const findings: RatchetFinding[] = [];\r\n for (const file of files) {\r\n const content = await readRelative(input.cwd, file);\r\n for (const hit of inspectFile({ filePath: file, content, threshold })) {\r\n findings.push({\r\n ratchet: RATCHET_ID,\r\n rule: hit.rule,\r\n file,\r\n severity: hit.severity,\r\n message: hit.message,\r\n ...(typeof hit.line === 'number' ? { line: hit.line } : {}),\r\n });\r\n }\r\n }\r\n return {\r\n ratchet: RATCHET_ID,\r\n ran: true,\r\n findings,\r\n filesScanned: files.length,\r\n stub: false,\r\n };\r\n },\r\n};\r\n\r\nexport const __test = {\r\n FILE_ALLOW_RE,\r\n LINE_ALLOW_RE,\r\n COMPARATOR_PATTERNS,\r\n DEFAULT_PINNING_PATTERNS,\r\n LITERAL_TRUE_ASSERT_RE,\r\n AWAIT_CALL_RE,\r\n};\r\n", "// Detector registry. Order matches the `RatchetId` enum so iteration is stable.\r\n\r\nimport type { Detector } from '../types.js';\r\nimport { assertionStrengthDetector } from './assertion-strength.js';\r\nimport { fixStrengthDetector } from './fix-strength.js';\r\nimport { hollowTestsDetector } from './hollow-tests.js';\r\nimport { qaHonestyDetector } from './qa-honesty.js';\r\nimport { verifyAnswerDetector } from './verify-answer.js';\r\n\r\nexport const ALL_DETECTORS: readonly Detector[] = [\r\n assertionStrengthDetector,\r\n hollowTestsDetector,\r\n verifyAnswerDetector,\r\n fixStrengthDetector,\r\n qaHonestyDetector,\r\n] as const;\r\n\r\nexport {\r\n assertionStrengthDetector,\r\n fixStrengthDetector,\r\n hollowTestsDetector,\r\n qaHonestyDetector,\r\n verifyAnswerDetector,\r\n};\r\n", "// runRatchets \u2014 the programmatic API entry point.\r\n//\r\n// Orchestrates: config resolution -> detector dispatch -> allowlist filter ->\r\n// report assembly. This is the single function the cloud control plane,\r\n// MCP server, and CLI all call.\r\n\r\nimport { applyOnlyFilter, resolveConfig } from './config.js';\r\nimport { ALL_DETECTORS } from './detectors/index.js';\r\nimport type {\r\n DetectorResult,\r\n RatchetFinding,\r\n RatchetReport,\r\n RunRatchetsOptions,\r\n} from './types.js';\r\nimport { pathMatchesAny } from './util/walk.js';\r\n\r\n/**\r\n * Run every enabled ratchet against the project at `options.cwd`. Returns a\r\n * `RatchetReport` whose `failed` flag is true iff at least one\r\n * non-allowlisted `error`-severity finding remains and `reportOnly` is false.\r\n *\r\n * Never throws on detector-internal errors \u2014 those surface as `findings`\r\n * with severity `error`, not as exceptions. Caller-shape errors (bad\r\n * config) DO throw \u2014 see `resolveConfig`.\r\n */\r\nexport async function runRatchets(\r\n options: RunRatchetsOptions,\r\n): Promise<RatchetReport> {\r\n const startedAtMs = Date.now();\r\n const startedAt = new Date(startedAtMs).toISOString();\r\n const resolved = applyOnlyFilter(resolveConfig(options.config), options.only);\r\n\r\n const detectorResults: DetectorResult[] = [];\r\n for (const detector of ALL_DETECTORS) {\r\n if (!resolved.enabled[detector.id]) {\r\n detectorResults.push({\r\n ratchet: detector.id,\r\n ran: false,\r\n findings: [],\r\n filesScanned: 0,\r\n stub: detector.stub,\r\n });\r\n continue;\r\n }\r\n const result = await detector.run({ cwd: options.cwd, config: resolved });\r\n detectorResults.push(result);\r\n }\r\n\r\n const allFindings: RatchetFinding[] = detectorResults.flatMap((r) => r.findings);\r\n const { kept, suppressed } = partitionByAllowlist(allFindings, resolved.allowlist);\r\n\r\n const hasErrorFinding = kept.some((f) => f.severity === 'error');\r\n const failed = !resolved.reportOnly && hasErrorFinding;\r\n\r\n return {\r\n startedAt,\r\n durationMs: Date.now() - startedAtMs,\r\n cwd: options.cwd,\r\n config: resolved,\r\n detectors: detectorResults,\r\n findings: kept,\r\n suppressedFindings: suppressed,\r\n failed,\r\n };\r\n}\r\n\r\nfunction partitionByAllowlist(\r\n findings: readonly RatchetFinding[],\r\n allowlist: { paths: readonly string[]; rules: readonly string[] },\r\n): { kept: RatchetFinding[]; suppressed: RatchetFinding[] } {\r\n const kept: RatchetFinding[] = [];\r\n const suppressed: RatchetFinding[] = [];\r\n for (const finding of findings) {\r\n if (pathMatchesAny(finding.file, allowlist.paths)) {\r\n suppressed.push(finding);\r\n continue;\r\n }\r\n if (allowlist.rules.includes(finding.rule)) {\r\n suppressed.push(finding);\r\n continue;\r\n }\r\n if (allowlist.rules.includes(`${finding.ratchet}:${finding.rule}`)) {\r\n suppressed.push(finding);\r\n continue;\r\n }\r\n kept.push(finding);\r\n }\r\n return { kept, suppressed };\r\n}\r\n\r\nexport const __test = {\r\n partitionByAllowlist,\r\n};\r\n", "/**\r\n * Tool: `vo_check_ratchets`\r\n *\r\n * Runs the `@algosuite/vo-ratchets` library against a project directory and\r\n * returns the deterministic detector report. Wraps `runRatchets` \u2014 the\r\n * generic, project-agnostic CI ratchet runner (assertion-strength,\r\n * hollow-tests, verify-answer, fix-strength, qa-honesty).\r\n *\r\n * Plan PR #11 (vo-next-agent-plan-2026-05-23 \u00A711.3) \u2014 Wave 2 prerequisite.\r\n * First MCP tool that closes the loop operator \u2192 MCP \u2192 vo-ratchets moat\r\n * library. The three existing static-ratchet tools\r\n * (`vo_check_assertion_strength`, `vo_check_hollow_test`, `vo_verify_answer`)\r\n * still call the stub ratchet client + consensus engine respectively; a\r\n * follow-up PR (#11.5) will migrate them to call into vo-ratchets too.\r\n *\r\n * Contract:\r\n * input: { cwd?: string, only?: RatchetId, report_only?: boolean, paths?: string[] }\r\n * output: ToolResultEnvelope<CheckRatchetsPayload>\r\n *\r\n * Behavior:\r\n * 1. Validate input shape and resolve cwd default (process.cwd()).\r\n * 2. Call `deps.ratchetsRunner({ cwd, config, only })`.\r\n * 3. Project the resulting RatchetReport into the open-shell payload.\r\n * 4. Write one consensus-call event (gate #7) with gate_type='ratchet'.\r\n *\r\n * Cache: deliberately bypassed. RatchetReport is a function of the\r\n * FILESYSTEM at `cwd` \u2014 input hash alone does not uniquely determine the\r\n * output. Returning cached results would mask file changes between calls.\r\n * The event log still records every invocation for the dataset moat.\r\n */\r\nimport { runRatchets } from '@algosuite/vo-ratchets';\r\nimport type {\r\n CheckRatchetsPayload,\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n} from '../types.js';\r\nimport {\r\n buildBaseEvent,\r\n bytesOf,\r\n invalidParams,\r\n jsonContent,\r\n type ToolInputSchema,\r\n} from './common.js';\r\n\r\nexport const TOOL_NAME = 'vo_check_ratchets';\r\n\r\n/** Static-ratchet tool \u2014 not consensus-engine routed. */\r\nconst GATE_TYPE = 'ratchet' as const;\r\n\r\n/** Canonical detector IDs (mirrors vo-ratchets `RatchetId` \u2014 keep in sync). */\r\nconst ALL_RATCHET_IDS = [\r\n 'assertion-strength',\r\n 'hollow-tests',\r\n 'verify-answer',\r\n 'fix-strength',\r\n 'qa-honesty',\r\n] as const;\r\ntype RatchetIdLocal = (typeof ALL_RATCHET_IDS)[number];\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n cwd: {\r\n type: 'string',\r\n description:\r\n 'Absolute path the ratchet run resolves files against. Defaults to the MCP server process cwd (the directory the operator launched it from \u2014 typically the project root).',\r\n },\r\n only: {\r\n type: 'string',\r\n enum: [...ALL_RATCHET_IDS],\r\n description:\r\n 'Restrict the run to a single detector. Equivalent to disabling every other detector. Omit to run all enabled detectors.',\r\n },\r\n report_only: {\r\n type: 'boolean',\r\n description:\r\n 'When true, the run never reports `failed: true` even on error-severity findings. Useful for diagnostic scans; do NOT set in CI gates.',\r\n },\r\n paths: {\r\n type: 'array',\r\n items: { type: 'string' },\r\n description:\r\n 'Optional list of file globs / paths to scan. When omitted, each detector walks the project and applies its own file filters.',\r\n },\r\n },\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Runs the vo-ratchets library against a project directory and returns the deterministic detector report (assertion-strength, hollow-tests, verify-answer, fix-strength, qa-honesty). Use BEFORE accepting generated tests or fixes to catch hollow assertions, weak verification, and other product-truth bypass patterns. Output includes per-detector findings (file, line, severity, message), suppressed findings (allowlist hits), and a pass/fail signal. Use `only` to scope to one detector for speed. Cache is bypassed \u2014 output reflects the live filesystem state at `cwd`.\";\r\n\r\ninterface ToolInput {\r\n readonly cwd?: string;\r\n readonly only?: RatchetIdLocal;\r\n readonly report_only?: boolean;\r\n readonly paths?: readonly string[];\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (o['cwd'] !== undefined && typeof o['cwd'] !== 'string') return false;\r\n if (o['only'] !== undefined) {\r\n if (typeof o['only'] !== 'string') return false;\r\n if (!ALL_RATCHET_IDS.includes(o['only'] as RatchetIdLocal)) return false;\r\n }\r\n if (o['report_only'] !== undefined && typeof o['report_only'] !== 'boolean') return false;\r\n if (o['paths'] !== undefined) {\r\n if (!Array.isArray(o['paths'])) return false;\r\n if (!o['paths'].every((p) => typeof p === 'string')) return false;\r\n }\r\n return true;\r\n}\r\n\r\nexport async function handleCheckRatchets(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n // Accepted for ToolHandler signature parity. vo-ratchets runs are file-IO\r\n // bound and do not currently honor AbortSignal mid-walk; if a long run\r\n // needs cancellation, follow-up work would thread `signal` into the\r\n // runner's detector loop. Marked underscore-unused for eslint.\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n `invalid input. All fields optional. Shape: { cwd?: string, only?: '${ALL_RATCHET_IDS.join(\"' | '\")}', report_only?: boolean, paths?: string[] }.`,\r\n );\r\n }\r\n\r\n const cwd = rawInput.cwd ?? process.cwd();\r\n const key = deps.cache.keyFor(TOOL_NAME, rawInput);\r\n // Event excerpt: a stable single-line summary of the request shape.\r\n const excerpt = JSON.stringify({\r\n cwd,\r\n only: rawInput.only ?? null,\r\n report_only: rawInput.report_only ?? false,\r\n paths_count: rawInput.paths?.length ?? 0,\r\n }).slice(0, 300);\r\n const inputSizeBytes = bytesOf(JSON.stringify(rawInput));\r\n\r\n // Build the UserRatchetConfig from the optional input fields. Omit\r\n // properties entirely (rather than passing `undefined`) so vo-ratchets'\r\n // default merging behavior applies cleanly.\r\n const config: {\r\n reportOnly?: boolean;\r\n paths?: readonly string[];\r\n } = {};\r\n if (rawInput.report_only !== undefined) config.reportOnly = rawInput.report_only;\r\n if (rawInput.paths !== undefined) config.paths = rawInput.paths;\r\n\r\n const report = await runRatchets({\r\n cwd,\r\n ...(Object.keys(config).length > 0 ? { config: config as never } : {}),\r\n ...(rawInput.only !== undefined ? { only: rawInput.only } : {}),\r\n });\r\n\r\n const payload: CheckRatchetsPayload = {\r\n failed: report.failed,\r\n started_at: report.startedAt,\r\n duration_ms: report.durationMs,\r\n cwd: report.cwd,\r\n detectors: report.detectors.map((d) => ({\r\n ratchet: d.ratchet,\r\n ran: d.ran,\r\n files_scanned: d.filesScanned,\r\n stub: d.stub,\r\n finding_count: d.findings.length,\r\n })),\r\n findings: report.findings.map((f) => ({\r\n ratchet: f.ratchet,\r\n rule: f.rule,\r\n file: f.file,\r\n severity: f.severity,\r\n message: f.message,\r\n ...(f.line !== undefined ? { line: f.line } : {}),\r\n })),\r\n suppressed_findings: report.suppressedFindings.map((f) => ({\r\n ratchet: f.ratchet,\r\n rule: f.rule,\r\n file: f.file,\r\n severity: f.severity,\r\n message: f.message,\r\n ...(f.line !== undefined ? { line: f.line } : {}),\r\n })),\r\n summary: buildSummary(report),\r\n };\r\n\r\n const envelope: ToolResultEnvelope<CheckRatchetsPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n\r\n deps.events.append(\r\n buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n }),\r\n );\r\n\r\n return jsonContent(envelope);\r\n}\r\n\r\nfunction buildSummary(report: {\r\n failed: boolean;\r\n detectors: readonly { ratchet: string; ran: boolean; findings: readonly unknown[] }[];\r\n findings: readonly { severity: string }[];\r\n suppressedFindings: readonly unknown[];\r\n}): string {\r\n const ranCount = report.detectors.filter((d) => d.ran).length;\r\n const errorCount = report.findings.filter((f) => f.severity === 'error').length;\r\n const warnCount = report.findings.filter((f) => f.severity === 'warning').length;\r\n const infoCount = report.findings.filter((f) => f.severity === 'info').length;\r\n const verdict = report.failed ? 'fail' : 'pass';\r\n return (\r\n `verdict=${verdict} detectors_ran=${ranCount}/${report.detectors.length} ` +\r\n `findings=error:${errorCount} warning:${warnCount} info:${infoCount} ` +\r\n `suppressed=${report.suppressedFindings.length}`\r\n );\r\n}\r\n", "/**\r\n * Tool: `vo_decompose_dispatch`\r\n *\r\n * Per-tier dispatch decomposition (Plan PR #14, vo-next-agent-plan-2026-05-23 \u00A76\r\n * Phase 2 / \u00A711.3 Wave 2). Takes a customer goal, returns a structured JSON\r\n * dispatch plan that breaks the work into bounded tiers (research / build /\r\n * verify / ship) with token cost estimates + kill switches + artifacts.\r\n *\r\n * Implemented as a specialization of the consensus-judgment pattern:\r\n * 1. Embed a fixed system prompt (the `decompose-by-subagent-budget`\r\n * doctrine \u2014 Plan PR #7's skill text inlined here so PR #14 stays\r\n * independent of PR #7 per the plan's parallel-PR matrix).\r\n * 2. Call the consensus engine with gate_type='plan-review' so each\r\n * model in the panel produces its own dispatch plan.\r\n * 3. Parse the synthesized verdict's reasoning text as JSON; if it\r\n * parses, surface the structured plan in the payload. Surface ALL\r\n * per-model plan texts too so the operator can audit divergence.\r\n * 4. Emit one V1 gate #7 event with gate_type='plan-review' (the engine\r\n * path) \u2014 consistent with consensus-judgment.\r\n *\r\n * Contract:\r\n * input: { customer_goal: string, context?: Record<string, unknown> }\r\n * output: ToolResultEnvelope<DecomposeDispatchPayload>\r\n *\r\n * Cache:\r\n * Cached on (system_prompt_version, customer_goal, context). Cache HIT\r\n * short-circuits engine. The system prompt is versioned (constant below)\r\n * so doctrine updates flip the cache.\r\n */\r\nimport type {\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n DecomposeDispatchPayload,\r\n DispatchPlan,\r\n ConsensusCallEvent,\r\n} from '../types.js';\r\nimport {\r\n assertWithinByteCap,\r\n buildBaseEvent,\r\n bytesOf,\r\n invalidParams,\r\n jsonContent,\r\n toEventPerModelVerdicts,\r\n toEventSynthesizedVerdict,\r\n type ToolInputSchema,\r\n} from './common.js';\r\n\r\nexport const TOOL_NAME = 'vo_decompose_dispatch';\r\n\r\n/** Forced gate type \u2014 this tool always routes to plan-review. */\r\nconst GATE_TYPE = 'plan-review' as const;\r\n\r\n/** 32 KB customer-goal cap (tighter than consensus-judgment's 64 KB \u2014 goals\r\n * are short by design; large pastes belong in `context`). */\r\nconst MAX_GOAL_BYTES = 32 * 1024;\r\n/** 256 KB context cap (matches consensus-judgment). */\r\nconst MAX_CONTEXT_BYTES = 256 * 1024;\r\n\r\n/**\r\n * System prompt \u2014 the `decompose-by-subagent-budget` doctrine inlined.\r\n *\r\n * Versioned: bumping the suffix flips cache keys so doctrine updates take\r\n * effect on the next call without manual cache eviction.\r\n */\r\nconst SYSTEM_PROMPT_VERSION = 'v1' as const;\r\n\r\nconst SYSTEM_PROMPT = `You are a dispatch architect for an AI agent fleet operated by a non-coder founder. Given a customer goal, produce a STRUCTURED JSON dispatch plan that decomposes the work into bounded tiers and surfaces the cost shape BEFORE execution begins.\r\n\r\nTiers (use only those that apply; omit irrelevant phases):\r\n - research: subagents read code/docs and produce a finding report\r\n - build: subagents implement changes\r\n - verify: subagents run tests / smoke / consensus checks\r\n - ship: a single agent commits + PRs + merges + deploys\r\n\r\nFor each phase you include, specify:\r\n - subagent_count how many parallel finite tasks vs single-threaded (1)\r\n - model_recommendation model id (claude-opus-4, claude-sonnet-4, gpt-4o, etc.)\r\n - estimated_tokens_in integer best-guess input tokens for the phase\r\n - estimated_tokens_out integer best-guess output tokens for the phase\r\n - estimated_cost_usd USD number (or the string \"unknown\" if you cannot estimate)\r\n - kill_switch one-sentence condition that should HALT this phase\r\n - artifacts array of file paths / report names the phase produces\r\n\r\nOutput STRICT JSON conforming to this schema (no markdown fences, no prose outside the JSON):\r\n\r\n{\r\n \"summary\": \"<one-sentence framing of the overall work>\",\r\n \"phases\": [\r\n {\r\n \"name\": \"research\" | \"build\" | \"verify\" | \"ship\",\r\n \"subagent_count\": <integer >= 1>,\r\n \"model_recommendation\": \"<model-id>\",\r\n \"estimated_tokens_in\": <integer>,\r\n \"estimated_tokens_out\": <integer>,\r\n \"estimated_cost_usd\": <number> | \"unknown\",\r\n \"kill_switch\": \"<condition that should halt this phase>\",\r\n \"artifacts\": [\"<file or report this phase produces>\"]\r\n }\r\n ],\r\n \"total_estimated_tokens\": <integer> | \"unknown\",\r\n \"total_estimated_cost_usd\": <number> | \"unknown\",\r\n \"risks\": [\"<risk + suggested mitigation>\"],\r\n \"operator_decision_points\": [\"<question the operator should answer before next phase starts>\"]\r\n}\r\n\r\nHonesty rules (NON-NEGOTIABLE):\r\n - If you cannot estimate a token cost, return the string \"unknown\" \u2014 do NOT bluff a number.\r\n - If a phase requires consensus verification, list it as an artifact dependency on \\`vo_consensus_judgment\\`, not as work-itself.\r\n - The plan is a PROPOSAL, not a commitment \u2014 the operator approves each tier separately.\r\n - Surface at least one operator_decision_point unless the goal is trivially scoped.\r\n - Surface at least one risk + mitigation. If you genuinely see no risk, say \"no significant risk identified; primary failure mode is cost overrun\" and explain why.\r\n - Do NOT pad the plan with phases that add no value. A 1-file change may only need a build phase.`;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n customer_goal: {\r\n type: 'string',\r\n description:\r\n \"The customer's goal statement \u2014 what they want accomplished. Keep concise; large supporting material belongs in `context`.\",\r\n },\r\n context: {\r\n type: 'object',\r\n description:\r\n 'Optional context: relevant file paths, current state observations, constraints, prior decisions, deadlines. Forwarded to the engine as caller_context.',\r\n additionalProperties: true,\r\n },\r\n },\r\n required: ['customer_goal'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Decomposes a customer goal into a structured dispatch plan (research / build / verify / ship tiers) with subagent counts, model recommendations, token cost estimates, kill switches, and per-tier artifacts. Runs the consensus engine with gate_type='plan-review' so multiple models each propose a plan, then surfaces the synthesized JSON plan AND each model's raw plan for operator audit. Use BEFORE starting any non-trivial work to make the cost shape visible up front. Operator approves each tier separately; the plan is a proposal, not a commitment.\";\r\n\r\ninterface ToolInput {\r\n readonly customer_goal: string;\r\n readonly context?: Record<string, unknown>;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['customer_goal'] !== 'string') return false;\r\n if (o['context'] !== undefined && (typeof o['context'] !== 'object' || o['context'] === null)) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\n/**\r\n * Try to parse a model's response text as the dispatch-plan JSON. The\r\n * prompt forbids markdown fences but defensively strip them if present.\r\n * Returns null on any parse failure \u2014 the caller surfaces the failure as\r\n * `parse_error` rather than blowing up.\r\n */\r\nfunction tryParseDispatchPlan(text: string): DispatchPlan | null {\r\n const trimmed = text.trim();\r\n if (trimmed.length === 0) return null;\r\n\r\n // Strip ```json \u2026 ``` fences defensively.\r\n let body = trimmed;\r\n const fenceMatch = body.match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/u);\r\n if (fenceMatch && fenceMatch[1]) body = fenceMatch[1].trim();\r\n\r\n // Find the first { and last } \u2014 handles models that prepend a sentence.\r\n const firstBrace = body.indexOf('{');\r\n const lastBrace = body.lastIndexOf('}');\r\n if (firstBrace === -1 || lastBrace === -1 || lastBrace < firstBrace) return null;\r\n const jsonStr = body.slice(firstBrace, lastBrace + 1);\r\n\r\n try {\r\n const parsed = JSON.parse(jsonStr) as unknown;\r\n if (!isDispatchPlan(parsed)) return null;\r\n return parsed;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nfunction isDispatchPlan(v: unknown): v is DispatchPlan {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['summary'] !== 'string') return false;\r\n if (!Array.isArray(o['phases'])) return false;\r\n if (!Array.isArray(o['risks'])) return false;\r\n if (!Array.isArray(o['operator_decision_points'])) return false;\r\n // total_estimated_tokens / total_estimated_cost_usd may be number OR 'unknown'\r\n const totalToks = o['total_estimated_tokens'];\r\n if (typeof totalToks !== 'number' && totalToks !== 'unknown') return false;\r\n const totalCost = o['total_estimated_cost_usd'];\r\n if (typeof totalCost !== 'number' && totalCost !== 'unknown') return false;\r\n // Phase shape \u2014 light validation; missing fields tolerated rather than rejecting the whole plan.\r\n for (const phase of o['phases']) {\r\n if (typeof phase !== 'object' || phase === null) return false;\r\n const p = phase as Record<string, unknown>;\r\n if (typeof p['name'] !== 'string') return false;\r\n if (typeof p['subagent_count'] !== 'number') return false;\r\n }\r\n return true;\r\n}\r\n\r\nexport async function handleDecomposeDispatch(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Required: { customer_goal: string }. Optional: context (object).',\r\n );\r\n }\r\n assertWithinByteCap(TOOL_NAME, 'customer_goal', rawInput.customer_goal, MAX_GOAL_BYTES);\r\n\r\n let contextStrForSize = '';\r\n if (rawInput.context !== undefined) {\r\n try {\r\n contextStrForSize = JSON.stringify(rawInput.context);\r\n } catch {\r\n throw invalidParams(TOOL_NAME, \"input field 'context' is not JSON-serializable.\");\r\n }\r\n assertWithinByteCap(TOOL_NAME, 'context', contextStrForSize, MAX_CONTEXT_BYTES);\r\n }\r\n\r\n // Cache key includes prompt version so doctrine updates invalidate cache.\r\n const cacheInput = {\r\n system_prompt_version: SYSTEM_PROMPT_VERSION,\r\n customer_goal: rawInput.customer_goal,\r\n ...(rawInput.context !== undefined ? { context: rawInput.context } : {}),\r\n };\r\n const key = deps.cache.keyFor(TOOL_NAME, cacheInput);\r\n const excerpt = rawInput.customer_goal.slice(0, 300);\r\n const inputSizeBytes = bytesOf(rawInput.customer_goal) + bytesOf(contextStrForSize);\r\n\r\n // Cache replay \u2014 return cached envelope, still emit event (gate #7).\r\n const cached = deps.cache.get<ToolResultEnvelope<DecomposeDispatchPayload>>(key);\r\n if (cached !== null) {\r\n const replayEvent: ConsensusCallEvent = {\r\n ...buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n }),\r\n per_model_verdicts: cached.value.payload.per_model_plans.map((p) => ({\r\n model: p.model_id,\r\n model_id: p.model_id,\r\n provider: 'cache-replay',\r\n verdict: p.parse_ok ? 'pass' : 'uncertain',\r\n confidence: p.confidence,\r\n duration_ms: 0,\r\n raw_response_excerpt: p.plan_text.slice(0, 500),\r\n raw_response_hash: '',\r\n reasoning_summary: null,\r\n cited_sources: [],\r\n error: null,\r\n })),\r\n consensus_engine_version: cached.value.payload.engine_version ?? null,\r\n cache_hit: true,\r\n };\r\n deps.events.append(replayEvent);\r\n const replayEnvelope: ToolResultEnvelope<DecomposeDispatchPayload> = {\r\n ...cached.value,\r\n cache: { hit: true, key },\r\n };\r\n return jsonContent(replayEnvelope);\r\n }\r\n\r\n // Build the full prompt: system + customer goal.\r\n const fullPrompt =\r\n `${SYSTEM_PROMPT}\\n\\n---\\n\\nCustomer goal:\\n${rawInput.customer_goal}\\n\\nProduce the JSON dispatch plan now.`;\r\n\r\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes,\r\n session: deps.session,\r\n now: deps.now(),\r\n });\r\n\r\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\r\n let engineThrew = false;\r\n try {\r\n engineResult = await deps.consensus.run({\r\n gate_type: GATE_TYPE,\r\n prompt: fullPrompt,\r\n ...(rawInput.context !== undefined ? { caller_context: rawInput.context } : {}),\r\n ...(signal !== undefined ? { signal } : {}),\r\n });\r\n } catch (err) {\r\n engineThrew = true;\r\n const message = err instanceof Error ? err.message : String(err);\r\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\r\n }\r\n\r\n // Cancellation short-circuit (mirrors consensus-judgment.ts pattern).\r\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\r\n deps.events.append({\r\n ...baseEvent,\r\n downstream_outcome: {\r\n status: 'cancelled',\r\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\r\n },\r\n });\r\n const e = new Error('Request cancelled by client (notifications/cancelled)');\r\n e.name = 'AbortError';\r\n throw e;\r\n }\r\n\r\n if (!engineResult.ok) {\r\n // Engine unavailable \u2014 degrade to unimplemented envelope.\r\n const payload: DecomposeDispatchPayload = {\r\n verdict: 'unimplemented',\r\n reason: engineResult.reason,\r\n dispatch_plan: null,\r\n per_model_plans: [],\r\n synthesized_reasoning: '',\r\n consensus_confidence: 0,\r\n ...(engineThrew ? { degraded_mode: true } : {}),\r\n };\r\n const envelope: ToolResultEnvelope<DecomposeDispatchPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n // Do NOT cache !ok envelopes (same reasoning as consensus-judgment HP2-1 fix).\r\n deps.events.append(baseEvent);\r\n return jsonContent(envelope);\r\n }\r\n\r\n // Engine ran \u2014 parse each per-model response as a dispatch plan, parse\r\n // the synthesized verdict's reasoning_excerpt as THE plan.\r\n const perModelPlans = engineResult.per_model_verdicts.map((v) => {\r\n const parsed = tryParseDispatchPlan(v.raw_response_excerpt);\r\n return {\r\n model_id: v.model,\r\n plan_text: v.raw_response_excerpt,\r\n confidence: v.confidence,\r\n parse_ok: parsed !== null,\r\n };\r\n });\r\n\r\n const synthText = engineResult.synthesized_verdict.reasoning_excerpt;\r\n const synthPlan = tryParseDispatchPlan(synthText);\r\n\r\n const payload: DecomposeDispatchPayload = {\r\n verdict: synthPlan !== null ? 'pass' : 'uncertain',\r\n reason: synthPlan !== null\r\n ? 'Synthesized dispatch plan parsed successfully'\r\n : 'Synthesized verdict did not parse as a valid dispatch plan JSON \u2014 see per_model_plans for raw outputs',\r\n dispatch_plan: synthPlan,\r\n ...(synthPlan === null ? { parse_error: 'synthesized verdict text was not valid dispatch-plan JSON' } : {}),\r\n per_model_plans: perModelPlans,\r\n synthesized_reasoning: synthText,\r\n consensus_confidence: engineResult.synthesized_verdict.confidence,\r\n engine_version: engineResult.engine_version,\r\n ...(engineResult.per_model_verdicts.length < 3 ? { degraded: true } : {}),\r\n };\r\n\r\n const envelope: ToolResultEnvelope<DecomposeDispatchPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n\r\n // Cache the successful response (ok-engine envelopes are cacheable).\r\n deps.cache.set(key, envelope);\r\n\r\n const enrichedEvent: ConsensusCallEvent = {\r\n ...baseEvent,\r\n consensus_confidence: engineResult.synthesized_verdict.confidence,\r\n duration_ms: engineResult.duration_ms,\r\n consensus_engine_version: engineResult.engine_version,\r\n per_model_verdicts: toEventPerModelVerdicts(engineResult.per_model_verdicts),\r\n synthesized_verdict: toEventSynthesizedVerdict(engineResult.synthesized_verdict),\r\n };\r\n deps.events.append(enrichedEvent);\r\n\r\n return jsonContent(envelope);\r\n}\r\n\r\n/** Exported for tests. */\r\nexport const __test = {\r\n SYSTEM_PROMPT,\r\n SYSTEM_PROMPT_VERSION,\r\n tryParseDispatchPlan,\r\n isDispatchPlan,\r\n};\r\n", "/**\r\n * HTTP client for vo-control-plane's admin proxy endpoints (PR 1 #5670\r\n * and PR 2 #5674). Used by the vo-mcp heal/PR tool family when running\r\n * in cloud-mode.\r\n *\r\n * ## Why this exists\r\n *\r\n * The 10 stub tools in `src/tools/heal/` and `src/tools/pr/` return\r\n * `{verdict: 'unimplemented'}` because their Firebase callables (in\r\n * `functions-core-vo/src/vo/`) gate on Firebase auth UID \u2014 which the\r\n * MCP server, running as a desktop subprocess, cannot mint server-side.\r\n *\r\n * vo-control-plane bridges the gap: PRs 1+2 ship 11 admin HTTP proxy\r\n * endpoints under `/api/v1/admin/*` that forward to the callables.\r\n * vo-mcp's cloud-mode tools call these HTTP endpoints with the\r\n * `VO_CONTROL_PLANE_ADMIN_TOKEN` Bearer.\r\n *\r\n * ## End-to-end auth\r\n *\r\n * MCP client \u2192 vo-mcp (no auth \u2014 local subprocess)\r\n * vo-mcp \u2192 vo-control-plane (Bearer VO_CONTROL_PLANE_ADMIN_TOKEN)\r\n * vo-control-plane \u2192 Firebase callable (Bearer VO_INTERNAL_ADMIN_TOKEN,\r\n * after PR 1a/1b lands)\r\n *\r\n * ## Failure modes (all surface as thrown `AdminCallableError`)\r\n *\r\n * - 401 \u2014 invalid/missing token at vo-control-plane gate\r\n * - 502 \u2014 vo-control-plane couldn't reach the callable (during 1a/1b\r\n * gap, OR vo-control-plane is down)\r\n * - 503 \u2014 vo-control-plane has VO_ADMIN_CALLABLES=disabled\r\n * - 400 \u2014 input failed Zod validation at vo-control-plane\r\n * - network \u2014 connection refused / DNS / timeout\r\n *\r\n * Tool handlers map these to a `verdict: 'fail'` envelope with the\r\n * specific reason. Stub-mode (no client wired) remains the default.\r\n */\r\n\r\nimport {\r\n type AuthTokenSource,\r\n createStaticTokenSource,\r\n createAuthTokenSourceFromEnv,\r\n} from './auth-token-source.js';\r\nimport { readStoredCredential } from './credential-store.js';\r\n\r\nexport interface AdminInvokeOptions {\r\n /**\r\n * When true, return the full success envelope (with `ok` stripped)\r\n * instead of mandating a `.result` field. For control-plane endpoints\r\n * that do NOT use the callable-proxy `{ok, callable, result}` shape \u2014\r\n * e.g. concierge dispatch returns `{ok, mode, routed_from, pack}`.\r\n */\r\n readonly rawEnvelope?: boolean;\r\n}\r\n\r\nexport interface AdminCallableClient {\r\n /**\r\n * Read-only mode flag. When true, `buildCloudOrStubResponse` gates any\r\n * caller that is NOT itself read-only (the heal/PR *write* tools) back to\r\n * the stub envelope, while read-only tools (concierge dispatch + the\r\n * list/get diagnostics) still forward to cloud. Set via the\r\n * `VO_ADMIN_CALLABLES_READONLY` env var. Optional so existing test mocks\r\n * (plain object literals) remain valid.\r\n */\r\n readonly readOnly?: boolean;\r\n\r\n /**\r\n * Invoke an admin proxy endpoint. Returns the wrapped `result` field\r\n * from vo-control-plane's `{ok: true, callable, result}` envelope on\r\n * success. Throws `AdminCallableError` on any failure.\r\n *\r\n * @param path Relative path, e.g. '/api/v1/admin/pr/list'. Leading\r\n * slash required; gets joined with the control-plane base URL.\r\n * @param body JSON-serializable body. Pass `{}` for callables with no\r\n * parameters.\r\n * @param opts Pass `{ rawEnvelope: true }` for endpoints that return\r\n * a non-callable-proxy envelope (no `.result`) \u2014 the full object is\r\n * returned with `ok` stripped.\r\n */\r\n invoke<T = unknown>(\r\n path: string,\r\n body: Record<string, unknown>,\r\n opts?: AdminInvokeOptions,\r\n ): Promise<T>;\r\n}\r\n\r\n/**\r\n * Minimal fetch-like surface for testing. Production uses\r\n * `globalThis.fetch`.\r\n */\r\nexport type FetchLike = (\r\n url: string,\r\n init?: {\r\n method?: string;\r\n headers?: Record<string, string>;\r\n body?: string;\r\n },\r\n) => Promise<{\r\n status: number;\r\n text: () => Promise<string>;\r\n}>;\r\n\r\nexport interface HttpAdminCallableClientConfig {\r\n /**\r\n * Base URL of vo-control-plane (no trailing slash), e.g.\r\n * `https://vo-control-plane-bzjphrajaq-uc.a.run.app`.\r\n */\r\n readonly controlPlaneUrl: string;\r\n /**\r\n * Legacy static god-token (`VO_CONTROL_PLANE_ADMIN_TOKEN`). Provide this OR\r\n * `tokenSource`. Retained for back-compat \u2014 wrapped in a static source.\r\n */\r\n readonly adminToken?: string;\r\n /**\r\n * Per-user / pluggable auth (Increment 3). Preferred over `adminToken`; lets\r\n * the client send the USER's Firebase token so the god-token leaves the client\r\n * config. See {@link AuthTokenSource}.\r\n */\r\n readonly tokenSource?: AuthTokenSource;\r\n /** Read-only mode \u2014 gates non-read-only (write) tools back to stub. Default false. */\r\n readonly readOnly?: boolean;\r\n /** Optional fetch override for tests. Defaults to `globalThis.fetch`. */\r\n readonly fetchFn?: FetchLike;\r\n}\r\n\r\nexport class AdminCallableError extends Error {\r\n constructor(\r\n public readonly status: number,\r\n public readonly path: string,\r\n message: string,\r\n ) {\r\n super(message);\r\n this.name = 'AdminCallableError';\r\n }\r\n}\r\n\r\nexport class HttpAdminCallableClient implements AdminCallableClient {\r\n private readonly baseUrl: string;\r\n private readonly tokenSource: AuthTokenSource;\r\n private readonly fetchFn: FetchLike;\r\n /** See {@link AdminCallableClient.readOnly}. */\r\n public readonly readOnly: boolean;\r\n\r\n constructor(config: HttpAdminCallableClientConfig) {\r\n if (!config.controlPlaneUrl || config.controlPlaneUrl.length === 0) {\r\n throw new Error('HttpAdminCallableClient: controlPlaneUrl is required');\r\n }\r\n if (config.tokenSource) {\r\n this.tokenSource = config.tokenSource;\r\n } else if (config.adminToken && config.adminToken.length > 0) {\r\n this.tokenSource = createStaticTokenSource(config.adminToken, 'admin-token');\r\n } else {\r\n throw new Error('HttpAdminCallableClient: a tokenSource or adminToken is required');\r\n }\r\n this.baseUrl = config.controlPlaneUrl.replace(/\\/+$/, '');\r\n this.fetchFn = config.fetchFn ?? (globalThis.fetch as unknown as FetchLike);\r\n this.readOnly = config.readOnly ?? false;\r\n }\r\n\r\n async invoke<T = unknown>(\r\n path: string,\r\n body: Record<string, unknown>,\r\n opts?: AdminInvokeOptions,\r\n ): Promise<T> {\r\n if (!path.startsWith('/')) {\r\n throw new Error(\r\n `HttpAdminCallableClient.invoke: path must start with '/', got '${path}'`,\r\n );\r\n }\r\n const token = await this.tokenSource.getToken();\r\n if (!token) {\r\n // Fail-closed: never send an empty Bearer. Surfaces as a tool 'fail' envelope.\r\n throw new AdminCallableError(\r\n 401,\r\n path,\r\n `admin proxy ${path}: no auth token available (check VO_USER_REFRESH_TOKEN / VO_USER_ID_TOKEN / VO_CONTROL_PLANE_ADMIN_TOKEN)`,\r\n );\r\n }\r\n const url = `${this.baseUrl}${path}`;\r\n const response = await this.fetchFn(url, {\r\n method: 'POST',\r\n headers: {\r\n 'authorization': `Bearer ${token}`,\r\n 'content-type': 'application/json',\r\n },\r\n body: JSON.stringify(body),\r\n });\r\n const text = await response.text();\r\n if (response.status < 200 || response.status >= 300) {\r\n throw new AdminCallableError(\r\n response.status,\r\n path,\r\n `admin proxy ${path} returned HTTP ${response.status}: ${text.slice(0, 200)}`,\r\n );\r\n }\r\n let parsed: unknown;\r\n try {\r\n parsed = JSON.parse(text);\r\n } catch {\r\n throw new AdminCallableError(\r\n response.status,\r\n path,\r\n `admin proxy ${path} returned non-JSON body`,\r\n );\r\n }\r\n if (typeof parsed !== 'object' || parsed === null) {\r\n throw new AdminCallableError(\r\n response.status,\r\n path,\r\n `admin proxy ${path} response not an object`,\r\n );\r\n }\r\n const obj = parsed as Record<string, unknown>;\r\n if (obj['ok'] !== true) {\r\n throw new AdminCallableError(\r\n response.status,\r\n path,\r\n `admin proxy ${path} returned ok=false: ${JSON.stringify(obj).slice(0, 200)}`,\r\n );\r\n }\r\n // Endpoints that don't use the callable-proxy `{ok, callable, result}`\r\n // envelope (e.g. concierge dispatch \u2192 `{ok, mode, routed_from, pack}`)\r\n // opt into receiving the full envelope, with `ok` stripped.\r\n if (opts?.rawEnvelope) {\r\n const rest: Record<string, unknown> = {};\r\n for (const [k, v] of Object.entries(obj)) {\r\n if (k !== 'ok') rest[k] = v;\r\n }\r\n return rest as T;\r\n }\r\n if (!('result' in obj)) {\r\n throw new AdminCallableError(\r\n response.status,\r\n path,\r\n `admin proxy ${path} response missing .result field`,\r\n );\r\n }\r\n return obj['result'] as T;\r\n }\r\n}\r\n\r\n/**\r\n * Build an admin callable client from env vars. Called by `cli.ts` \u2014\r\n * tests construct directly.\r\n *\r\n * Env vars:\r\n * - `VO_CONTROL_PLANE_URL` \u2014 required for cloud mode\r\n * - `VO_CONTROL_PLANE_ADMIN_TOKEN` \u2014 required for cloud mode\r\n * - `VO_ADMIN_CALLABLES_READONLY` \u2014 optional; when truthy (`1`/`true`)\r\n * the client is read-only: write tools (heal/PR) stay stubbed while\r\n * read-only tools (concierge dispatch + list/get diagnostics) forward.\r\n *\r\n * Returns `null` when both URL + token are unset \u2014 cloud mode is off, tools\r\n * fall back to stub envelopes. Both must be set together; presence of\r\n * only one is a configuration error (throws).\r\n */\r\nexport function buildAdminCallableClientFromEnv(\r\n env: NodeJS.ProcessEnv = process.env,\r\n): AdminCallableClient | null {\r\n const url = env['VO_CONTROL_PLANE_URL'];\r\n // Increment 3: prefer a per-user token over the legacy god-token. Priority:\r\n // explicit env user-token \u2192 stored `vo-mcp login` credential (3b) \u2192 god-token.\r\n // The god-token is no longer required in client config when a user token is\r\n // present. Throws on a half-configured env refresh source (token without key).\r\n const tokenSource = createAuthTokenSourceFromEnv(env, undefined, () => readStoredCredential(env));\r\n if (!url && !tokenSource) return null;\r\n if (!url) {\r\n throw new Error('Cloud mode requires VO_CONTROL_PLANE_URL');\r\n }\r\n if (!tokenSource) {\r\n throw new Error(\r\n 'Cloud mode requires auth: set VO_USER_REFRESH_TOKEN+VO_FIREBASE_API_KEY (per-user), VO_USER_ID_TOKEN, or VO_CONTROL_PLANE_ADMIN_TOKEN',\r\n );\r\n }\r\n const readOnlyRaw = env['VO_ADMIN_CALLABLES_READONLY'];\r\n const readOnly = readOnlyRaw === '1' || readOnlyRaw?.toLowerCase() === 'true';\r\n return new HttpAdminCallableClient({\r\n controlPlaneUrl: url,\r\n tokenSource,\r\n readOnly,\r\n });\r\n}\r\n", "/**\r\n * Shared cloud-or-stub response builder for the heal/PR tool family.\r\n *\r\n * Replaces the buildPrStubResponse / buildHealStubResponse calls with a\r\n * single path that branches on `deps.adminCallables`:\r\n *\r\n * - null/undefined \u2192 stub envelope (`verdict: 'unimplemented'`)\r\n * - set + success \u2192 cloud envelope (`verdict: 'pass'` + response_data)\r\n * - set + failure \u2192 cloud envelope (`verdict: 'fail'` + reason)\r\n *\r\n * In all 3 cases a gate #7 event is appended. The stub-mode `!ok`\r\n * envelope is NOT cached (HP2-1 hardening); the cloud-mode `pass`\r\n * envelope is cacheable but we leave caching off in PR 3 to keep the\r\n * surface tight \u2014 PR 4 can revisit.\r\n *\r\n * PR 3 wires this helper into `list-pending-prs.ts` (PR family) and\r\n * `get-workflow-runs.ts` (heal family) as proof of pattern. PR 4\r\n * migrates the remaining 8 stub tools.\r\n */\r\nimport type {\r\n AdminCallablePayload,\r\n ConsensusCallEvent,\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n} from '../types.js';\r\nimport { AdminCallableError } from '../cloud/admin-callable-client.js';\r\nimport { buildBaseEvent, bytesOf, jsonContent } from './common.js';\r\n\r\nexport interface BuildCloudOrStubArgs {\r\n readonly toolName: string;\r\n /** Firebase callable name, e.g. `voListPendingPRs`. */\r\n readonly callableName: string;\r\n /** vo-control-plane admin proxy path, e.g. `/api/v1/admin/pr/list`. */\r\n readonly adminPath: string;\r\n /**\r\n * Snake-case input as received from the MCP client. Used verbatim in\r\n * the response envelope's `normalized_input` field, AND as the\r\n * cloud-mode wire body when `cloudBody` is not provided.\r\n */\r\n readonly normalizedInput: Record<string, unknown>;\r\n /**\r\n * Optional camelCase body for the cloud HTTP call. Tools whose MCP\r\n * input contract uses snake_case (e.g. `pr_number`) but whose admin\r\n * endpoint validates camelCase (e.g. `prNumber`) pass an explicit\r\n * remapped body here. Defaults to `normalizedInput` for tools where\r\n * the two shapes coincide (empty body, or already camelCase).\r\n */\r\n readonly cloudBody?: Record<string, unknown>;\r\n /**\r\n * When true, the cloud HTTP response is accepted as a raw envelope (no\r\n * mandated `.result` field) and used as `response_data` directly. For\r\n * endpoints like concierge dispatch that return `{ok, mode, pack}`\r\n * rather than the callable-proxy `{ok, callable, result}` shape.\r\n */\r\n readonly rawEnvelope?: boolean;\r\n /** Either 'admin-action' (matches existing heal/PR stub families). */\r\n readonly gateType: string;\r\n /** Stub-mode reason text \u2014 see common-pr.ts PR_STUB_REASON. */\r\n readonly stubReason: string;\r\n /**\r\n * Marks this tool as read-only. When the wired admin client is in\r\n * read-only mode (`VO_ADMIN_CALLABLES_READONLY`), only read-only tools\r\n * forward to cloud; non-read-only (write) tools are gated back to the\r\n * stub envelope. Default false (write).\r\n */\r\n readonly readOnly?: boolean;\r\n readonly deps: ToolDeps;\r\n}\r\n\r\n/**\r\n * Reason emitted when cloud mode is active but read-only, and a write tool\r\n * is gated back to its stub. Exported for tests.\r\n */\r\nexport const ADMIN_READONLY_GATE_REASON =\r\n '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.';\r\n\r\nexport async function buildCloudOrStubResponse(\r\n args: BuildCloudOrStubArgs,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n const inputJson = JSON.stringify(args.normalizedInput);\r\n const excerpt = inputJson.slice(0, 300);\r\n const key = args.deps.cache.keyFor(args.toolName, args.normalizedInput);\r\n\r\n const event: ConsensusCallEvent = buildBaseEvent({\r\n tool: args.toolName,\r\n gateType: args.gateType,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes: bytesOf(inputJson),\r\n session: args.deps.session,\r\n now: args.deps.now(),\r\n });\r\n\r\n // Read-only gate: when the wired client is read-only and THIS tool is a\r\n // write tool, fall back to the stub envelope instead of forwarding.\r\n const gatedByReadonly =\r\n Boolean(args.deps.adminCallables?.readOnly) && !args.readOnly;\r\n\r\n if (!args.deps.adminCallables || gatedByReadonly) {\r\n const payload: AdminCallablePayload = {\r\n verdict: 'unimplemented',\r\n reason: gatedByReadonly ? ADMIN_READONLY_GATE_REASON : args.stubReason,\r\n callable: args.callableName,\r\n normalized_input: args.normalizedInput,\r\n };\r\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\r\n tool: args.toolName,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n args.deps.events.append(event);\r\n return jsonContent(envelope);\r\n }\r\n\r\n try {\r\n const result = await args.deps.adminCallables.invoke<Record<string, unknown>>(\r\n args.adminPath,\r\n args.cloudBody ?? args.normalizedInput,\r\n args.rawEnvelope ? { rawEnvelope: true } : undefined,\r\n );\r\n const payload: AdminCallablePayload = {\r\n verdict: 'pass',\r\n reason: `forwarded to ${args.callableName} via vo-control-plane ${args.adminPath}`,\r\n callable: args.callableName,\r\n normalized_input: args.normalizedInput,\r\n response_data:\r\n result && typeof result === 'object' && !Array.isArray(result)\r\n ? (result as Record<string, unknown>)\r\n : { value: result },\r\n };\r\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\r\n tool: args.toolName,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n args.deps.events.append(event);\r\n return jsonContent(envelope);\r\n } catch (err) {\r\n const status =\r\n err instanceof AdminCallableError ? err.status : undefined;\r\n const message = err instanceof Error ? err.message : String(err);\r\n const payload: AdminCallablePayload = {\r\n verdict: 'fail',\r\n reason:\r\n status !== undefined\r\n ? `vo-control-plane returned HTTP ${status}: ${message.slice(0, 300)}`\r\n : `cloud invocation failed: ${message.slice(0, 300)}`,\r\n callable: args.callableName,\r\n normalized_input: args.normalizedInput,\r\n };\r\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\r\n tool: args.toolName,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n args.deps.events.append(event);\r\n return jsonContent(envelope);\r\n }\r\n}\r\n", "/**\r\n * Shared helpers for the heal/* MCP tool family (Plan PR #12a).\r\n *\r\n * All 5 heal tools (vo_trigger_heal, vo_fix_retry, vo_fix_clear,\r\n * vo_stop_workflow, vo_get_workflow_runs) wrap admin onCall callables in\r\n * `functions-core-vo/src/vo/vo-heal-*.ts`. For V1 those callables are\r\n * NOT reachable from the local MCP server because cloud-mode is still\r\n * unwired (`packages/vo-mcp/src/modes/cloud.ts` throws on construction,\r\n * pending `vo-cloud-tenant-model-2026-05-21.md` dispatch).\r\n *\r\n * So this PR ships the tool SURFACE \u2014 `tools/list` reveals the 5 tools,\r\n * input schemas are pinned to the cloud callables' contracts, and every\r\n * call returns a structured \"unimplemented\" envelope with a precise\r\n * reason. The contract is locked; a follow-up PR adds the real cloud\r\n * wiring without touching the contract or any consumer.\r\n *\r\n * This mirrors how `vo_consensus_judgment` originally shipped: stub \u2192\r\n * engine wired later \u2192 consumers unchanged. Documented in\r\n * `EXTRACTION_AUDIT.md` under \"Stub remaining (2026-05-24)\".\r\n */\r\nimport type {\r\n AdminCallablePayload,\r\n ConsensusCallEvent,\r\n SessionContext,\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n} from '../../types.js';\r\nimport {\r\n buildBaseEvent,\r\n bytesOf,\r\n jsonContent,\r\n} from '../common.js';\r\n\r\n/** Reason text shared by every heal-family stub. */\r\nexport const HEAL_STUB_REASON =\r\n '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\")';\r\n\r\n/** gate_type for admin-action telemetry \u2014 distinct from ratchet/consensus paths. */\r\nexport const HEAL_GATE_TYPE = 'admin-action' as const;\r\n\r\nexport interface BuildHealStubArgs {\r\n readonly toolName: string;\r\n readonly callableName: string;\r\n readonly normalizedInput: Record<string, unknown>;\r\n readonly deps: ToolDeps;\r\n}\r\n\r\n/**\r\n * Build the standard heal-stub envelope + emit the gate #7 event line.\r\n *\r\n * Every tool in the heal family calls this after validating its input.\r\n * The shape is intentionally identical across tools so the operator's\r\n * consumers can branch on a single AdminCallablePayload type.\r\n */\r\nexport function buildHealStubResponse(\r\n args: BuildHealStubArgs,\r\n): ReturnType<typeof jsonContent> {\r\n const inputJson = JSON.stringify(args.normalizedInput);\r\n const excerpt = inputJson.slice(0, 300);\r\n const key = args.deps.cache.keyFor(args.toolName, args.normalizedInput);\r\n\r\n const payload: AdminCallablePayload = {\r\n verdict: 'unimplemented',\r\n reason: HEAL_STUB_REASON,\r\n callable: args.callableName,\r\n normalized_input: args.normalizedInput,\r\n };\r\n\r\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\r\n tool: args.toolName,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n\r\n // Do NOT cache the unimplemented envelope (HP2-1 hardening \u2014 see\r\n // consensus-judgment.ts). When cloud-mode lands, the env transition\r\n // unwired\u2192wired must be visible without manual cache eviction.\r\n const event: ConsensusCallEvent = buildBaseEvent({\r\n tool: args.toolName,\r\n gateType: HEAL_GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes: bytesOf(inputJson),\r\n session: args.deps.session,\r\n now: args.deps.now(),\r\n });\r\n args.deps.events.append(event);\r\n\r\n return jsonContent(envelope);\r\n}\r\n\r\n/** Re-exported for tools that need to construct events directly. */\r\nexport type { SessionContext };\r\n", "/**\r\n * Tool: `vo_trigger_heal`\r\n *\r\n * Wraps the `voTriggerHeal` admin callable\r\n * (`functions-core-vo/src/vo/vo-heal-trigger.ts`). Triggers a self-heal\r\n * pass \u2014 either for a specific focus page/tester (priority queue) or\r\n * for the auto-process queue.\r\n *\r\n * V1 ships as a stub returning `verdict: 'unimplemented'` because\r\n * cloud-mode is not yet wired (see common-heal.ts header). The input\r\n * schema mirrors the cloud callable's contract exactly so a future\r\n * cloud-mode wiring PR can drop in the real implementation without\r\n * touching any consumer.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\r\n\r\nexport const TOOL_NAME = 'vo_trigger_heal';\r\nconst CALLABLE_NAME = 'voTriggerHeal' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/heal/trigger' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n focus_page: {\r\n type: 'string',\r\n description:\r\n 'Optional focus page identifier (maps to a known tester via vo-heal-focus state). When set, the heal pass becomes priority-queued for that tester. Omit to trigger the auto-process queue.',\r\n },\r\n force: {\r\n type: 'boolean',\r\n description:\r\n 'When true, request the heal pass even if a queue manager is already active. Default false.',\r\n },\r\n },\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Triggers a self-heal pass against open PRs. Optionally scope to a `focus_page` (priority queue for one tester) or omit to fire the auto-process queue. Wraps the `voTriggerHeal` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` with structured normalized_input \u2014 cloud-mode wiring is pending. The contract is locked; consumers can call this tool today and get the correct surface without working execution.\";\r\n\r\ninterface ToolInput {\r\n readonly focus_page?: string;\r\n readonly force?: boolean;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (o['focus_page'] !== undefined && typeof o['focus_page'] !== 'string') return false;\r\n if (o['force'] !== undefined && typeof o['force'] !== 'boolean') return false;\r\n return true;\r\n}\r\n\r\nexport async function handleTriggerHeal(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. All fields optional. Shape: { focus_page?: string, force?: boolean }.',\r\n );\r\n }\r\n const normalizedInput: Record<string, unknown> = {};\r\n const cloudBody: Record<string, unknown> = {};\r\n if (rawInput.focus_page !== undefined) {\r\n normalizedInput['focus_page'] = rawInput.focus_page;\r\n cloudBody['focusPage'] = rawInput.focus_page;\r\n }\r\n if (rawInput.force !== undefined) {\r\n normalizedInput['force'] = rawInput.force;\r\n cloudBody['force'] = rawInput.force;\r\n }\r\n\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput,\r\n cloudBody,\r\n gateType: HEAL_GATE_TYPE,\r\n stubReason: HEAL_STUB_REASON,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_fix_retry`\r\n *\r\n * Consolidates two admin callables:\r\n * - `voRetryFixAttempt({ attemptId })` \u2014 retry one failed fix attempt\r\n * - `voRetryFixAttempts({ attemptIds[] })` \u2014 retry up to 50 at once\r\n *\r\n * The MCP tool exposes BOTH via a single input shape: pass `attempt_id`\r\n * for the single case, or `attempt_ids` for the batch case. Mutually\r\n * exclusive \u2014 passing both is rejected.\r\n *\r\n * V1 stub \u2014 see common-heal.ts header.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\r\n\r\nexport const TOOL_NAME = 'vo_fix_retry';\r\n\r\n/** Batch cap matches the cloud callable's slice(0, 50) limit. */\r\nconst MAX_BATCH = 50;\r\n\r\n/** Admin proxy paths for the two callables this MCP tool consolidates. */\r\nconst ADMIN_PATH_SINGLE = '/api/v1/admin/heal/retry-attempt' as const;\r\nconst ADMIN_PATH_BATCH = '/api/v1/admin/heal/retry-attempts' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n attempt_id: {\r\n type: 'string',\r\n description:\r\n 'Retry a single fix attempt by id (wraps voRetryFixAttempt). Mutually exclusive with attempt_ids.',\r\n },\r\n attempt_ids: {\r\n type: 'array',\r\n items: { type: 'string' },\r\n description:\r\n 'Retry up to 50 fix attempts by id (wraps voRetryFixAttempts). Mutually exclusive with attempt_id.',\r\n },\r\n },\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Retries one or more failed fix attempts by id. Pass `attempt_id` for the single case or `attempt_ids` (up to 50) for the batch case. Wraps `voRetryFixAttempt` / `voRetryFixAttempts` admin Cloud Functions. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\r\n\r\ninterface ToolInput {\r\n readonly attempt_id?: string;\r\n readonly attempt_ids?: readonly string[];\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (o['attempt_id'] !== undefined && typeof o['attempt_id'] !== 'string') return false;\r\n if (o['attempt_ids'] !== undefined) {\r\n if (!Array.isArray(o['attempt_ids'])) return false;\r\n if (!o['attempt_ids'].every((id) => typeof id === 'string')) return false;\r\n }\r\n return true;\r\n}\r\n\r\nexport async function handleFixRetry(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Shape: { attempt_id: string } OR { attempt_ids: string[] } (mutually exclusive).',\r\n );\r\n }\r\n\r\n // Validate one-of contract. Destructure so the truthy-check inside each\r\n // `if` block also narrows the type \u2014 avoids the non-null-assertion path\r\n // the older code used (`rawInput.attempt_id!` etc.).\r\n const { attempt_id, attempt_ids } = rawInput;\r\n const hasSingle = typeof attempt_id === 'string' && attempt_id.trim().length > 0;\r\n const hasBatch = Array.isArray(attempt_ids) && attempt_ids.length > 0;\r\n\r\n if (!hasSingle && !hasBatch) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'either attempt_id (string) or attempt_ids (non-empty array) is required.',\r\n );\r\n }\r\n if (hasSingle && hasBatch) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'attempt_id and attempt_ids are mutually exclusive \u2014 pass exactly one.',\r\n );\r\n }\r\n if (Array.isArray(attempt_ids) && attempt_ids.length > MAX_BATCH) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n `attempt_ids exceeds batch cap of ${MAX_BATCH} (got ${attempt_ids.length}).`,\r\n );\r\n }\r\n\r\n const normalizedInput: Record<string, unknown> = {};\r\n const cloudBody: Record<string, unknown> = {};\r\n let resolvedCallable: string;\r\n let resolvedAdminPath: string;\r\n if (typeof attempt_id === 'string' && attempt_id.trim().length > 0) {\r\n const trimmed = attempt_id.trim();\r\n normalizedInput['attempt_id'] = trimmed;\r\n cloudBody['attemptId'] = trimmed;\r\n resolvedCallable = 'voRetryFixAttempt';\r\n resolvedAdminPath = ADMIN_PATH_SINGLE;\r\n } else if (Array.isArray(attempt_ids)) {\r\n // De-dupe + trim, mirroring the cloud callable's normalization.\r\n const cleaned = [\r\n ...new Set(attempt_ids.map((id) => id.trim()).filter(Boolean)),\r\n ].slice(0, MAX_BATCH);\r\n normalizedInput['attempt_ids'] = cleaned;\r\n cloudBody['attemptIds'] = cleaned;\r\n resolvedCallable = 'voRetryFixAttempts';\r\n resolvedAdminPath = ADMIN_PATH_BATCH;\r\n } else {\r\n // Defensive: the validation above guarantees one of the two branches\r\n // fired, but TypeScript can't propagate the hasSingle/hasBatch booleans.\r\n throw invalidParams(TOOL_NAME, 'internal validation skipped \u2014 please report.');\r\n }\r\n\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: resolvedCallable,\r\n adminPath: resolvedAdminPath,\r\n normalizedInput,\r\n cloudBody,\r\n gateType: HEAL_GATE_TYPE,\r\n stubReason: HEAL_STUB_REASON,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_fix_clear`\r\n *\r\n * Wraps `voClearFixAttempt({ attemptId })` admin callable. Clears (cancels)\r\n * a fix attempt in the virtual_office_attempts collection.\r\n *\r\n * V1 stub \u2014 see common-heal.ts header.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\r\n\r\nexport const TOOL_NAME = 'vo_fix_clear';\r\nconst CALLABLE_NAME = 'voClearFixAttempt' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/heal/clear-attempt' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n attempt_id: {\r\n type: 'string',\r\n description: 'Required. Fix-attempt id in the virtual_office_attempts collection.',\r\n },\r\n },\r\n required: ['attempt_id'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Clears (cancels) a single fix attempt by id. Wraps `voClearFixAttempt` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\r\n\r\ninterface ToolInput {\r\n readonly attempt_id: string;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['attempt_id'] !== 'string') return false;\r\n if (o['attempt_id'].trim().length === 0) return false;\r\n return true;\r\n}\r\n\r\nexport async function handleFixClear(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Shape: { attempt_id: non-empty string }.',\r\n );\r\n }\r\n\r\n const trimmed = rawInput.attempt_id.trim();\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput: { attempt_id: trimmed },\r\n cloudBody: { attemptId: trimmed },\r\n gateType: HEAL_GATE_TYPE,\r\n stubReason: HEAL_STUB_REASON,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_stop_workflow`\r\n *\r\n * Wraps `voStopWorkflow({ runId, force? })` admin callable. Cancels a\r\n * running GitHub Actions workflow run via the gh API.\r\n *\r\n * V1 stub \u2014 see common-heal.ts header.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\r\n\r\nexport const TOOL_NAME = 'vo_stop_workflow';\r\nconst CALLABLE_NAME = 'voStopWorkflow' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/workflow/stop' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n run_id: {\r\n type: 'number',\r\n description: 'Required. GitHub Actions workflow run id (positive integer).',\r\n },\r\n force: {\r\n type: 'boolean',\r\n description: 'Optional. When true, force-cancel even if the run looks healthy. Default false.',\r\n },\r\n },\r\n required: ['run_id'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Cancels a running GitHub Actions workflow by run id. Wraps `voStopWorkflow` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\r\n\r\ninterface ToolInput {\r\n readonly run_id: number;\r\n readonly force?: boolean;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['run_id'] !== 'number') return false;\r\n if (!Number.isFinite(o['run_id']) || o['run_id'] <= 0 || !Number.isInteger(o['run_id'])) {\r\n return false;\r\n }\r\n if (o['force'] !== undefined && typeof o['force'] !== 'boolean') return false;\r\n return true;\r\n}\r\n\r\nexport async function handleStopWorkflow(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Shape: { run_id: positive integer, force?: boolean }.',\r\n );\r\n }\r\n\r\n const normalizedInput: Record<string, unknown> = { run_id: rawInput.run_id };\r\n const cloudBody: Record<string, unknown> = { runId: rawInput.run_id };\r\n if (rawInput.force !== undefined) {\r\n normalizedInput['force'] = rawInput.force;\r\n cloudBody['force'] = rawInput.force;\r\n }\r\n\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput,\r\n cloudBody,\r\n gateType: HEAL_GATE_TYPE,\r\n stubReason: HEAL_STUB_REASON,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_get_workflow_runs`\r\n *\r\n * Wraps `voGetWorkflowRuns()` admin callable (no parameters). Returns\r\n * the Command Center workflow-runs snapshot via the gh API.\r\n *\r\n * V1 stub \u2014 see common-heal.ts header. Stub envelope's\r\n * `normalized_input` is the empty object since the callable takes no\r\n * params; the structured contract is still cache-keyed for consistency\r\n * with the rest of the heal family.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\r\n\r\nexport const TOOL_NAME = 'vo_get_workflow_runs';\r\nconst CALLABLE_NAME = 'voGetWorkflowRuns' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/workflow/runs' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {},\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Returns the current Command Center workflow-runs snapshot (Heal, Manager, Auto-Merge, Deploy on Merge, etc.). Wraps `voGetWorkflowRuns` admin Cloud Function. Read-only diagnostic. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\r\n\r\nfunction isToolInput(v: unknown): v is Record<string, never> {\r\n if (typeof v !== 'object' || v === null) return false;\r\n // Tolerate extra fields rather than rejecting \u2014 the schema declares\r\n // additionalProperties:false, so the MCP SDK will reject unknown fields\r\n // before we get here on spec-compliant clients. Loose check for\r\n // non-compliant clients.\r\n return true;\r\n}\r\n\r\nexport async function handleGetWorkflowRuns(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(TOOL_NAME, 'invalid input. This tool takes no parameters.');\r\n }\r\n\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput: {},\r\n gateType: HEAL_GATE_TYPE,\r\n stubReason: HEAL_STUB_REASON,\r\n // Read-only diagnostic \u2014 stays live under VO_ADMIN_CALLABLES_READONLY.\r\n readOnly: true,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Shared helpers for the pr/* MCP tool family (Plan PR #12b).\r\n *\r\n * Mirrors `packages/vo-mcp/src/tools/heal/common-heal.ts` exactly \u2014 same\r\n * stub-mode pattern, same envelope shape (AdminCallablePayload), same\r\n * gate_type ('admin-action'). The two helpers diverge only in their\r\n * STUB_REASON text so logs distinguish which family was called.\r\n *\r\n * Wraps the pr-manager admin callables in\r\n * `functions-core-vo/src/vo/vo-pr-manager.ts`:\r\n * - voListPendingPRs \u2192 vo_list_pending_prs\r\n * - voMergePR \u2192 vo_merge_pr\r\n * - voRejectPR \u2192 vo_reject_pr\r\n * - voApproveAllFixes \u2192 vo_approve_all_fixes\r\n * - voRejectAndRetry \u2192 vo_reject_and_retry\r\n *\r\n * V1 ships as stubs because vo-mcp's cloud-mode adapter\r\n * (packages/vo-mcp/src/modes/cloud.ts) still throws on construction.\r\n * See common-heal.ts header for the longer rationale.\r\n */\r\nimport type {\r\n AdminCallablePayload,\r\n ConsensusCallEvent,\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n} from '../../types.js';\r\nimport {\r\n buildBaseEvent,\r\n bytesOf,\r\n jsonContent,\r\n} from '../common.js';\r\n\r\n/** Reason text shared by every pr-family stub. */\r\nexport const PR_STUB_REASON =\r\n '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\")';\r\n\r\n/** Same gate_type as the heal family \u2014 both are admin-action telemetry. */\r\nexport const PR_GATE_TYPE = 'admin-action' as const;\r\n\r\nexport interface BuildPrStubArgs {\r\n readonly toolName: string;\r\n readonly callableName: string;\r\n readonly normalizedInput: Record<string, unknown>;\r\n readonly deps: ToolDeps;\r\n}\r\n\r\n/**\r\n * Build the standard pr-stub envelope + emit the gate #7 event line.\r\n * Parallel to `buildHealStubResponse` \u2014 see common-heal.ts.\r\n */\r\nexport function buildPrStubResponse(\r\n args: BuildPrStubArgs,\r\n): ReturnType<typeof jsonContent> {\r\n const inputJson = JSON.stringify(args.normalizedInput);\r\n const excerpt = inputJson.slice(0, 300);\r\n const key = args.deps.cache.keyFor(args.toolName, args.normalizedInput);\r\n\r\n const payload: AdminCallablePayload = {\r\n verdict: 'unimplemented',\r\n reason: PR_STUB_REASON,\r\n callable: args.callableName,\r\n normalized_input: args.normalizedInput,\r\n };\r\n\r\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\r\n tool: args.toolName,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n\r\n // Do NOT cache unimplemented envelopes (HP2-1 hardening \u2014 same as\r\n // common-heal.ts).\r\n const event: ConsensusCallEvent = buildBaseEvent({\r\n tool: args.toolName,\r\n gateType: PR_GATE_TYPE,\r\n inputHash: key,\r\n inputExcerpt: excerpt,\r\n inputSizeBytes: bytesOf(inputJson),\r\n session: args.deps.session,\r\n now: args.deps.now(),\r\n });\r\n args.deps.events.append(event);\r\n\r\n return jsonContent(envelope);\r\n}\r\n", "/**\r\n * Tool: `vo_list_pending_prs`\r\n *\r\n * Wraps `voListPendingPRs()` admin callable (no parameters). Returns\r\n * the list of open VO-source pull requests with blocker / source /\r\n * tester / specialist-context metadata.\r\n *\r\n * V1 stub \u2014 see common-pr.ts header.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\r\n\r\nexport const TOOL_NAME = 'vo_list_pending_prs';\r\nconst CALLABLE_NAME = 'voListPendingPRs' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/pr/list' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {},\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"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.\";\r\n\r\nfunction isToolInput(v: unknown): v is Record<string, never> {\r\n return typeof v === 'object' && v !== null;\r\n}\r\n\r\nexport async function handleListPendingPRs(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(TOOL_NAME, 'invalid input. This tool takes no parameters.');\r\n }\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput: {},\r\n gateType: PR_GATE_TYPE,\r\n stubReason: PR_STUB_REASON,\r\n // Read-only diagnostic \u2014 stays live under VO_ADMIN_CALLABLES_READONLY.\r\n readOnly: true,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_merge_pr`\r\n *\r\n * Wraps `voMergePR({ prNumber })` admin callable. Approves + merges a\r\n * single VO-source PR by number (refuses non-VO PRs server-side).\r\n *\r\n * V1 stub \u2014 see common-pr.ts header.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\r\n\r\nexport const TOOL_NAME = 'vo_merge_pr';\r\nconst CALLABLE_NAME = 'voMergePR' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/pr/merge' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n pr_number: {\r\n type: 'number',\r\n description: 'Required. GitHub pull request number (positive integer).',\r\n },\r\n },\r\n required: ['pr_number'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"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.\";\r\n\r\ninterface ToolInput {\r\n readonly pr_number: number;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['pr_number'] !== 'number') return false;\r\n if (!Number.isFinite(o['pr_number']) || o['pr_number'] < 1 || !Number.isInteger(o['pr_number'])) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\nexport async function handleMergePR(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Shape: { pr_number: positive integer }.',\r\n );\r\n }\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput: { pr_number: rawInput.pr_number },\r\n cloudBody: { prNumber: rawInput.pr_number },\r\n gateType: PR_GATE_TYPE,\r\n stubReason: PR_STUB_REASON,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_reject_pr`\r\n *\r\n * Wraps `voRejectPR({ prNumber })` admin callable. Closes a PR without\r\n * merging (no retry dispatched \u2014 use vo_reject_and_retry for that).\r\n *\r\n * V1 stub \u2014 see common-pr.ts header.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\r\n\r\nexport const TOOL_NAME = 'vo_reject_pr';\r\nconst CALLABLE_NAME = 'voRejectPR' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/pr/reject' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n pr_number: {\r\n type: 'number',\r\n description: 'Required. GitHub pull request number (positive integer).',\r\n },\r\n },\r\n required: ['pr_number'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Closes a pull request without merging. No retry dispatched \u2014 use `vo_reject_and_retry` for close+retry. Wraps `voRejectPR` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\r\n\r\ninterface ToolInput {\r\n readonly pr_number: number;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['pr_number'] !== 'number') return false;\r\n if (!Number.isFinite(o['pr_number']) || o['pr_number'] < 1 || !Number.isInteger(o['pr_number'])) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\nexport async function handleRejectPR(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Shape: { pr_number: positive integer }.',\r\n );\r\n }\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput: { pr_number: rawInput.pr_number },\r\n cloudBody: { prNumber: rawInput.pr_number },\r\n gateType: PR_GATE_TYPE,\r\n stubReason: PR_STUB_REASON,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_approve_all_fixes`\r\n *\r\n * Wraps `voApproveAllFixes()` admin callable (no parameters). Iterates\r\n * all open VO-source PRs and merges/auto-merges each. Returns a summary\r\n * of merged / accepted / total counts plus per-PR results.\r\n *\r\n * V1 stub \u2014 see common-pr.ts header.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\r\n\r\nexport const TOOL_NAME = 'vo_approve_all_fixes';\r\nconst CALLABLE_NAME = 'voApproveAllFixes' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/pr/approve-all' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {},\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"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.\";\r\n\r\nfunction isToolInput(v: unknown): v is Record<string, never> {\r\n return typeof v === 'object' && v !== null;\r\n}\r\n\r\nexport async function handleApproveAllFixes(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(TOOL_NAME, 'invalid input. This tool takes no parameters.');\r\n }\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput: {},\r\n gateType: PR_GATE_TYPE,\r\n stubReason: PR_STUB_REASON,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_reject_and_retry`\r\n *\r\n * Wraps `voRejectAndRetry({ prNumber })` admin callable. Closes a VO PR\r\n * and dispatches a self-heal pass to retry the same focus page. The\r\n * cloud callable refuses non-VO PRs and respects the self-heal kill\r\n * switch + per-PR retry block.\r\n *\r\n * V1 stub \u2014 see common-pr.ts header.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type {\r\n jsonContent} from '../common.js';\r\nimport {\r\n invalidParams,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\r\n\r\nexport const TOOL_NAME = 'vo_reject_and_retry';\r\nconst CALLABLE_NAME = 'voRejectAndRetry' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/pr/reject-retry' as const;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n pr_number: {\r\n type: 'number',\r\n description: 'Required. GitHub pull request number (positive integer).',\r\n },\r\n },\r\n required: ['pr_number'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"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.\";\r\n\r\ninterface ToolInput {\r\n readonly pr_number: number;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['pr_number'] !== 'number') return false;\r\n if (!Number.isFinite(o['pr_number']) || o['pr_number'] < 1 || !Number.isInteger(o['pr_number'])) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\nexport async function handleRejectAndRetry(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Shape: { pr_number: positive integer }.',\r\n );\r\n }\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput: { pr_number: rawInput.pr_number },\r\n cloudBody: { prNumber: rawInput.pr_number },\r\n gateType: PR_GATE_TYPE,\r\n stubReason: PR_STUB_REASON,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_review_merge` \u2014 READ-ONLY consensus pre-merge review.\r\n *\r\n * The safe half of \"verify-before-act\" for the Command Center. Given a PR\r\n * number it:\r\n * 1. Pulls the PR's metadata from the live admin proxy (`/api/v1/admin/pr/list`\r\n * \u2014 CI/blocker status, source, linked bug ids, title).\r\n * 2. Runs the consensus engine (\"should this merge? assess risk\") via the\r\n * `final-deep-verify` gate.\r\n * 3. Returns a `recommendation` (merge / hold / reject) with per-model\r\n * verdicts + reasoning.\r\n *\r\n * It NEVER merges \u2014 `merged` is always `false`. Acting on the recommendation is\r\n * a separate, explicit step (`vo_merge_pr`). A deterministic SAFETY OVERLAY sits\r\n * on top of the consensus verdict: it will not recommend `merge` when the PR has\r\n * any open blocker (failed checks, conflict, draft), and defaults to `hold` when\r\n * consensus is unavailable \u2014 so a single model hallucinating \"merge\" can never\r\n * upgrade a blocked or unverified PR.\r\n *\r\n * Increment 1 (this tool) reviews on METADATA (`basis: 'metadata'`). A later\r\n * increment deepens it with the actual diff + full check-runs.\r\n *\r\n * Requires cloud mode (admin proxy) for PR context + \u22652 model keys for a real\r\n * consensus verdict. Without admin callables it returns `recommendation:\r\n * 'unavailable'`. Every call is logged (gate #7).\r\n */\r\nimport type {\r\n ToolDeps,\r\n ToolResultEnvelope,\r\n ConsensusCallEvent,\r\n PerModelVerdict,\r\n SynthesizedVerdict,\r\n} from '../../types.js';\r\nimport {\r\n buildBaseEvent,\r\n bytesOf,\r\n invalidParams,\r\n jsonContent,\r\n toEventPerModelVerdicts,\r\n toEventSynthesizedVerdict,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport { AdminCallableError } from '../../cloud/admin-callable-client.js';\r\n\r\nexport const TOOL_NAME = 'vo_review_merge';\r\nconst LIST_PATH = '/api/v1/admin/pr/list' as const;\r\n/** Recognized engine gate \u2014 thorough deliberation, fitting a merge decision. */\r\nconst ENGINE_GATE = 'final-deep-verify' as const;\r\n/** Telemetry gate_type for the gate-#7 event (slices merge reviews separately). */\r\nconst EVENT_GATE = 'merge-review' as const;\r\n/** Reason when cloud mode (admin proxy) is not wired, so PR context can't be fetched. */\r\nconst UNAVAILABLE_REASON =\r\n 'vo_review_merge needs cloud mode to fetch PR context \u2014 set VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN in the MCP env. (It is read-only; it never merges.)';\r\n\r\nexport type MergeRecommendation = 'merge' | 'hold' | 'reject' | 'unavailable';\r\n\r\ninterface ReviewedPr {\r\n readonly number: number;\r\n readonly title: string;\r\n readonly source: string | null;\r\n readonly blocker: string | null;\r\n readonly bug_ids: readonly string[];\r\n}\r\n\r\ninterface MergeReviewPayload {\r\n readonly recommendation: MergeRecommendation;\r\n /** Raw consensus verdict (null when consensus didn't run). */\r\n readonly verdict: 'pass' | 'fail' | 'uncertain' | null;\r\n readonly confidence: number | null;\r\n readonly reason: string;\r\n /** What the review is grounded on. Increment 1 = metadata (no diff yet). */\r\n readonly basis: 'metadata';\r\n readonly pr: ReviewedPr | null;\r\n readonly per_model_verdicts: readonly PerModelVerdict[];\r\n readonly synthesized_verdict: SynthesizedVerdict | null;\r\n readonly engine_version: string | null;\r\n readonly degraded: boolean;\r\n /** This tool NEVER merges. Always false \u2014 present so callers can assert it. */\r\n readonly merged: false;\r\n}\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n pr_number: {\r\n type: 'number',\r\n description: 'GitHub pull request number (positive integer) to review for merge.',\r\n },\r\n notes: {\r\n type: 'string',\r\n description: 'Optional reviewer notes / context to factor into the verdict.',\r\n },\r\n },\r\n required: ['pr_number'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"READ-ONLY pre-merge review: given a PR number, fetches its CI/blocker status + source + linked bugs, runs a multi-model consensus 'should this merge?' check, and returns a recommendation (merge | hold | reject) with per-model reasoning. NEVER merges \u2014 acting on it is a separate vo_merge_pr call. A deterministic safety overlay refuses to recommend merge when the PR has an open blocker and defaults to hold when consensus is unavailable. Use BEFORE vo_merge_pr to put a verification gate in front of the Command Center. Increment 1 reviews on metadata; needs cloud mode for PR context + model keys for a real verdict.\";\r\n\r\ninterface ToolInput {\r\n readonly pr_number: number;\r\n readonly notes?: string;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['pr_number'] !== 'number') return false;\r\n if (o['notes'] !== undefined && typeof o['notes'] !== 'string') return false;\r\n return true;\r\n}\r\n\r\nfunction str(v: unknown): string | null {\r\n return typeof v === 'string' && v.length > 0 ? v : null;\r\n}\r\n\r\n/** Extract the fields we depend on from a list-proxy PR entry (defensive on casing). */\r\nfunction readPr(raw: Record<string, unknown>): ReviewedPr {\r\n const bugsRaw = raw['bugIds'] ?? raw['bug_ids'];\r\n const bug_ids = Array.isArray(bugsRaw) ? bugsRaw.filter((b): b is string => typeof b === 'string') : [];\r\n return {\r\n number: Number(raw['number']),\r\n title: str(raw['title']) ?? '(untitled)',\r\n source: str(raw['source']) ?? str(raw['prSource']),\r\n blocker: str(raw['blockerKind']) ?? str(raw['blocker_kind']) ?? str(raw['blockerLabel']),\r\n bug_ids,\r\n };\r\n}\r\n\r\nfunction buildPrompt(pr: ReviewedPr, notes: string | undefined): string {\r\n const lines = [\r\n 'You are a release gatekeeper deciding whether a pull request is safe to MERGE.',\r\n 'Recommend exactly one of: merge / hold / reject. Be conservative \u2014 this is a high-stakes irreversible action.',\r\n '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.',\r\n '',\r\n `PR #${pr.number}: ${pr.title}`,\r\n `Source: ${pr.source ?? 'unknown'}`,\r\n `Linked bug ids: ${pr.bug_ids.length > 0 ? pr.bug_ids.join(', ') : '(none)'}`,\r\n `Open blocker: ${pr.blocker ?? 'none reported'}`,\r\n ];\r\n if (notes !== undefined && notes.length > 0) lines.push('', `Reviewer notes: ${notes}`);\r\n lines.push('', 'Return your verdict (pass = recommend merge, fail = reject, uncertain = hold) with concise reasoning.');\r\n return lines.join('\\n');\r\n}\r\n\r\nexport async function handleReviewMerge(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput) || !Number.isInteger(rawInput.pr_number) || rawInput.pr_number < 1) {\r\n throw invalidParams(TOOL_NAME, 'invalid input. Required: { pr_number: positive integer }. Optional: notes.');\r\n }\r\n const prNumber = rawInput.pr_number;\r\n const normalizedInput: Record<string, unknown> = { pr_number: prNumber };\r\n if (rawInput.notes !== undefined) normalizedInput.notes = rawInput.notes;\r\n const inputJson = JSON.stringify(normalizedInput);\r\n const key = deps.cache.keyFor(TOOL_NAME, normalizedInput);\r\n\r\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\r\n tool: TOOL_NAME,\r\n gateType: EVENT_GATE,\r\n inputHash: key,\r\n inputExcerpt: inputJson.slice(0, 300),\r\n inputSizeBytes: bytesOf(inputJson),\r\n session: deps.session,\r\n now: deps.now(),\r\n });\r\n\r\n const emit = (\r\n payload: MergeReviewPayload,\r\n eventExtra?: Partial<ConsensusCallEvent>,\r\n ): ReturnType<typeof jsonContent> => {\r\n deps.events.append(eventExtra ? { ...baseEvent, ...eventExtra } : baseEvent);\r\n const envelope: ToolResultEnvelope<MergeReviewPayload> = {\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n cache: { hit: false, key },\r\n payload,\r\n };\r\n return jsonContent(envelope);\r\n };\r\n\r\n const emptyPayload = (\r\n recommendation: MergeRecommendation,\r\n reason: string,\r\n pr: ReviewedPr | null,\r\n ): MergeReviewPayload => ({\r\n recommendation,\r\n verdict: null,\r\n confidence: null,\r\n reason,\r\n basis: 'metadata',\r\n pr,\r\n per_model_verdicts: [],\r\n synthesized_verdict: null,\r\n engine_version: null,\r\n degraded: false,\r\n merged: false,\r\n });\r\n\r\n // No cloud mode \u2192 can't fetch PR context. Read-only, so report unavailable.\r\n if (!deps.adminCallables) {\r\n return emit(emptyPayload('unavailable', UNAVAILABLE_REASON, null));\r\n }\r\n\r\n // Fetch the PR's metadata via the live admin proxy.\r\n let pr: ReviewedPr | null = null;\r\n try {\r\n const list = await deps.adminCallables.invoke<unknown>(LIST_PATH, {});\r\n const entries = Array.isArray(list) ? (list as Array<Record<string, unknown>>) : [];\r\n const found = entries.find((p) => Number(p['number']) === prNumber);\r\n if (found) pr = readPr(found);\r\n } catch (err) {\r\n const status = err instanceof AdminCallableError ? ` (HTTP ${err.status})` : '';\r\n const message = err instanceof Error ? err.message : String(err);\r\n return emit(emptyPayload('hold', `could not fetch PR context${status}: ${message.slice(0, 200)}`, null));\r\n }\r\n\r\n if (pr === null) {\r\n return emit(\r\n emptyPayload('hold', `PR #${prNumber} is not among open VO PRs (already merged/closed, or not a VO-source PR).`, null),\r\n );\r\n }\r\n\r\n const hasBlocker = pr.blocker !== null && pr.blocker !== 'none';\r\n\r\n // Run consensus. Engine-client never throws by contract, but wrap defensively.\r\n let result: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\r\n let threw = false;\r\n try {\r\n result = await deps.consensus.run({\r\n gate_type: ENGINE_GATE,\r\n prompt: buildPrompt(pr, rawInput.notes),\r\n caller_context: { pr_number: prNumber, pr_metadata: pr, basis: 'metadata' },\r\n ...(signal !== undefined ? { signal } : {}),\r\n });\r\n } catch (err) {\r\n threw = true;\r\n result = { ok: false, reason: `engine-threw: ${err instanceof Error ? err.message : String(err)}` };\r\n }\r\n\r\n if (!result.ok && result.reason === 'cancelled') {\r\n deps.events.append({\r\n ...baseEvent,\r\n downstream_outcome: { status: 'cancelled', notes: 'mcp notifications/cancelled' },\r\n });\r\n const e = new Error('Request cancelled by client (notifications/cancelled)');\r\n e.name = 'AbortError';\r\n throw e;\r\n }\r\n\r\n // Consensus unavailable \u2192 conservative HOLD (never auto-recommend merge unverified).\r\n if (!result.ok) {\r\n const reason =\r\n `consensus unavailable (${result.reason}) \u2014 holding for manual review.` +\r\n (hasBlocker ? ` PR also has an open blocker: ${pr.blocker}.` : '');\r\n return emit(\r\n { ...emptyPayload('hold', reason, pr), degraded: threw },\r\n threw ? {} : undefined,\r\n );\r\n }\r\n\r\n // Map consensus verdict \u2192 recommendation, then apply the safety overlay.\r\n const verdict = result.synthesized_verdict.verdict;\r\n let recommendation: MergeRecommendation = verdict === 'pass' ? 'merge' : verdict === 'fail' ? 'reject' : 'hold';\r\n let reason =\r\n result.synthesized_verdict.dissent_summary ??\r\n result.synthesized_verdict.reasoning_excerpt ??\r\n `panel agreed (${result.per_model_verdicts.length} models, engine=${result.engine_version})`;\r\n\r\n if (hasBlocker && recommendation === 'merge') {\r\n recommendation = 'hold';\r\n reason = `Consensus leaned merge, but PR has an open blocker (${pr.blocker}) \u2014 holding (safety overlay). ${reason}`;\r\n }\r\n\r\n const perModel = toEventPerModelVerdicts(result.per_model_verdicts);\r\n const synth = toEventSynthesizedVerdict(result.synthesized_verdict);\r\n\r\n const payload: MergeReviewPayload = {\r\n recommendation,\r\n verdict,\r\n confidence: result.synthesized_verdict.confidence,\r\n reason,\r\n basis: 'metadata',\r\n pr,\r\n per_model_verdicts: perModel,\r\n synthesized_verdict: synth,\r\n engine_version: result.engine_version,\r\n degraded: result.degraded,\r\n merged: false,\r\n };\r\n\r\n return emit(payload, {\r\n consensus_confidence: result.synthesized_verdict.confidence,\r\n duration_ms: result.duration_ms,\r\n consensus_engine_version: result.engine_version,\r\n per_model_verdicts: perModel,\r\n synthesized_verdict: synth,\r\n });\r\n}\r\n", "/**\r\n * Directive computation for `vo_report_session_state`.\r\n *\r\n * Pure function of `context_used_pct`. V1 ships VO-fixed thresholds;\r\n * per-tenant configurable thresholds land in V2 per\r\n * `docs/vo/vo-roadmap-2026-05-26.md` \u00A73.4 (open operator question).\r\n *\r\n * Thresholds chosen 2026-05-26 per operator directive (\"at 70% time to\r\n * wrap up\"). 85% is the hard-handoff threshold \u2014 the agent must\r\n * terminate now or risk hitting context exhaustion mid-write.\r\n *\r\n * **Duplication notice.** These thresholds are also defined at\r\n * `cloud-run/vo-control-plane/src/directive.ts` (the server-side\r\n * implementation that this MCP tool will eventually call once\r\n * vo-control-plane is deployed \u2014 Phase 3). They MUST stay in lock-step.\r\n * When the cloud-control-plane is wired (Phase 3), the local stub\r\n * computation here gets removed and this file becomes a thin caller of\r\n * the HTTP endpoint. Until then the duplication is the cost of running\r\n * V1 launch gate #9 before the cloud control plane is live.\r\n *\r\n * Rationale for two thresholds (not one):\r\n * - 70% gives the agent slack to finish the current sub-task before\r\n * writing a handoff. Premature handoff fragments work.\r\n * - 85% leaves ~15% of context budget for the handoff doc itself + any\r\n * final commits/PRs. Tighter than that risks the handoff being\r\n * incomplete.\r\n */\r\n\r\n/** Directive the agent should follow based on its context utilization. */\r\nexport type SessionDirective = 'continue' | 'prepare_handoff' | 'execute_handoff_now';\r\n\r\n/**\r\n * Thresholds for context-usage \u2192 directive transitions.\r\n *\r\n * V1: VO-fixed. V2 plan: per-tenant override via\r\n * `Tenant.context_directive_thresholds`.\r\n *\r\n * **Schema-v1 lock invariant:** changes here must match the\r\n * vo-control-plane sibling exactly. CI doesn't yet enforce this; cover\r\n * via the cross-package directive-threshold-parity test in PR #B3.\r\n */\r\nexport const SESSION_DIRECTIVE_THRESHOLDS = {\r\n prepare_handoff_pct: 70,\r\n execute_handoff_now_pct: 85,\r\n} as const;\r\n\r\n/**\r\n * Compute the directive given current context usage.\r\n *\r\n * Inputs outside [0, 100] are clamped to the nearest valid value\r\n * (defensive \u2014 schema validation rejects them upstream but routes that\r\n * skip validation should not crash).\r\n */\r\nexport function computeDirective(context_used_pct: number): SessionDirective {\r\n const clamped = Math.max(0, Math.min(100, context_used_pct));\r\n if (clamped >= SESSION_DIRECTIVE_THRESHOLDS.execute_handoff_now_pct) {\r\n return 'execute_handoff_now';\r\n }\r\n if (clamped >= SESSION_DIRECTIVE_THRESHOLDS.prepare_handoff_pct) {\r\n return 'prepare_handoff';\r\n }\r\n return 'continue';\r\n}\r\n\r\n/** Human-readable message paired with each directive. */\r\nexport function directiveMessage(directive: SessionDirective): string {\r\n switch (directive) {\r\n case 'continue':\r\n return 'Context usage healthy. Continue work.';\r\n case 'prepare_handoff':\r\n return `Context usage at or above ${SESSION_DIRECTIVE_THRESHOLDS.prepare_handoff_pct}%. Begin wrapping up the current sub-task and prepare a handoff doc covering current state, next steps, blockers, and verification needed.`;\r\n case 'execute_handoff_now':\r\n return `Context usage at or above ${SESSION_DIRECTIVE_THRESHOLDS.execute_handoff_now_pct}%. Stop work, write the handoff doc using the template at ~/.vo/templates/handoff.md to ~/.vo/handoffs/<session_id>-<ts>.md, then call endSession with terminal_status='handed_off' and the handoff path.`;\r\n }\r\n}\r\n\r\n/**\r\n * Compute the suggested handoff destination path for a session. Only\r\n * meaningful when directive === 'execute_handoff_now'; returned by the\r\n * MCP tool as a convenience so the caller doesn't have to build the\r\n * path themselves.\r\n *\r\n * The MCP tool does NOT create the file \u2014 that's the caller's job. It\r\n * just suggests the canonical location.\r\n */\r\nexport function suggestedHandoffPath(session_id: string, isoTimestamp: string): string {\r\n // ISO timestamps include `:` which is invalid in Windows filenames. Replace\r\n // with `-` so the same path is portable.\r\n const safeTs = isoTimestamp.replace(/[:.]/g, '-');\r\n return `~/.vo/handoffs/${session_id}-${safeTs}.md`;\r\n}\r\n", "/**\r\n * Tool: `vo_report_session_state`\r\n *\r\n * Implements V1 launch gate #9 (fleet context lifecycle management) per\r\n * `docs/vo/vo-roadmap-2026-05-26.md` \u00A73.4. Accepts a per-session context\r\n * report from a host agent (Claude Code, Cursor, Continue, Codex) and\r\n * returns a directive: continue | prepare_handoff | execute_handoff_now.\r\n *\r\n * V1 backend: **stub-local**. Computes the directive purely from\r\n * `context_used_pct` against the documented thresholds (70% / 85%)\r\n * without making a network call. The cloud-control-plane HTTP endpoint\r\n * is the Phase 3 wiring target \u2014 see `cloud-run/vo-control-plane/`.\r\n * The response shape stays stable across the cutover (`backend_mode`\r\n * field in the payload tells the caller which mode produced the\r\n * verdict, so observability + telemetry can detect drift).\r\n *\r\n * Sibling SessionState entity at `cloud-run/vo-control-plane/src/schema/session-v1.ts`\r\n * was shipped in PR #5409. The cloud-side `computeDirective` at\r\n * `cloud-run/vo-control-plane/src/directive.ts` is mirrored here in\r\n * `./directive.ts` \u2014 both files MUST share the same thresholds until\r\n * the cutover.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport {\r\n invalidParams,\r\n jsonContent,\r\n type ToolInputSchema,\r\n} from '../common.js';\r\nimport {\r\n computeDirective,\r\n directiveMessage,\r\n suggestedHandoffPath,\r\n type SessionDirective,\r\n} from './directive.js';\r\n\r\nexport const TOOL_NAME = 'vo_report_session_state';\r\n\r\nconst VALID_AGENT_TYPES = ['claude-code', 'codex', 'cursor', 'continue'] as const;\r\ntype AgentType = (typeof VALID_AGENT_TYPES)[number];\r\n\r\n/** Max length of `current_goal` accepted \u2014 anything longer is truncated, not rejected. */\r\nconst MAX_GOAL_CHARS = 500;\r\n/** Max items in `recent_files_touched`. Inputs over this are rejected. */\r\nconst MAX_RECENT_FILES = 20;\r\n/** Max items in `recent_tool_uses`. Inputs over this are rejected. */\r\nconst MAX_RECENT_TOOLS = 50;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n operator_id: {\r\n type: 'string',\r\n minLength: 1,\r\n description: 'Stable identifier (UUID expected) of the operator owning this session.',\r\n },\r\n session_id: {\r\n type: 'string',\r\n minLength: 1,\r\n description: 'Stable identifier (UUID expected) of the session reporting state.',\r\n },\r\n agent_type: {\r\n type: 'string',\r\n enum: [...VALID_AGENT_TYPES],\r\n description: 'Which host agent runtime is reporting (claude-code | codex | cursor | continue).',\r\n },\r\n context_used_pct: {\r\n type: 'number',\r\n minimum: 0,\r\n maximum: 100,\r\n description: 'Current context-window utilization, 0-100. Used to compute the directive.',\r\n },\r\n current_goal: {\r\n type: 'string',\r\n maxLength: MAX_GOAL_CHARS,\r\n description: 'Optional one-line goal statement. Truncated server-side at 500 chars.',\r\n },\r\n recent_files_touched: {\r\n type: 'array',\r\n items: { type: 'string' },\r\n maxItems: MAX_RECENT_FILES,\r\n description: `Optional list of recent file paths touched in the session. Capped at ${MAX_RECENT_FILES} items.`,\r\n },\r\n recent_tool_uses: {\r\n type: 'array',\r\n items: { type: 'string' },\r\n maxItems: MAX_RECENT_TOOLS,\r\n description: `Optional list of recent tool names invoked in the session. Capped at ${MAX_RECENT_TOOLS} items.`,\r\n },\r\n },\r\n required: ['operator_id', 'session_id', 'agent_type', 'context_used_pct'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"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).\";\r\n\r\ninterface ToolInput {\r\n readonly operator_id: string;\r\n readonly session_id: string;\r\n readonly agent_type: AgentType;\r\n readonly context_used_pct: number;\r\n readonly current_goal?: string;\r\n readonly recent_files_touched?: readonly string[];\r\n readonly recent_tool_uses?: readonly string[];\r\n}\r\n\r\nfunction isStringArray(v: unknown, maxItems: number): v is readonly string[] {\r\n if (!Array.isArray(v)) return false;\r\n if (v.length > maxItems) return false;\r\n return v.every((item) => typeof item === 'string');\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (typeof o['operator_id'] !== 'string' || o['operator_id'].length === 0) return false;\r\n if (typeof o['session_id'] !== 'string' || o['session_id'].length === 0) return false;\r\n if (typeof o['agent_type'] !== 'string') return false;\r\n if (!(VALID_AGENT_TYPES as readonly string[]).includes(o['agent_type'])) return false;\r\n if (typeof o['context_used_pct'] !== 'number') return false;\r\n if (!Number.isFinite(o['context_used_pct'])) return false;\r\n if (o['context_used_pct'] < 0 || o['context_used_pct'] > 100) return false;\r\n if (o['current_goal'] !== undefined && typeof o['current_goal'] !== 'string') return false;\r\n if (o['recent_files_touched'] !== undefined && !isStringArray(o['recent_files_touched'], MAX_RECENT_FILES)) {\r\n return false;\r\n }\r\n if (o['recent_tool_uses'] !== undefined && !isStringArray(o['recent_tool_uses'], MAX_RECENT_TOOLS)) {\r\n return false;\r\n }\r\n return true;\r\n}\r\n\r\n/**\r\n * Output payload shape. Schema-v1 lock \u2014 new fields MUST be additive +\r\n * optional, never required-shape changes. Mirrors V1 gate #9 contract.\r\n */\r\nexport interface ReportSessionStatePayload {\r\n /** Directive the agent should follow this turn. */\r\n readonly directive: SessionDirective;\r\n /** Human-readable message explaining the directive. */\r\n readonly message: string;\r\n /** When directive === 'execute_handoff_now', the canonical handoff doc path. Otherwise absent. */\r\n readonly handoff_path?: string;\r\n /** Echoed for caller correlation. */\r\n readonly session_id: string;\r\n /** Echoed for caller correlation. */\r\n readonly operator_id: string;\r\n /** Echoed for caller correlation. */\r\n readonly agent_type: AgentType;\r\n /** Echoed back so the caller can confirm the value was received. */\r\n readonly context_used_pct: number;\r\n /**\r\n * Which backend produced this verdict. V1: 'stub-local' always.\r\n * Phase 3: 'cloud-control-plane' when wired to the deployed HTTP API.\r\n */\r\n readonly backend_mode: 'stub-local' | 'cloud-control-plane';\r\n /** Server-side ISO timestamp. */\r\n readonly ts: string;\r\n /** Hard-locked at 1. */\r\n readonly schema_version: 1;\r\n}\r\n\r\n/**\r\n * Cloud mode detection \u2014 checks if VO_CONTROL_PLANE_URL and\r\n * VO_CONTROL_PLANE_ADMIN_TOKEN are set. When both are present, cloud mode\r\n * is available; otherwise fall back to stub-local.\r\n */\r\ninterface CloudConfig {\r\n readonly url: string;\r\n readonly token: string;\r\n}\r\n\r\nfunction getCloudConfig(): CloudConfig | null {\r\n const url = process.env['VO_CONTROL_PLANE_URL'];\r\n const token = process.env['VO_CONTROL_PLANE_ADMIN_TOKEN'];\r\n if (!url || !token) return null;\r\n return { url, token };\r\n}\r\n\r\n/**\r\n * Cloud-mode response shape from `POST /api/v1/session/:id/report-state`.\r\n * Matches `cloud-run/vo-control-plane/src/routes/session.ts` handleReportSessionState.\r\n */\r\ninterface CloudReportStateResponse {\r\n readonly ok: boolean;\r\n readonly session?: {\r\n readonly session_id: string;\r\n readonly operator_id: string;\r\n readonly agent_type: string;\r\n readonly context_used_pct: number;\r\n readonly current_goal: string;\r\n readonly last_directive: SessionDirective;\r\n readonly last_seen_at: string;\r\n };\r\n readonly directive?: {\r\n readonly action: SessionDirective;\r\n readonly message: string;\r\n };\r\n readonly error?: string;\r\n}\r\n\r\nasync function tryCloudReportState(\r\n cloud: CloudConfig,\r\n input: ToolInput,\r\n): Promise<ReportSessionStatePayload | null> {\r\n try {\r\n const body: Record<string, unknown> = {\r\n context_used_pct: input.context_used_pct,\r\n };\r\n if (input.current_goal !== undefined) body['current_goal'] = input.current_goal;\r\n if (input.recent_files_touched !== undefined) {\r\n body['recent_files_touched'] = input.recent_files_touched;\r\n }\r\n if (input.recent_tool_uses !== undefined) {\r\n body['recent_tool_uses'] = input.recent_tool_uses;\r\n }\r\n\r\n const url = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;\r\n const response = await fetch(url, {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${cloud.token}`,\r\n },\r\n body: JSON.stringify(body),\r\n });\r\n\r\n if (!response.ok) {\r\n // Server error or 404 (session not found/terminal) \u2014 fall back to stub\r\n return null;\r\n }\r\n\r\n const data: CloudReportStateResponse = await response.json() as CloudReportStateResponse;\r\n if (!data.ok || !data.session || !data.directive) {\r\n return null;\r\n }\r\n\r\n // Map cloud response to tool payload shape\r\n return {\r\n directive: data.directive.action,\r\n message: data.directive.message,\r\n session_id: data.session.session_id,\r\n operator_id: data.session.operator_id,\r\n agent_type: input.agent_type,\r\n context_used_pct: data.session.context_used_pct,\r\n backend_mode: 'cloud-control-plane',\r\n ts: data.session.last_seen_at,\r\n schema_version: 1,\r\n ...(data.directive.action === 'execute_handoff_now'\r\n ? { handoff_path: suggestedHandoffPath(data.session.session_id, data.session.last_seen_at) }\r\n : {}),\r\n };\r\n } catch {\r\n // Network error, parse error, etc. \u2014 fail open to stub-local\r\n return null;\r\n }\r\n}\r\n\r\nexport async function handleReportSessionState(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Required fields: operator_id (non-empty string), session_id (non-empty string), ' +\r\n `agent_type (one of: ${VALID_AGENT_TYPES.join(' | ')}), context_used_pct (number 0-100). ` +\r\n `Optional: current_goal (string \u2264${MAX_GOAL_CHARS} chars), ` +\r\n `recent_files_touched (string[] \u2264${MAX_RECENT_FILES}), ` +\r\n `recent_tool_uses (string[] \u2264${MAX_RECENT_TOOLS}).`,\r\n );\r\n }\r\n\r\n // Try cloud mode first if configured\r\n const cloud = getCloudConfig();\r\n if (cloud !== null) {\r\n const cloudPayload = await tryCloudReportState(cloud, rawInput);\r\n if (cloudPayload !== null) {\r\n return jsonContent(cloudPayload);\r\n }\r\n // Cloud call failed \u2014 fall through to stub-local\r\n }\r\n\r\n // Stub-local fallback\r\n const directive = computeDirective(rawInput.context_used_pct);\r\n const ts = deps.now().toISOString();\r\n const payload: ReportSessionStatePayload = {\r\n directive,\r\n message: directiveMessage(directive),\r\n session_id: rawInput.session_id,\r\n operator_id: rawInput.operator_id,\r\n agent_type: rawInput.agent_type,\r\n context_used_pct: rawInput.context_used_pct,\r\n backend_mode: 'stub-local',\r\n ts,\r\n schema_version: 1,\r\n ...(directive === 'execute_handoff_now'\r\n ? { handoff_path: suggestedHandoffPath(rawInput.session_id, ts) }\r\n : {}),\r\n };\r\n\r\n return jsonContent(payload);\r\n}\r\n", "/**\r\n * Tool: `vo_spawn_successor` \u2014 Mode B auto-handoff (Phase 3 PR-AB;\r\n * `docs/vo/vo-roadmap-2026-05-26.md` \u00A73.4 step 5 Mode B).\r\n *\r\n * Spawns a DETACHED headless `claude -p` successor with the handoff doc\r\n * pre-injected into its initial prompt, so a context-exhausted session can hand\r\n * the lane to a fresh agent without the operator re-orienting it. The V3\r\n * PR-watcher spawn pattern: prompt via STDIN (never argv \u2014 on Windows `claude`\r\n * is a .cmd shim needing shell:true, and argv+shell would be an injection\r\n * hole), `detached` + `unref` so the MCP server doesn't hold the child,\r\n * stdout/stderr appended to a log under `~/.vo/successors/`.\r\n *\r\n * ADR-001 boundary: the successor is a WORKER continuing the lane under the\r\n * same gates as any session (verify-before-act, human merge approval). This\r\n * tool only ever acts when explicitly invoked \u2014 it is NOT an autonomous\r\n * trigger (`project_no_automatic_agent_triggers`).\r\n */\r\nimport { spawn } from 'node:child_process';\r\nimport { homedir } from 'node:os';\r\nimport { join } from 'node:path';\r\nimport { existsSync, mkdirSync, openSync, readFileSync, readdirSync, statSync } from 'node:fs';\r\nimport type { ToolDeps } from '../../types.js';\r\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\r\n\r\nexport const TOOL_NAME = 'vo_spawn_successor';\r\n\r\n/** Hard cap on the handoff size injected into the successor prompt. */\r\nconst MAX_HANDOFF_BYTES = 64_000;\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n handoff_path: {\r\n type: 'string',\r\n description: 'Path to the handoff doc to pre-inject. Default: the newest .md in ~/.vo/handoffs/.',\r\n },\r\n goal: {\r\n type: 'string',\r\n description: 'Optional one-line goal override appended after the handoff.',\r\n },\r\n cwd: {\r\n type: 'string',\r\n description: 'Working directory for the successor (default: the repo the handoff names, else process cwd).',\r\n },\r\n max_turns: {\r\n type: 'number',\r\n description: 'Optional --max-turns bound for the successor.',\r\n },\r\n },\r\n required: [],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n 'Mode B auto-handoff (roadmap \u00A73.4): spawn a DETACHED headless `claude -p` successor with a handoff doc pre-injected into its prompt. Defaults to the newest handoff in ~/.vo/handoffs/. Returns {spawned, pid, log_path, handoff_path}. The successor works under the same gates as any session (ADR-001: verify-before-act, human merge approval) \u2014 this tool never fires autonomously.';\r\n\r\ninterface ToolInput {\r\n readonly handoff_path?: string;\r\n readonly goal?: string;\r\n readonly cwd?: string;\r\n readonly max_turns?: number;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (o['handoff_path'] !== undefined && typeof o['handoff_path'] !== 'string') return false;\r\n if (o['goal'] !== undefined && typeof o['goal'] !== 'string') return false;\r\n if (o['cwd'] !== undefined && typeof o['cwd'] !== 'string') return false;\r\n if (o['max_turns'] !== undefined && typeof o['max_turns'] !== 'number') return false;\r\n return true;\r\n}\r\n\r\n/** Newest .md in ~/.vo/handoffs (by mtime), or null. Exported for tests. */\r\nexport function newestHandoff(dir = join(homedir(), '.vo', 'handoffs')): string | null {\r\n try {\r\n const entries = readdirSync(dir)\r\n .filter((f) => f.endsWith('.md'))\r\n .map((f) => ({ f, m: statSync(join(dir, f)).mtimeMs }))\r\n .sort((a, b) => b.m - a.m);\r\n return entries.length > 0 && entries[0] ? join(dir, entries[0].f) : null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\n/** Compose the successor's initial prompt. Exported for tests. */\r\n/**\r\n * The mandatory onboarding reading list every dispatched/successor agent must\r\n * read first. Mirrors AGENTS.md's \"spawned subagent\" contract + the VO doctrine\r\n * docs that CLAUDE.md only references (so they are NOT auto-loaded). Kept in\r\n * sync with scripts/virtual-office/code-runner/dispatch-onboarding.mjs.\r\n */\r\nconst MANDATORY_READS = [\r\n 'CLAUDE.md + AGENTS.md + README.md (repo root)',\r\n 'docs/current/virtual-office-agent-charter.md, -operating-model.md, -test-architect.md',\r\n 'docs/current/evidence-grounded-consensus-testing.md',\r\n 'docs/vo/ADR-001-* (verify + sign, human approves merge; no autonomous bot-merge / headless triggers) + docs/vo/vo-adr-002-two-plane-moat.md',\r\n 'docs/vo/vo-roadmap-2026-05-26.md (read the Change log tail for current state)',\r\n 'the operator memory index ~/.claude/projects/C--Users-greyl/memory/MEMORY.md',\r\n];\r\n\r\nexport function buildSuccessorPrompt(handoffMarkdown: string, goal?: string): string {\r\n const reads = MANDATORY_READS.map((r, i) => ` ${i + 1}. ${r}`).join('\\n');\r\n const lines = [\r\n 'You are the SUCCESSOR agent for a Virtual Office lane. The previous session',\r\n 'exhausted its context and wrote the handoff below. Read it fully, verify its',\r\n '\"verification needed\" items against live state (a handoff is a claim, not',\r\n 'evidence \u2014 verify via `git show origin/main:<path>`), then continue the lane.',\r\n '',\r\n 'MANDATORY READS before writing any code (NOT all auto-loaded \u2014 open them):',\r\n reads,\r\n '',\r\n 'NON-NEGOTIABLES: multi-model consensus verification is the core; test honesty',\r\n '(verified-answer-only, no fake green); verify-before-act + human merge approval;',\r\n 'never a full functions-shared deploy; Gen2 only; work in a worktree on your own',\r\n 'branch; finish line is MERGED + DEPLOYED + LIVE-VERIFIED, and VO changes update',\r\n 'the roadmap in the same PR.',\r\n '',\r\n '--- HANDOFF ---',\r\n handoffMarkdown,\r\n '--- END HANDOFF ---',\r\n ];\r\n if (goal && goal.trim().length > 0) lines.push('', `OPERATOR GOAL OVERRIDE: ${goal.trim()}`);\r\n return lines.join('\\n');\r\n}\r\n\r\n/** Build argv for the detached successor. Exported for tests. */\r\nexport function buildSuccessorArgs(maxTurns?: number): string[] {\r\n const args = ['-p', '--permission-mode', 'acceptEdits'];\r\n if (Number.isInteger(maxTurns) && (maxTurns as number) > 0) {\r\n args.push('--max-turns', String(maxTurns));\r\n }\r\n return args;\r\n}\r\n\r\nexport type SpawnLike = (\r\n cmd: string,\r\n args: string[],\r\n opts: Record<string, unknown>,\r\n) => { pid?: number; stdin: { write(s: string): void; end(): void }; unref(): void; on(ev: string, cb: (e: Error) => void): void };\r\n\r\nexport async function handleSpawnSuccessor(\r\n _deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n spawnImpl: SpawnLike = spawn as unknown as SpawnLike,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(TOOL_NAME, 'invalid input. Optional: { handoff_path, goal, cwd, max_turns }.');\r\n }\r\n const handoffPath = rawInput.handoff_path?.trim() || newestHandoff();\r\n if (!handoffPath || !existsSync(handoffPath)) {\r\n return jsonContent({\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n payload: {\r\n spawned: false,\r\n reason: rawInput.handoff_path\r\n ? `handoff not found: ${rawInput.handoff_path}`\r\n : 'no handoff docs in ~/.vo/handoffs \u2014 write one first (the 85% directive does this)',\r\n },\r\n });\r\n }\r\n const handoff = readFileSync(handoffPath, 'utf8').slice(0, MAX_HANDOFF_BYTES);\r\n const prompt = buildSuccessorPrompt(handoff, rawInput.goal);\r\n\r\n // Env override keeps tests hermetic (no writes into the real home dir).\r\n const logDir = process.env['VO_MCP_SUCCESSOR_LOG_DIR']?.trim() || join(homedir(), '.vo', 'successors');\r\n mkdirSync(logDir, { recursive: true });\r\n const logPath = join(logDir, `successor-${Date.now()}.log`);\r\n const logFd = openSync(logPath, 'a');\r\n\r\n const child = spawnImpl('claude', buildSuccessorArgs(rawInput.max_turns), {\r\n cwd: rawInput.cwd?.trim() || process.cwd(),\r\n detached: true,\r\n stdio: ['pipe', logFd, logFd],\r\n // Windows: `claude` is a .cmd shim \u2014 needs a shell to resolve. The prompt\r\n // goes via STDIN below, never argv, so the shell never sees it.\r\n shell: process.platform === 'win32',\r\n windowsHide: true,\r\n });\r\n let spawnError: string | null = null;\r\n child.on('error', (e: Error) => {\r\n spawnError = e.message;\r\n });\r\n try {\r\n child.stdin.write(prompt);\r\n child.stdin.end();\r\n } catch {\r\n /* 'error' handler captures the failure */\r\n }\r\n child.unref();\r\n // Give a synchronous ENOENT a beat to surface before reporting success.\r\n await new Promise((r) => setTimeout(r, 150));\r\n\r\n return jsonContent({\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n payload: spawnError\r\n ? { spawned: false, reason: `spawn failed: ${spawnError}`, handoff_path: handoffPath }\r\n : { spawned: true, pid: child.pid ?? null, log_path: logPath, handoff_path: handoffPath },\r\n });\r\n}\r\n", "/**\r\n * Shared helpers for the concierge/* MCP tool family.\r\n *\r\n * Mirrors the heal/* and pr/* family pattern (common-heal.ts /\r\n * common-pr.ts): one helper module per family carrying the gate type +\r\n * stub reason constants and a stub-mode envelope builder. Tool handlers\r\n * (`dispatch.ts`) call `buildCloudOrStubResponse` from `../cloud-call.ts`\r\n * which branches on `deps.adminCallables` to choose stub vs cloud path.\r\n *\r\n * The concierge family routes provider-scoped knowledge packs by\r\n * `tenant.cloud_provider`:\r\n *\r\n * - Caller provides `pack` directly \u2192 vo-control-plane returns that\r\n * pack's README + file index\r\n * - Caller provides `tenant_id` only \u2192 vo-control-plane looks up the\r\n * Tenant's `cloud_provider` and maps to the right pack (defaults to\r\n * `gcp` if `cloud_provider === 'unspecified'`)\r\n * - Caller provides both \u2192 `pack` wins (explicit overrides routing)\r\n *\r\n * The wire body (camelCase) passed to vo-control-plane mirrors that\r\n * shape: `{ pack?, tenantId? }`.\r\n *\r\n * V1 ships as a stub because the server-side endpoint\r\n * `/api/v1/admin/concierge/dispatch` is not yet implemented in\r\n * vo-control-plane. The MCP tool surface is live so cross-vendor MCP\r\n * clients (Cursor / Continue / Codex) have the contract; the cloud-mode\r\n * wiring lands in a separate vo-control-plane PR.\r\n */\r\n\r\n/** Reason text shared by every concierge-family stub. */\r\nexport const CONCIERGE_STUB_REASON =\r\n '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.';\r\n\r\n/**\r\n * Gate type for telemetry events. Distinct from `admin-action` (heal/pr\r\n * families) and from `kb-prefilter` (architecture-review family) so\r\n * fleet observability can slice concierge traffic separately.\r\n */\r\nexport const CONCIERGE_GATE_TYPE = 'concierge-dispatch' as const;\r\n\r\n/**\r\n * The 8 packs currently shipped under `vo-claude-plugin/concierge/*` as of\r\n * 2026-05-29. Kept here for stub-mode reference + cross-vendor consumers\r\n * that want to enumerate available packs without a server round-trip.\r\n *\r\n * **Sync invariant:** when packs change in `vo-claude-plugin/concierge/`,\r\n * update this list AND `vo-claude-plugin/commands/vo-concierge.md` AND\r\n * the server-side authoritative list (vo-control-plane). The vo-control-\r\n * plane endpoint is the source of truth at runtime; this constant is for\r\n * stub-mode + offline enumeration only.\r\n */\r\nexport const KNOWN_CONCIERGE_PACKS = [\r\n 'gcp',\r\n 'firebase',\r\n 'aws',\r\n 'cloudflare',\r\n 'vercel',\r\n 'netlify',\r\n 'tax',\r\n 'hybrid',\r\n] as const;\r\n\r\nexport type ConciergePackName = typeof KNOWN_CONCIERGE_PACKS[number];\r\n\r\n/** Type-guard for ConciergePackName. */\r\nexport function isKnownConciergePack(value: unknown): value is ConciergePackName {\r\n return typeof value === 'string' && (KNOWN_CONCIERGE_PACKS as readonly string[]).includes(value);\r\n}\r\n", "/**\r\n * Tool: `vo_concierge_dispatch`\r\n *\r\n * Provider-scoped knowledge-pack dispatcher. Cross-vendor MCP equivalent\r\n * of the Claude-Code-only `/vo-concierge` slash command in\r\n * `vo-claude-plugin/commands/vo-concierge.md`. Cursor, Continue, and\r\n * Codex have no slash-command surface \u2014 they reach packs through this\r\n * tool instead.\r\n *\r\n * Input shape:\r\n * {\r\n * pack?: 'gcp' | 'firebase' | 'aws' | 'cloudflare' | 'vercel'\r\n * | 'netlify' | 'tax' | 'hybrid' // explicit pack\r\n * tenant_id?: string // route via tenant.cloud_provider\r\n * }\r\n *\r\n * Routing precedence (resolved server-side by vo-control-plane):\r\n * 1. `pack` if provided \u2192 return that pack\r\n * 2. `tenant_id` if provided \u2192 look up Tenant.cloud_provider, map to pack\r\n * 3. neither \u2192 server-side default behavior (lists available packs)\r\n *\r\n * Output shape (cloud mode \u2014 when vo-control-plane returns success):\r\n * {\r\n * verdict: 'pass',\r\n * response_data: {\r\n * pack_name: ConciergePackName,\r\n * readme_markdown: string, // README.md verbatim\r\n * file_index: Array<{\r\n * filename: string, // e.g. 'iam-and-secrets.md'\r\n * description: string, // one-liner\r\n * }>,\r\n * routed_from?: 'pack' | 'tenant_cloud_provider' | 'default',\r\n * }\r\n * }\r\n *\r\n * Returns `verdict: 'unimplemented'` only when this MCP runtime has no\r\n * cloud mode (adminCallables null \u2014 set VO_CONTROL_PLANE_URL +\r\n * VO_CONTROL_PLANE_ADMIN_TOKEN to enable). The server-side\r\n * `/api/v1/admin/concierge/dispatch` endpoint is built + deployed\r\n * (vo-control-plane #5724/#5734); in cloud mode this returns\r\n * `verdict: 'pass'` with the routed pack content.\r\n *\r\n * Cache: stub responses are NOT cached (HP2-1 hardening \u2014 same rule as\r\n * other unimplemented envelopes); cloud-mode `pass` responses are also\r\n * not cached in V1 because pack content is small (~5 KB) and changes\r\n * with every plugin release. PR 4 of the concierge series can revisit.\r\n */\r\nimport type { ToolDeps } from '../../types.js';\r\nimport type { jsonContent } from '../common.js';\r\nimport { invalidParams, type ToolInputSchema } from '../common.js';\r\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\r\nimport {\r\n CONCIERGE_GATE_TYPE,\r\n CONCIERGE_STUB_REASON,\r\n KNOWN_CONCIERGE_PACKS,\r\n isKnownConciergePack,\r\n} from './common-concierge.js';\r\n\r\nexport const TOOL_NAME = 'vo_concierge_dispatch';\r\nconst CALLABLE_NAME = 'voConciergeDispatch' as const;\r\nconst ADMIN_PATH = '/api/v1/admin/concierge/dispatch' as const;\r\n\r\ninterface ConciergeDispatchInput {\r\n readonly pack?: string;\r\n readonly tenant_id?: string;\r\n}\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n pack: {\r\n type: 'string',\r\n description: `Explicit knowledge pack name. One of: ${KNOWN_CONCIERGE_PACKS.join(', ')}. When provided, overrides tenant-based routing. Omit to let server route by tenant_id.`,\r\n enum: [...KNOWN_CONCIERGE_PACKS],\r\n },\r\n tenant_id: {\r\n type: 'string',\r\n description: 'Tenant identifier. When provided (and pack is omitted), the server looks up Tenant.cloud_provider and dispatches to the matching pack. Ignored when pack is also provided.',\r\n },\r\n },\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n \"Dispatches a provider-scoped knowledge pack (gcp | firebase | aws | cloudflare | vercel | netlify | tax | hybrid). Cross-vendor MCP equivalent of the /vo-concierge Claude-Code slash command. Route explicitly via `pack`, or via tenant.cloud_provider by passing `tenant_id`. Returns the pack's README (`readme_markdown`) + file index. In cloud mode, dispatches via vo-control-plane and returns `verdict: 'pass'` with the pack/directory data; without cloud config, returns `verdict: 'unimplemented'`.\";\r\n\r\nfunction isToolInput(v: unknown): v is ConciergeDispatchInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const obj = v as Record<string, unknown>;\r\n if (obj.pack !== undefined && typeof obj.pack !== 'string') return false;\r\n if (obj.tenant_id !== undefined && typeof obj.tenant_id !== 'string') return false;\r\n return true;\r\n}\r\n\r\nexport async function handleConciergeDispatch(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Expected { pack?: string, tenant_id?: string }.',\r\n );\r\n }\r\n\r\n // Validate `pack` value early so cross-vendor clients get a clear\r\n // error for unknown packs without round-tripping to cloud mode. We\r\n // tolerate empty/missing \u2014 that's the \"route via tenant_id\" path.\r\n if (rawInput.pack !== undefined && rawInput.pack !== '' && !isKnownConciergePack(rawInput.pack)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n `unknown pack: ${JSON.stringify(rawInput.pack)}. Known packs: ${KNOWN_CONCIERGE_PACKS.join(', ')}.`,\r\n );\r\n }\r\n\r\n // Build the normalized input passed through to cloud / stub. Strip\r\n // undefined fields so log excerpts are clean.\r\n const normalizedInput: Record<string, unknown> = {};\r\n if (rawInput.pack) normalizedInput.pack = rawInput.pack;\r\n if (rawInput.tenant_id) normalizedInput.tenant_id = rawInput.tenant_id;\r\n\r\n // Cloud body uses camelCase per vo-control-plane convention.\r\n const cloudBody: Record<string, unknown> = {};\r\n if (rawInput.pack) cloudBody.pack = rawInput.pack;\r\n if (rawInput.tenant_id) cloudBody.tenantId = rawInput.tenant_id;\r\n\r\n return buildCloudOrStubResponse({\r\n toolName: TOOL_NAME,\r\n callableName: CALLABLE_NAME,\r\n adminPath: ADMIN_PATH,\r\n normalizedInput,\r\n cloudBody,\r\n // Concierge dispatch returns `{ok, mode, routed_from, pack|packs}`,\r\n // NOT the callable-proxy `{ok, callable, result}` shape \u2014 take the\r\n // raw envelope as response_data instead of mandating `.result`.\r\n rawEnvelope: true,\r\n gateType: CONCIERGE_GATE_TYPE,\r\n stubReason: CONCIERGE_STUB_REASON,\r\n // Read-only pack fetch \u2014 stays live under VO_ADMIN_CALLABLES_READONLY.\r\n readOnly: true,\r\n deps,\r\n });\r\n}\r\n", "/**\r\n * Tool: `vo_sync_config` \u2014 cloud memory sync (Increment 7, Lane 2 PR2).\r\n *\r\n * Syncs memory entries between local `~/.claude/projects/<slug>/memory/` and\r\n * the cloud control-plane `/api/v1/agent-config/memory/me` routes (merged #6593).\r\n *\r\n * Auth: OPERATOR-role only. Uses the stored `vo_credential` from `vo-mcp login`\r\n * (NOT the admin god-token). Reads from `credential-store.ts` via `auth-token-source.ts`\r\n * (same pattern as all cloud tools). Fails closed when no credential is stored.\r\n *\r\n * Actions:\r\n * - **pull**: GET `/me` \u2192 write each entry's `content` to local memory files.\r\n * - **push**: read local memory dir \u2192 GET `/me` to map file_name\u2192memory_id \u2192\r\n * PUT `/:memory_id` if exists, else POST `/me` (idempotent; entry_type='index'\r\n * for MEMORY.md else 'topic').\r\n */\r\nimport { homedir } from 'node:os';\r\nimport { join } from 'node:path';\r\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';\r\nimport type { ToolDeps } from '../../types.js';\r\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\r\n\r\nexport const TOOL_NAME = 'vo_sync_config';\r\n\r\nexport const inputSchema: ToolInputSchema = {\r\n type: 'object',\r\n properties: {\r\n action: {\r\n type: 'string',\r\n enum: ['pull', 'push'],\r\n description: 'pull: download cloud memory to local files. push: upload local files to cloud.',\r\n },\r\n cwd: {\r\n type: 'string',\r\n description: 'Working directory to derive project slug from (default: process.cwd()).',\r\n },\r\n },\r\n required: ['action'],\r\n additionalProperties: false,\r\n};\r\n\r\nexport const description =\r\n '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.';\r\n\r\ninterface ToolInput {\r\n readonly action: 'pull' | 'push';\r\n readonly cwd?: string;\r\n}\r\n\r\nfunction isToolInput(v: unknown): v is ToolInput {\r\n if (typeof v !== 'object' || v === null) return false;\r\n const o = v as Record<string, unknown>;\r\n if (o['action'] !== 'pull' && o['action'] !== 'push') return false;\r\n if (o['cwd'] !== undefined && typeof o['cwd'] !== 'string') return false;\r\n return true;\r\n}\r\n\r\n/**\r\n * Derive the Claude Code project slug from a cwd. Mirrors how Claude Code\r\n * generates project dirs under `~/.claude/projects/`.\r\n * E.g. `C:/my apps/Nexus` \u2192 `C--my-apps--Nexus`\r\n */\r\nexport function deriveProjectSlug(cwd: string): string {\r\n const normalized = cwd.replace(/\\\\/g, '/');\r\n return normalized\r\n .replace(/^([A-Z]):/i, (_, drive) => `${drive.toUpperCase()}-`)\r\n .replace(/\\/$/g, '')\r\n .split('/')\r\n .join('--')\r\n .replace(/\\s+/g, '-');\r\n}\r\n\r\n/**\r\n * Get the local memory directory path for the given cwd.\r\n */\r\nexport function getMemoryDir(cwd: string): string {\r\n const slug = deriveProjectSlug(cwd);\r\n return join(homedir(), '.claude', 'projects', slug, 'memory');\r\n}\r\n\r\ninterface MemoryEntry {\r\n readonly memory_id: string;\r\n readonly operator_id: string;\r\n readonly entry_type: 'index' | 'topic';\r\n readonly file_name: string;\r\n readonly content: string;\r\n readonly session_id?: string;\r\n readonly created_at?: string;\r\n readonly updated_at?: string;\r\n}\r\n\r\ninterface GetMemoryResponse {\r\n readonly ok: boolean;\r\n readonly entries: MemoryEntry[];\r\n}\r\n\r\ninterface CreateMemoryBody {\r\n readonly entry_type: 'index' | 'topic';\r\n readonly file_name: string;\r\n readonly content: string;\r\n readonly session_id?: string;\r\n}\r\n\r\ninterface UpdateMemoryBody {\r\n readonly content: string;\r\n readonly session_id?: string;\r\n}\r\n\r\ninterface CreateMemoryResponse {\r\n readonly ok: boolean;\r\n readonly memory_id?: string;\r\n}\r\n\r\ninterface UpdateMemoryResponse {\r\n readonly ok: boolean;\r\n}\r\n\r\n/**\r\n * Minimal fetch-like surface for testability. Production uses `globalThis.fetch`.\r\n */\r\nexport type FetchLike = (\r\n url: string,\r\n init?: {\r\n method?: string;\r\n headers?: Record<string, string>;\r\n body?: string;\r\n },\r\n) => Promise<{\r\n status: number;\r\n text: () => Promise<string>;\r\n}>;\r\n\r\n/**\r\n * Pull cloud memory entries to local files.\r\n */\r\nasync function pullMemory(\r\n controlPlaneUrl: string,\r\n token: string,\r\n memoryDir: string,\r\n sessionId: string,\r\n fetchFn: FetchLike,\r\n): Promise<{ pulled: number; files: string[] }> {\r\n const url = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\r\n const response = await fetchFn(url, {\r\n method: 'GET',\r\n headers: {\r\n authorization: `Bearer ${token}`,\r\n },\r\n });\r\n\r\n if (response.status !== 200) {\r\n const text = await response.text();\r\n throw new Error(`GET /api/v1/agent-config/memory/me returned HTTP ${response.status}: ${text.slice(0, 200)}`);\r\n }\r\n\r\n const data = JSON.parse(await response.text()) as GetMemoryResponse;\r\n if (!data.ok || !Array.isArray(data.entries)) {\r\n throw new Error('GET /api/v1/agent-config/memory/me response missing ok=true or entries array');\r\n }\r\n\r\n mkdirSync(memoryDir, { recursive: true });\r\n const files: string[] = [];\r\n\r\n for (const entry of data.entries) {\r\n const filePath = join(memoryDir, entry.file_name);\r\n writeFileSync(filePath, entry.content, 'utf8');\r\n files.push(entry.file_name);\r\n }\r\n\r\n return { pulled: data.entries.length, files };\r\n}\r\n\r\n/**\r\n * Push local memory files to cloud.\r\n */\r\nasync function pushMemory(\r\n controlPlaneUrl: string,\r\n token: string,\r\n memoryDir: string,\r\n sessionId: string,\r\n fetchFn: FetchLike,\r\n): Promise<{ pushed: number; created: number; updated: number }> {\r\n if (!existsSync(memoryDir)) {\r\n return { pushed: 0, created: 0, updated: 0 };\r\n }\r\n\r\n // Read local files\r\n const localFiles = readdirSync(memoryDir)\r\n .filter((f) => f.endsWith('.md'))\r\n .map((f) => ({\r\n file_name: f,\r\n content: readFileSync(join(memoryDir, f), 'utf8'),\r\n entry_type: (f === 'MEMORY.md' ? 'index' : 'topic') as 'index' | 'topic',\r\n }));\r\n\r\n if (localFiles.length === 0) {\r\n return { pushed: 0, created: 0, updated: 0 };\r\n }\r\n\r\n // GET existing entries to map file_name \u2192 memory_id\r\n const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\r\n const getResponse = await fetchFn(getUrl, {\r\n method: 'GET',\r\n headers: {\r\n authorization: `Bearer ${token}`,\r\n },\r\n });\r\n\r\n const existingMap = new Map<string, string>();\r\n if (getResponse.status === 200) {\r\n const getData = JSON.parse(await getResponse.text()) as GetMemoryResponse;\r\n if (getData.ok && Array.isArray(getData.entries)) {\r\n for (const entry of getData.entries) {\r\n existingMap.set(entry.file_name, entry.memory_id);\r\n }\r\n }\r\n }\r\n\r\n let created = 0;\r\n let updated = 0;\r\n\r\n for (const localFile of localFiles) {\r\n const memoryId = existingMap.get(localFile.file_name);\r\n\r\n if (memoryId) {\r\n // UPDATE\r\n const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${memoryId}`;\r\n const updateBody: UpdateMemoryBody = {\r\n content: localFile.content,\r\n session_id: sessionId,\r\n };\r\n const updateResponse = await fetchFn(updateUrl, {\r\n method: 'PUT',\r\n headers: {\r\n authorization: `Bearer ${token}`,\r\n 'content-type': 'application/json',\r\n },\r\n body: JSON.stringify(updateBody),\r\n });\r\n\r\n if (updateResponse.status !== 200) {\r\n const text = await updateResponse.text();\r\n throw new Error(\r\n `PUT /api/v1/agent-config/memory/${memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`,\r\n );\r\n }\r\n\r\n const updateData = JSON.parse(await updateResponse.text()) as UpdateMemoryResponse;\r\n if (!updateData.ok) {\r\n throw new Error(`PUT /api/v1/agent-config/memory/${memoryId} returned ok=false`);\r\n }\r\n updated++;\r\n } else {\r\n // CREATE\r\n const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\r\n const createBody: CreateMemoryBody = {\r\n entry_type: localFile.entry_type,\r\n file_name: localFile.file_name,\r\n content: localFile.content,\r\n session_id: sessionId,\r\n };\r\n const createResponse = await fetchFn(createUrl, {\r\n method: 'POST',\r\n headers: {\r\n authorization: `Bearer ${token}`,\r\n 'content-type': 'application/json',\r\n },\r\n body: JSON.stringify(createBody),\r\n });\r\n\r\n if (createResponse.status !== 200 && createResponse.status !== 201) {\r\n const text = await createResponse.text();\r\n throw new Error(\r\n `POST /api/v1/agent-config/memory/me returned HTTP ${createResponse.status}: ${text.slice(0, 200)}`,\r\n );\r\n }\r\n\r\n const createData = JSON.parse(await createResponse.text()) as CreateMemoryResponse;\r\n if (!createData.ok) {\r\n throw new Error('POST /api/v1/agent-config/memory/me returned ok=false');\r\n }\r\n created++;\r\n }\r\n }\r\n\r\n return { pushed: localFiles.length, created, updated };\r\n}\r\n\r\nexport async function handleSyncConfig(\r\n deps: ToolDeps,\r\n rawInput: unknown,\r\n _signal?: AbortSignal,\r\n fetchFn: FetchLike = globalThis.fetch as unknown as FetchLike,\r\n): Promise<ReturnType<typeof jsonContent>> {\r\n if (!isToolInput(rawInput)) {\r\n throw invalidParams(\r\n TOOL_NAME,\r\n 'invalid input. Required: { action: \"pull\" | \"push\" }. Optional: { cwd: \"<path>\" }.',\r\n );\r\n }\r\n\r\n // Get control-plane URL from env\r\n const controlPlaneUrl = process.env['VO_CONTROL_PLANE_URL'];\r\n if (!controlPlaneUrl) {\r\n return jsonContent({\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n payload: {\r\n synced: false,\r\n reason: 'VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled',\r\n },\r\n });\r\n }\r\n\r\n // Get auth token via the existing auth-token-source infrastructure\r\n // This handles vo_credential, refresh tokens, etc.\r\n const { createAuthTokenSourceFromEnv } = await import('../../cloud/auth-token-source.js');\r\n const { readStoredCredential } = await import('../../cloud/credential-store.js');\r\n const tokenSource = createAuthTokenSourceFromEnv(process.env, fetchFn, () => readStoredCredential(process.env));\r\n\r\n if (!tokenSource) {\r\n return jsonContent({\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n payload: {\r\n synced: false,\r\n reason: 'No auth configured. Run `vo-mcp login` to authenticate as an operator.',\r\n },\r\n });\r\n }\r\n\r\n const token = await tokenSource.getToken();\r\n if (!token) {\r\n return jsonContent({\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n payload: {\r\n synced: false,\r\n reason: 'Failed to obtain auth token. Run `vo-mcp login` to re-authenticate.',\r\n },\r\n });\r\n }\r\n\r\n const cwd = rawInput.cwd?.trim() || process.cwd();\r\n const memoryDir = getMemoryDir(cwd);\r\n\r\n try {\r\n if (rawInput.action === 'pull') {\r\n const result = await pullMemory(\r\n controlPlaneUrl.replace(/\\/+$/, ''),\r\n token,\r\n memoryDir,\r\n deps.sessionId,\r\n fetchFn,\r\n );\r\n return jsonContent({\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n payload: {\r\n synced: true,\r\n action: 'pull',\r\n pulled: result.pulled,\r\n files: result.files,\r\n memory_dir: memoryDir,\r\n },\r\n });\r\n } else {\r\n const result = await pushMemory(\r\n controlPlaneUrl.replace(/\\/+$/, ''),\r\n token,\r\n memoryDir,\r\n deps.sessionId,\r\n fetchFn,\r\n );\r\n return jsonContent({\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n payload: {\r\n synced: true,\r\n action: 'push',\r\n pushed: result.pushed,\r\n created: result.created,\r\n updated: result.updated,\r\n memory_dir: memoryDir,\r\n },\r\n });\r\n }\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n return jsonContent({\r\n tool: TOOL_NAME,\r\n schema_version: 1,\r\n payload: {\r\n synced: false,\r\n reason: `Sync failed: ${message}`,\r\n },\r\n });\r\n }\r\n}\r\n", "/**\r\n * Content-hash cache (V1 launch gate #8).\r\n *\r\n * Backend: Node's built-in `node:sqlite` (sync, file-backed, Node 22+).\r\n * Keyed by `sha256(canonicalize(tool_input))`. Cache value =\r\n * JSON-serialized full tool response envelope.\r\n *\r\n * Design notes:\r\n * - Sync API on purpose. MCP tool handlers are async at the protocol\r\n * layer but cache hits should not pay an event-loop hop.\r\n * - `node:sqlite` is \"experimental\" in Node 22 \u2014 but it's the shipped\r\n * surface; switching to a native bindings library can be done by\r\n * swapping THIS file alone. The exported `ContentHashCache` interface\r\n * is the stable boundary.\r\n * - TTL is optional and stored alongside the value. If `expires_at`\r\n * is in the past, `get` returns null and deletes the row.\r\n * - Tests use in-memory DBs (`:memory:` path).\r\n */\r\nimport { createHash } from 'node:crypto';\r\nimport { chmodSync, mkdirSync } from 'node:fs';\r\nimport { dirname } from 'node:path';\r\nimport { DatabaseSync } from 'node:sqlite';\r\nimport { canonicalize, type CanonicalizeOptions } from './canonicalize.js';\r\n\r\nexport interface CacheEntry<T> {\r\n readonly key: string;\r\n readonly value: T;\r\n readonly cachedAt: number;\r\n}\r\n\r\nexport interface ContentHashCache {\r\n /** Compute the stable hash key for a tool input. */\r\n keyFor(toolName: string, input: unknown, opts?: CanonicalizeOptions): string;\r\n /** Read a cache entry. Returns null on miss or expired row. */\r\n get<T>(key: string): CacheEntry<T> | null;\r\n /** Write a cache entry. `ttlMs` optional \u2014 omit for no expiration. */\r\n set<T>(key: string, value: T, ttlMs?: number): void;\r\n /**\r\n * Delete a single cache entry by key. Returns true if a row was removed,\r\n * false if the key was already absent. Added PR-Q 2026-05-25 to close a\r\n * documented gap that previously forced the engine's CacheBackend\r\n * adapter (`engine-client.ts::createCacheBackendAdapter`) to ship as a\r\n * silent no-op.\r\n */\r\n delete(key: string): boolean;\r\n /**\r\n * Remove all cache entries (full purge). Returns the number of rows\r\n * removed. Added PR-Q 2026-05-25 alongside `delete()`. Useful for\r\n * operators who want to flush after a `vo_arch_defaults` corpus edit\r\n * without re-namespacing or `rm`ing the .db file.\r\n */\r\n clear(): number;\r\n /** For tests / diagnostics. */\r\n size(): number;\r\n /** Close the underlying handle. Tests must call this to release locks. */\r\n close(): void;\r\n}\r\n\r\nexport interface SqliteCacheOptions {\r\n /** Absolute path to the .db file, or ':memory:' for in-memory testing. */\r\n readonly dbPath: string;\r\n /**\r\n * Versioning namespace mixed into every cache key (2026-05-24 audit\r\n * BLOCKER #3 fix). When the underlying engine, KB corpus, or vo-mcp\r\n * package itself ships a new version, the namespace string changes\r\n * and previously-cached envelopes no longer hit \u2014 preventing stale\r\n * verdicts from old code being returned by new logic.\r\n *\r\n * Empty string (or omitted) preserves the pre-2026-05-24 keying\r\n * behavior \u2014 same input \u2192 same key regardless of version. Tests use\r\n * the empty default for stable cache-key fixtures.\r\n *\r\n * Recommended format (see cli.ts for the production wiring):\r\n * `vo_mcp@${voMcpVersion}|engine@${engineVersion ?? 'none'}|corpus@${corpusHash ?? 'none'}`\r\n *\r\n * Trade-off: on the first run after a version bump, the on-disk\r\n * cache file is fully invalidated (all keys miss). That's the desired\r\n * behavior \u2014 the upgrade IS the moment we want fresh results.\r\n */\r\n readonly cacheVersionNamespace?: string;\r\n}\r\n\r\ninterface CacheRow {\r\n readonly key: string;\r\n readonly value: string;\r\n readonly cached_at: number;\r\n readonly expires_at: number | null;\r\n}\r\n\r\nexport function createSqliteCache(options: SqliteCacheOptions): ContentHashCache {\r\n const fileBacked = options.dbPath !== ':memory:';\r\n if (fileBacked) {\r\n // 0o700: only the operator account can list / open the directory.\r\n // Best-effort on Windows (POSIX bits no-op there; rely on parent ACL).\r\n mkdirSync(dirname(options.dbPath), { recursive: true, mode: 0o700 });\r\n }\r\n // Capture the namespace once at construction. Default '' preserves the\r\n // pre-2026-05-24 key shape so tests with no namespace get the same keys.\r\n const versionNamespace = options.cacheVersionNamespace ?? '';\r\n const db = new DatabaseSync(options.dbPath);\r\n // WAL: lets multiple MCP-server processes (Claude Code + Cursor + Claude\r\n // Desktop sharing ~/.claude/vo-mcp-cache.db) read concurrently with a\r\n // single writer. busy_timeout: absorbs short write contention without\r\n // SQLITE_BUSY-throwing into tool handlers. PRAGMA NORMAL: durability vs\r\n // performance trade-off acceptable for a cache (worst case = lose a\r\n // freshly-written entry on power loss, recomputable from the source).\r\n // :memory: dbs don't need WAL but the PRAGMA is harmless.\r\n db.exec('PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000; PRAGMA synchronous=NORMAL;');\r\n db.exec(`\r\n CREATE TABLE IF NOT EXISTS cache (\r\n key TEXT PRIMARY KEY,\r\n value TEXT NOT NULL,\r\n cached_at INTEGER NOT NULL,\r\n expires_at INTEGER\r\n );\r\n `);\r\n if (fileBacked) {\r\n // 0o600 \u2014 readable+writable only by the operator account. The cache may\r\n // hold canonicalized tool inputs (code snippets, prompts) that, while\r\n // sanitized at logging time, are NOT scrubbed in the cached value.\r\n // Best-effort: chmod fails harmlessly on Windows.\r\n try {\r\n chmodSync(options.dbPath, 0o600);\r\n } catch {\r\n /* ignore \u2014 Windows / unsupported FS */\r\n }\r\n }\r\n\r\n const insertStmt = db.prepare(\r\n 'INSERT OR REPLACE INTO cache (key, value, cached_at, expires_at) VALUES (?, ?, ?, ?)',\r\n );\r\n const selectStmt = db.prepare(\r\n 'SELECT key, value, cached_at, expires_at FROM cache WHERE key = ?',\r\n );\r\n const deleteStmt = db.prepare('DELETE FROM cache WHERE key = ?');\r\n const clearAllStmt = db.prepare('DELETE FROM cache');\r\n const countStmt = db.prepare('SELECT COUNT(*) AS c FROM cache');\r\n\r\n return {\r\n keyFor(toolName: string, input: unknown, opts?: CanonicalizeOptions): string {\r\n const canonical = canonicalize(input, opts);\r\n const hash = createHash('sha256');\r\n // Version namespace is prefixed when non-empty. Empty default = the\r\n // pre-2026-05-24 key shape (back-compat). Using `|` as a separator\r\n // because canonical strings start with `{`/`\"`/`[` \u2014 `|` cannot\r\n // appear in canonicalized output ambiguously.\r\n if (versionNamespace.length > 0) {\r\n hash.update(versionNamespace);\r\n hash.update('|');\r\n }\r\n hash.update(toolName);\r\n hash.update(' ');\r\n hash.update(canonical);\r\n return hash.digest('hex');\r\n },\r\n\r\n get<T>(key: string): CacheEntry<T> | null {\r\n const row = selectStmt.get(key) as CacheRow | undefined;\r\n if (!row) return null;\r\n if (row.expires_at !== null && row.expires_at < Date.now()) {\r\n deleteStmt.run(key);\r\n return null;\r\n }\r\n return {\r\n key: row.key,\r\n value: JSON.parse(row.value) as T,\r\n cachedAt: row.cached_at,\r\n };\r\n },\r\n\r\n set<T>(key: string, value: T, ttlMs?: number): void {\r\n const now = Date.now();\r\n const expiresAt = typeof ttlMs === 'number' ? now + ttlMs : null;\r\n insertStmt.run(key, JSON.stringify(value), now, expiresAt);\r\n },\r\n\r\n delete(key: string): boolean {\r\n // node:sqlite `RunResult.changes` is a bigint in some Node 22 builds and\r\n // a number in others; coerce defensively before comparing.\r\n const result = deleteStmt.run(key);\r\n return Number(result.changes) > 0;\r\n },\r\n\r\n clear(): number {\r\n const result = clearAllStmt.run();\r\n return Number(result.changes);\r\n },\r\n\r\n size(): number {\r\n const r = countStmt.get() as { c: number } | undefined;\r\n return r?.c ?? 0;\r\n },\r\n\r\n close(): void {\r\n db.close();\r\n },\r\n };\r\n}\r\n", "/**\r\n * Canonicalize a tool input value into a stable string for sha256 hashing.\r\n *\r\n * Per handoff \u00A77:\r\n * - object keys sorted alphabetically\r\n * - whitespace trimmed from strings (interior whitespace preserved; leading/trailing stripped)\r\n * - line endings normalized to LF\r\n * - volatile fields (timestamps, session IDs) excluded\r\n *\r\n * `excludeKeys` lists keys to drop at ANY depth. Callers pass the set\r\n * relevant to their tool. We avoid hardcoding a global blacklist because\r\n * what's \"volatile\" depends on the tool.\r\n */\r\n\r\nconst DEFAULT_VOLATILE_KEYS = new Set<string>([\r\n 'ts',\r\n 'timestamp',\r\n 'session_id',\r\n 'sessionId',\r\n 'request_id',\r\n 'requestId',\r\n 'nonce',\r\n]);\r\n\r\nexport interface CanonicalizeOptions {\r\n /** Additional keys to exclude beyond the defaults. */\r\n readonly excludeKeys?: ReadonlySet<string>;\r\n}\r\n\r\nexport function canonicalize(value: unknown, options: CanonicalizeOptions = {}): string {\r\n const excluded = new Set<string>([\r\n ...DEFAULT_VOLATILE_KEYS,\r\n ...(options.excludeKeys ?? new Set<string>()),\r\n ]);\r\n const normalized = normalize(value, excluded);\r\n return JSON.stringify(normalized);\r\n}\r\n\r\nfunction normalize(value: unknown, excluded: ReadonlySet<string>): unknown {\r\n if (value === null || value === undefined) return null;\r\n if (typeof value === 'string') return normalizeString(value);\r\n if (typeof value === 'number' || typeof value === 'boolean') return value;\r\n if (Array.isArray(value)) return value.map((item) => normalize(item, excluded));\r\n if (typeof value === 'object') {\r\n const out: Record<string, unknown> = {};\r\n const obj = value as Record<string, unknown>;\r\n const keys = Object.keys(obj)\r\n .filter((k) => !excluded.has(k))\r\n .sort();\r\n for (const key of keys) {\r\n out[key] = normalize(obj[key], excluded);\r\n }\r\n return out;\r\n }\r\n // Functions, symbols, bigints \u2014 not part of our schema; coerce to string for stability.\r\n return String(value);\r\n}\r\n\r\nfunction normalizeString(s: string): string {\r\n // Normalize line endings (CRLF / CR -> LF), then trim leading/trailing\r\n // whitespace. Interior whitespace and indentation preserved \u2014 they're\r\n // semantically meaningful (e.g. test bodies).\r\n return s.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n').trim();\r\n}\r\n", "/**\r\n * Stub ratchet client \u2014 Phase 1 scaffold.\r\n *\r\n * Implements `RatchetClient` with a tiny rule set good enough to demonstrate\r\n * the open shape end-to-end. Detects three common \"hollow assertion\"\r\n * shapes that any honest test reviewer would flag:\r\n *\r\n * - `expect(...).toBeDefined()`\r\n * - `expect(...).toBeTruthy()` / `toBeFalsy()` against a literal\r\n * - `expect(true).toBe(true)` style tautologies\r\n *\r\n * It also rewards presence of equality / property assertions\r\n * (`toBe(`, `toEqual(`, `toMatchObject(`, `toContain(`).\r\n *\r\n * Phase 2 swaps this whole module for `import { ... } from '@algosuite/ratchets-generic'`.\r\n * The thresholds below are placeholders, not the real moat content\r\n * (EXTRACTION_AUDIT.md documents the deferral).\r\n */\r\nimport type {\r\n AssertionStrengthRequest,\r\n AssertionStrengthResult,\r\n RatchetClient,\r\n} from './client.js';\r\nimport type { AssertionFinding } from '../types.js';\r\n\r\nconst HOLLOW_PATTERNS: ReadonlyArray<{\r\n readonly regex: RegExp;\r\n readonly code: string;\r\n readonly severity: AssertionFinding['severity'];\r\n readonly message: string;\r\n}> = [\r\n {\r\n regex: /expect\\([^)]*\\)\\.toBeDefined\\(\\)/g,\r\n code: 'hollow.toBeDefined',\r\n severity: 'warn',\r\n message: 'toBeDefined() only proves the call returned something; assert a value.',\r\n },\r\n {\r\n regex: /expect\\((true|false|1|0|'[^']*'|\"[^\"]*\")\\)\\.toBe\\(\\1\\)/g,\r\n code: 'hollow.tautology',\r\n severity: 'error',\r\n message: 'Tautological assertion: literal compared to itself.',\r\n },\r\n {\r\n regex: /expect\\([^)]*\\)\\.toBeTruthy\\(\\)|expect\\([^)]*\\)\\.toBeFalsy\\(\\)/g,\r\n code: 'hollow.toBeTruthy',\r\n severity: 'warn',\r\n message: 'toBeTruthy/toBeFalsy hide what value you expected; prefer toBe(...) / toEqual(...).',\r\n },\r\n];\r\n\r\nconst STRONG_PATTERNS: readonly RegExp[] = [\r\n /\\.toBe\\(/g,\r\n /\\.toEqual\\(/g,\r\n /\\.toMatchObject\\(/g,\r\n /\\.toContain\\(/g,\r\n /\\.toHaveBeenCalledWith\\(/g,\r\n /\\.toStrictEqual\\(/g,\r\n];\r\n\r\n/**\r\n * Toy heuristic scoring \u2014 DO NOT treat these numbers as the real ratchet.\r\n * Open-shell deliberately: real thresholds live in the closed library.\r\n */\r\nconst MAX_SCORE = 100;\r\nconst WEAK_FLOOR = 30;\r\nconst HOLLOW_PENALTY = 25;\r\nconst HOLLOW_TAUTOLOGY_PENALTY = 50;\r\nconst STRONG_REWARD_PER = 8;\r\n\r\nexport function createStubRatchetClient(): RatchetClient {\r\n return {\r\n async checkAssertionStrength(\r\n req: AssertionStrengthRequest,\r\n ): Promise<AssertionStrengthResult> {\r\n const findings: AssertionFinding[] = [];\r\n let score = 50; // neutral start\r\n let hollowHits = 0;\r\n let tautologyHits = 0;\r\n\r\n for (const pat of HOLLOW_PATTERNS) {\r\n // Reset lastIndex in case the regex is reused with /g.\r\n pat.regex.lastIndex = 0;\r\n let m: RegExpExecArray | null;\r\n while ((m = pat.regex.exec(req.source)) !== null) {\r\n findings.push({\r\n line_excerpt: clip(m[0], 80),\r\n severity: pat.severity,\r\n code: pat.code,\r\n message: pat.message,\r\n });\r\n if (pat.code === 'hollow.tautology') tautologyHits += 1;\r\n else hollowHits += 1;\r\n }\r\n }\r\n\r\n let strongHits = 0;\r\n for (const pat of STRONG_PATTERNS) {\r\n pat.lastIndex = 0;\r\n const matches = req.source.match(pat);\r\n if (matches) strongHits += matches.length;\r\n }\r\n\r\n score -= hollowHits * HOLLOW_PENALTY;\r\n score -= tautologyHits * HOLLOW_TAUTOLOGY_PENALTY;\r\n score += strongHits * STRONG_REWARD_PER;\r\n score = Math.max(0, Math.min(MAX_SCORE, score));\r\n\r\n const verdict: AssertionStrengthResult['verdict'] =\r\n tautologyHits > 0 || (hollowHits > 0 && strongHits === 0)\r\n ? 'hollow'\r\n : score < WEAK_FLOOR\r\n ? 'weak'\r\n : 'strong';\r\n\r\n const summary = buildSummary({\r\n verdict,\r\n score,\r\n strongHits,\r\n hollowHits,\r\n tautologyHits,\r\n filePath: req.filePath,\r\n });\r\n\r\n return { score, maxScore: MAX_SCORE, verdict, findings, summary };\r\n },\r\n };\r\n}\r\n\r\nfunction clip(s: string, n: number): string {\r\n return s.length <= n ? s : s.slice(0, n) + '\u2026';\r\n}\r\n\r\nfunction buildSummary(args: {\r\n verdict: AssertionStrengthResult['verdict'];\r\n score: number;\r\n strongHits: number;\r\n hollowHits: number;\r\n tautologyHits: number;\r\n filePath: string | undefined;\r\n}): string {\r\n const fileTag = args.filePath ? `${args.filePath}: ` : '';\r\n return (\r\n `${fileTag}verdict=${args.verdict} score=${args.score}/${MAX_SCORE} ` +\r\n `(strong=${args.strongHits} hollow=${args.hollowHits} tautology=${args.tautologyHits}) ` +\r\n `[stub-ratchet \u2014 real thresholds load from @algosuite/ratchets-generic in Phase 2]`\r\n );\r\n}\r\n", "/**\r\n * Null consensus engine client \u2014 returns `ok: false` with a stable reason.\r\n *\r\n * Used when the closed `@algosuite/consensus-engine` package is not\r\n * wired in (Phase 1 default; the engine is loaded only in environments\r\n * that have real model credentials configured), AND as a diagnostic\r\n * stub for the more-specific reasons surfaced by `engine-client.ts`\r\n * (since 2026-05-24 audit MEDIUM \"engine_unavailable reason-string\r\n * differentiation\" \u2014 the previous single `'consensus-engine-package-\r\n * pending'` couldn't tell an operator which of \"no API keys\",\r\n * \"@algosuite/consensus-engine not installed\", \"functions-shared not\r\n * resolvable\", or \"all panel adapters failed to construct\" was the\r\n * actual cause).\r\n *\r\n * The `vo_consensus_judgment` tool inspects the result and falls back to\r\n * the existing 'unimplemented' envelope when the engine is unavailable,\r\n * so cross-vendor MCP clients never see a missing-tool error \u2014 they get\r\n * `{ verdict: 'unimplemented', reason: '<whichever-specific-reason>' }`\r\n * which is operator-actionable.\r\n */\r\nimport type { ConsensusEngineClient, ConsensusResult } from './client.js';\r\n\r\n/**\r\n * Default reason. Kept stable since 2026-05-21 for back-compat with\r\n * existing event-log pins and downstream metric dashboards that group\r\n * on this string. New diagnostic call sites pass their own reason.\r\n */\r\nexport const NULL_CLIENT_DEFAULT_REASON = 'consensus-engine-package-pending';\r\n\r\n/**\r\n * Specific reasons emitted by `tryCreateEngineConsensusClientFromEnv*`\r\n * when it can identify exactly why the engine isn't wired. Exported so\r\n * tests and downstream observability code can pin them.\r\n */\r\nexport const ENGINE_UNAVAILABLE_REASONS = {\r\n /** Fewer than 2 provider API keys present in env \u2014 can't form a panel. */\r\n CREDENTIALS_MISSING: 'consensus-credentials-missing',\r\n /** `@algosuite/consensus-engine` module not resolvable / installed. */\r\n ENGINE_PACKAGE_NOT_INSTALLED: 'consensus-engine-package-not-installed',\r\n /** `functions-shared` module not resolvable in the host's node_modules. */\r\n FUNCTIONS_SHARED_NOT_INSTALLED: 'consensus-functions-shared-not-installed',\r\n /** Engine + functions-shared loaded but adapter construction failed for all providers. */\r\n PANEL_CONSTRUCTION_FAILED: 'consensus-panel-construction-failed',\r\n} as const;\r\n\r\nexport type EngineUnavailableReason =\r\n (typeof ENGINE_UNAVAILABLE_REASONS)[keyof typeof ENGINE_UNAVAILABLE_REASONS]\r\n | typeof NULL_CLIENT_DEFAULT_REASON;\r\n\r\nexport function createNullConsensusEngineClient(\r\n reason: string = NULL_CLIENT_DEFAULT_REASON,\r\n): ConsensusEngineClient {\r\n return {\r\n async run(): Promise<ConsensusResult> {\r\n return { ok: false, reason };\r\n },\r\n };\r\n}\r\n", "/**\r\n * Engine-backed consensus client.\r\n *\r\n * Bridges the open-shell `ConsensusEngineClient` interface to the closed\r\n * `@algosuite/consensus-engine` package via dynamic import. Phase 1 keeps\r\n * the open shell free of a build-time dependency on the closed package\r\n * (mirrors the `ratchets-generic` boundary pattern in `src/ratchets/`).\r\n *\r\n * Two factory variants:\r\n * - `createEngineConsensusClient({ panel })` \u2014 caller supplies the\r\n * pre-built model adapters. This is the production path: the cli\r\n * constructs Anthropic + Google adapters from env credentials and\r\n * hands them to this factory.\r\n * - `tryCreateEngineConsensusClientFromEnv()` \u2014 convenience wrapper.\r\n * Phase 1 returns `null` (no credential resolution wired). Phase 2\r\n * wires real Anthropic + Google SDK adapter construction from\r\n * `ANTHROPIC_API_KEY` + `GEMINI_API_KEY` env vars.\r\n *\r\n * On any engine error the client returns `{ ok: false, reason }` \u2014\r\n * never throws \u2014 so the `vo_consensus_judgment` tool can fall back to\r\n * the stable 'unimplemented' envelope.\r\n */\r\nimport { randomUUID } from 'node:crypto';\r\nimport type {\r\n ConsensusEngineClient,\r\n ConsensusRequest,\r\n ConsensusResult,\r\n ConsensusVerdict,\r\n} from './client.js';\r\nimport type { ContentHashCache } from '../cache/sqlite-cache.js';\r\nimport type { EventsWriter } from '../logging/events-writer.js';\r\nimport type { SessionContext, ConsensusCallEvent } from '../types.js';\r\nimport { sanitizeExcerpt } from '../logging/events-writer.js';\r\nimport { VO_MCP_VERSION, sha256Hex } from '../tools/common.js';\r\nimport {\r\n createNullConsensusEngineClient,\r\n ENGINE_UNAVAILABLE_REASONS,\r\n} from './null-client.js';\r\nimport {\r\n resolveAgreementGate,\r\n buildSourceGroundedFields,\r\n mapFanOutDiagnostics,\r\n mapCitationGrade,\r\n} from './engine-options.js';\r\nimport { createClaimClassifier } from './claim-classifier.js';\r\n\r\n// 2026-06-01 boot-hang fix: consensus-engine + functions-shared are loaded\r\n// via LAZY dynamic import inside loadFactoryAndCallers (NOT static top-level\r\n// imports). functions-shared transitively pulls firebase-admin + the three\r\n// provider SDKs; importing it at module-load added ~27s to vo-mcp startup,\r\n// blowing past the MCP stdio init timeout so the server never connected and\r\n// ZERO tools registered. The packages remain workspace:* deps \u2014 only the\r\n// import TIMING moved back to on-first-use, restoring the O(ms) boot the\r\n// factory docstrings below already promise. See loadFactoryAndCallers.\r\n\r\n/**\r\n * Loosely-typed adapter shape that mirrors the closed engine's\r\n * `ModelAdapter` interface. Declared structurally here so the open shell\r\n * doesn't import any closed types at build time. The closed package's\r\n * own types are the source of truth \u2014 this is the minimum surface the\r\n * shell needs to forward.\r\n */\r\nexport interface ConsensusModelAdapter {\r\n readonly model: string;\r\n readonly provider: string;\r\n call(args: {\r\n readonly system_prompt: string;\r\n readonly user_prompt: string;\r\n readonly abort_signal?: AbortSignal;\r\n }): Promise<{\r\n readonly model: string;\r\n readonly provider: string;\r\n readonly verdict: ConsensusVerdict | 'error';\r\n readonly confidence: number;\r\n readonly reasoning_excerpt: string;\r\n readonly raw_response_excerpt: string;\r\n readonly duration_ms: number;\r\n readonly error?: { readonly category: string; readonly message: string };\r\n }>;\r\n}\r\n\r\nexport interface EngineClientOptions {\r\n readonly panel: readonly ConsensusModelAdapter[];\r\n readonly per_model_timeout_ms?: number;\r\n /** For testability \u2014 caller may inject the loaded engine module. */\r\n readonly engineModule?: EngineModuleShape;\r\n /**\r\n * BEHAVIOUR-CHANGING opt-in (engine Feature 1 agreement-gate). When set to\r\n * `true` the engine fans out a cheap probe subset first and skips the rest of\r\n * the panel when the probe agrees strongly (an early-exit cost saving). When\r\n * `undefined` (the default) the `VO_CONSENSUS_AGREEMENT_GATE` env var decides;\r\n * when neither is truthy the whole panel is always called \u2014 UNCHANGED gate\r\n * behaviour. See engine-options.resolveAgreementGate.\r\n */\r\n readonly agreement_gate_enabled?: boolean;\r\n /** Override env lookups for the agreement-gate flag \u2014 primarily for tests. */\r\n readonly env?: Readonly<Record<string, string | undefined>>;\r\n}\r\n\r\n/**\r\n * Structural mirror of the engine's `agreement_gate` option (EngineOptions).\r\n * Re-declared here so the open shell carries no closed import.\r\n */\r\ninterface EngineAgreementGateShape {\r\n readonly enabled: boolean;\r\n readonly probe_size?: number;\r\n readonly modal_agreement_threshold?: number;\r\n readonly min_confidence?: number;\r\n}\r\n\r\n/** Engine response shape \u2014 shared by `runConsensus` and `runSourceGroundedConsensus`. */\r\ninterface EngineConsensusResponseShape {\r\n synthesized_verdict: {\r\n verdict: ConsensusVerdict;\r\n confidence: number;\r\n reasoning_excerpt: string;\r\n synthesizer: string;\r\n dissent_summary: string | null;\r\n /** Engine Feature 2 (calibrated-confidence) \u2014 additive; attached by the engine on every runConsensus. */\r\n calibrated_confidence?: number;\r\n confidence_badge?: 'high' | 'medium' | 'low';\r\n };\r\n per_model_verdicts: ReadonlyArray<{\r\n model: string;\r\n provider: string;\r\n verdict: ConsensusVerdict | 'error';\r\n confidence: number;\r\n reasoning_excerpt: string;\r\n raw_response_excerpt: string;\r\n duration_ms: number;\r\n error?: { category: string; message: string };\r\n }>;\r\n degraded: boolean;\r\n duration_ms: number;\r\n engine_version: string;\r\n /** Phase 2 Lane D-1 \u2014 optional; set by `human-tiebreak` synthesizer. */\r\n escalation_required?: boolean;\r\n escalation_reason?: string;\r\n /** Engine Feature 1 (agreement-gate) \u2014 present only when the gate ran this call. */\r\n fan_out_diagnostics?: {\r\n models_called: number;\r\n panel_size: number;\r\n early_exit: boolean;\r\n refused: number;\r\n };\r\n}\r\n\r\ninterface EngineModuleShape {\r\n runConsensus(\r\n request: { gate_type: string; prompt: string; system_prompt?: string },\r\n options: {\r\n panel: readonly ConsensusModelAdapter[];\r\n per_model_timeout_ms?: number;\r\n /** Engine Feature 1 (agreement-gate) opt-in. Absent \u21D2 whole panel always called. */\r\n agreement_gate?: EngineAgreementGateShape;\r\n },\r\n ): Promise<{\r\n ok: boolean;\r\n response?: EngineConsensusResponseShape;\r\n error?: { kind: string; reason: string };\r\n }>;\r\n /**\r\n * Engine source-grounded path (Tier-4). OPTIONAL on the module shape so an\r\n * engine build without it (or an injected fake that omits it) degrades\r\n * gracefully. When present AND the request carries `source_urls`, the\r\n * engine-client routes to this instead of `runConsensus`, activating the\r\n * retrieval-confidence + citation-grader features.\r\n */\r\n runSourceGroundedConsensus?(\r\n request: { gate_type: string; prompt: string; system_prompt?: string },\r\n options: {\r\n panel: readonly ConsensusModelAdapter[];\r\n per_model_timeout_ms?: number;\r\n agreement_gate?: EngineAgreementGateShape;\r\n source_urls: readonly string[];\r\n allow_url?: (url: string) => boolean;\r\n citation_grader?: {\r\n enabled: boolean;\r\n classifier: { classify(args: { claim: string; sources: string }): Promise<{ label: string; confidence: number }> };\r\n escalate_below?: number;\r\n };\r\n },\r\n ): Promise<\r\n | {\r\n ok: true;\r\n engine: { ok: true; response: EngineConsensusResponseShape };\r\n cite_warning: boolean;\r\n partial_sources: boolean;\r\n citation_grade?: {\r\n ok: boolean;\r\n unsupported_count: number;\r\n uncertain_count: number;\r\n graded_claims: ReadonlyArray<{\r\n claim: string;\r\n label: 'supported' | 'unsupported' | 'uncertain';\r\n confidence: number;\r\n cited_ids: readonly number[];\r\n }>;\r\n };\r\n escalation_required?: boolean;\r\n escalation_reason?: string;\r\n low_confidence_sources?: readonly number[];\r\n }\r\n | { ok: false; reason: string }\r\n >;\r\n}\r\n\r\n/**\r\n * A never-resolving promise that REJECTS with the `__VO_MCP_CANCELLED__`\r\n * sentinel the moment the abort signal fires (or immediately if already\r\n * aborted). Used to `Promise.race` the engine call so a cancellation returns\r\n * promptly even if the engine ignores the signal. Shared by the plain and\r\n * source-grounded engine call paths.\r\n */\r\nfunction raceAbort(signal: AbortSignal): Promise<never> {\r\n return new Promise<never>((_, reject) => {\r\n const onAbort = (): void => {\r\n reject(new Error('__VO_MCP_CANCELLED__'));\r\n };\r\n if (signal.aborted) onAbort();\r\n else signal.addEventListener('abort', onAbort, { once: true });\r\n });\r\n}\r\n\r\nasync function loadEngineModule(): Promise<EngineModuleShape | null> {\r\n try {\r\n // Dynamic import keeps `@algosuite/consensus-engine` out of the\r\n // vo-mcp build graph. If the closed package isn't installed in the\r\n // host's node_modules, the import throws \u2014 we treat that as\r\n // unavailable and the null-client fallback kicks in.\r\n //\r\n // The string is split so a static analyzer doesn't try to resolve\r\n // it at build time inside the open package.\r\n const moduleName = ['@algosuite', 'consensus-engine'].join('/');\r\n const mod = (await import(moduleName)) as unknown;\r\n if (\r\n mod !== null &&\r\n typeof mod === 'object' &&\r\n 'runConsensus' in mod &&\r\n typeof (mod as { runConsensus: unknown }).runConsensus === 'function'\r\n ) {\r\n return mod as EngineModuleShape;\r\n }\r\n return null;\r\n } catch {\r\n return null;\r\n }\r\n}\r\n\r\nexport function createEngineConsensusClient(options: EngineClientOptions): ConsensusEngineClient {\r\n const cachedEngine: EngineModuleShape | undefined = options.engineModule;\r\n let loaded: Promise<EngineModuleShape | null> | null = null;\r\n\r\n async function getEngine(): Promise<EngineModuleShape | null> {\r\n if (cachedEngine !== undefined) return cachedEngine;\r\n if (loaded === null) loaded = loadEngineModule();\r\n return loaded;\r\n }\r\n\r\n return {\r\n async run(request: ConsensusRequest): Promise<ConsensusResult> {\r\n // Cancellation fast-path. If the signal already fired (operator\r\n // cancelled before we even started), don't bother loading the\r\n // engine or building the panel \u2014 return immediately. Added\r\n // 2026-05-25 (audit HIGH \u2014 MCP `notifications/cancelled`).\r\n const signal = request.signal;\r\n if (signal?.aborted) {\r\n return { ok: false, reason: 'cancelled' };\r\n }\r\n const engine = await getEngine();\r\n if (engine === null) {\r\n return { ok: false, reason: 'consensus-engine-package-pending' };\r\n }\r\n if (options.panel.length < 2) {\r\n return { ok: false, reason: 'panel-too-small' };\r\n }\r\n try {\r\n const engineRequest: { gate_type: string; prompt: string; system_prompt?: string } = {\r\n gate_type: request.gate_type,\r\n prompt: request.prompt,\r\n ...(request.system_prompt !== undefined ? { system_prompt: request.system_prompt } : {}),\r\n };\r\n // Wrap each adapter so per-model `call({...})` invocations receive\r\n // the abort signal via the existing `abort_signal` arg the closed\r\n // engine's ModelAdapter shape already accepts (line ~53). This is\r\n // the cancellation level that actually matters \u2014 provider fetch()\r\n // calls abort instead of running to completion.\r\n const panel: readonly ConsensusModelAdapter[] = signal === undefined\r\n ? options.panel\r\n : options.panel.map((adapter) => ({\r\n ...adapter,\r\n call: (args) => adapter.call({ ...args, abort_signal: signal }),\r\n }));\r\n // Feature 1 (agreement-gate, BEHAVIOUR-CHANGING) \u2014 resolve the opt-in.\r\n // DEFAULT OFF: only present when the client config or the\r\n // VO_CONSENSUS_AGREEMENT_GATE env flag turns it on. When absent the\r\n // engine fans out the whole panel exactly as before.\r\n const agreementGate = resolveAgreementGate({\r\n ...(options.agreement_gate_enabled !== undefined\r\n ? { configEnabled: options.agreement_gate_enabled }\r\n : {}),\r\n ...(options.env !== undefined ? { env: options.env } : {}),\r\n });\r\n const engineOptions: {\r\n panel: readonly ConsensusModelAdapter[];\r\n per_model_timeout_ms?: number;\r\n agreement_gate?: EngineAgreementGateShape;\r\n } = {\r\n panel,\r\n ...(options.per_model_timeout_ms !== undefined\r\n ? { per_model_timeout_ms: options.per_model_timeout_ms }\r\n : {}),\r\n ...(agreementGate !== undefined ? { agreement_gate: agreementGate } : {}),\r\n };\r\n\r\n // Source-grounded routing (Tier-4). When the caller supplied source URLs\r\n // AND the engine build exposes runSourceGroundedConsensus, route there so\r\n // the retrieval-confidence + citation-grader features activate. Otherwise\r\n // stay on the unchanged plain runConsensus fan-out.\r\n const sources = request.source_urls;\r\n const useSourceGrounded =\r\n sources !== undefined &&\r\n sources.length > 0 &&\r\n typeof engine.runSourceGroundedConsensus === 'function';\r\n\r\n let response: EngineConsensusResponseShape;\r\n let sourceExtras:\r\n | {\r\n citation_grade?: ReturnType<typeof mapCitationGrade>;\r\n low_confidence_sources?: readonly number[];\r\n escalation_required?: boolean;\r\n escalation_reason?: string;\r\n }\r\n | undefined;\r\n\r\n if (useSourceGrounded) {\r\n // Build the engine source-grounded option fields. citation_grader is\r\n // forwarded when the caller enables it AND a classifier is available.\r\n // When grading is enabled but the caller injected no classifier, we\r\n // supply a REAL default one built from the panel's first adapter\r\n // (reusing the live model the engine-client already constructed from\r\n // credentials) \u2014 this is what lights citation grading up instead of\r\n // leaving it dormant. The grader still stays OFF unless the caller\r\n // sets `citation_grader.enabled === true`.\r\n // panel.length >= 2 is guaranteed by the panel-too-small guard above,\r\n // so `firstAdapter` is always defined \u2014 but resolve it defensively\r\n // (no non-null assertion) and only build a default classifier when an\r\n // adapter exists. The default classifier reuses this live panel adapter\r\n // (built from real credentials) as its single-model caller.\r\n const [firstAdapter] = panel;\r\n const classifierOptions = signal === undefined ? {} : { signal };\r\n const defaultClassifier =\r\n firstAdapter === undefined\r\n ? undefined\r\n : createClaimClassifier(firstAdapter, classifierOptions);\r\n const sgFields = buildSourceGroundedFields(request.source_grounded, defaultClassifier);\r\n const sgOptions = {\r\n ...engineOptions,\r\n source_urls: sources,\r\n ...(sgFields.allow_url !== undefined ? { allow_url: sgFields.allow_url } : {}),\r\n ...(sgFields.citation_grader !== undefined\r\n ? { citation_grader: sgFields.citation_grader }\r\n : {}),\r\n };\r\n if (typeof engine.runSourceGroundedConsensus !== 'function') {\r\n return { ok: false, reason: 'source-grounded engine missing runSourceGroundedConsensus' };\r\n }\r\n const sgCall = engine.runSourceGroundedConsensus(engineRequest, sgOptions);\r\n const sgResult = signal === undefined\r\n ? await sgCall\r\n : await Promise.race([sgCall, raceAbort(signal)]);\r\n if (!sgResult.ok) {\r\n return { ok: false, reason: sgResult.reason };\r\n }\r\n response = sgResult.engine.response;\r\n sourceExtras = {\r\n ...(sgResult.citation_grade !== undefined\r\n ? { citation_grade: mapCitationGrade(sgResult.citation_grade) }\r\n : {}),\r\n ...(sgResult.low_confidence_sources !== undefined\r\n ? { low_confidence_sources: sgResult.low_confidence_sources }\r\n : {}),\r\n ...(sgResult.escalation_required !== undefined\r\n ? { escalation_required: sgResult.escalation_required }\r\n : {}),\r\n ...(sgResult.escalation_reason !== undefined\r\n ? { escalation_reason: sgResult.escalation_reason }\r\n : {}),\r\n };\r\n } else {\r\n // Promise.race the engine call against the signal so we return a\r\n // `cancelled` result PROMPTLY even if the closed engine ignores the\r\n // signal or the wrapped adapters don't honor it. Orphaned promises\r\n // may keep running in the background but the awaited call returns\r\n // immediately \u2014 that's the contract the MCP spec requires.\r\n const result = signal === undefined\r\n ? await engine.runConsensus(engineRequest, engineOptions)\r\n : await Promise.race([engine.runConsensus(engineRequest, engineOptions), raceAbort(signal)]);\r\n if (!result.ok || result.response === undefined) {\r\n return {\r\n ok: false,\r\n reason: result.error?.reason ?? 'engine-returned-not-ok',\r\n };\r\n }\r\n response = result.response;\r\n }\r\n\r\n // The synthesized_verdict already carries the engine's additive\r\n // calibrated_confidence + confidence_badge (Feature 2) when present \u2014\r\n // it is forwarded by value, so those fields flow through automatically.\r\n return {\r\n ok: true,\r\n synthesized_verdict: response.synthesized_verdict,\r\n per_model_verdicts: response.per_model_verdicts,\r\n degraded: response.degraded,\r\n duration_ms: response.duration_ms,\r\n engine_version: response.engine_version,\r\n // Phase 2 Lane D-1 \u2014 forward escalation signal when present. The\r\n // source-grounded layer's own escalation (from the citation grade)\r\n // takes precedence when set, else the synthesizer's.\r\n ...(sourceExtras?.escalation_required !== undefined\r\n ? { escalation_required: sourceExtras.escalation_required }\r\n : response.escalation_required !== undefined\r\n ? { escalation_required: response.escalation_required }\r\n : {}),\r\n ...(sourceExtras?.escalation_reason !== undefined\r\n ? { escalation_reason: sourceExtras.escalation_reason }\r\n : response.escalation_reason !== undefined\r\n ? { escalation_reason: response.escalation_reason }\r\n : {}),\r\n // Feature 1 (agreement-gate) \u2014 fan-out diagnostics (additive telemetry).\r\n ...(mapFanOutDiagnostics(response.fan_out_diagnostics) !== undefined\r\n ? { fan_out_diagnostics: mapFanOutDiagnostics(response.fan_out_diagnostics) }\r\n : {}),\r\n // Source-grounded additive outputs (Tier-4 features).\r\n ...(useSourceGrounded ? { source_grounded: true } : {}),\r\n ...(sourceExtras?.citation_grade !== undefined\r\n ? { citation_grade: sourceExtras.citation_grade }\r\n : {}),\r\n ...(sourceExtras?.low_confidence_sources !== undefined\r\n ? { low_confidence_sources: sourceExtras.low_confidence_sources }\r\n : {}),\r\n };\r\n } catch (err) {\r\n const message = err instanceof Error ? err.message : String(err);\r\n // Distinguish cancellation from a genuine engine throw. The\r\n // `__VO_MCP_CANCELLED__` sentinel comes from the Promise.race\r\n // abort branch above. Also catch native AbortError from anywhere\r\n // in the engine that respects the signal.\r\n if (message === '__VO_MCP_CANCELLED__' || signal?.aborted || (err instanceof Error && err.name === 'AbortError')) {\r\n return { ok: false, reason: 'cancelled' };\r\n }\r\n return { ok: false, reason: `engine-threw: ${message}` };\r\n }\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Phase 2 Lane A: env-driven adapter construction.\r\n *\r\n * Probes `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY` (the\r\n * actual env var names used by `functions-shared`'s `call*WithMetrics`\r\n * helpers \u2014 NOT `GEMINI_API_KEY` despite the dispatch's suggested\r\n * naming; see flagged operator decision in the PR body).\r\n *\r\n * Per Phase 2 Lane A scope: when at least TWO credentials are present,\r\n * builds the corresponding `ModelAdapter`s via the closed engine's\r\n * `createAdapter` factory and returns a populated `ConsensusEngineClient`.\r\n * When fewer than two are configured (cannot form a 2-model panel) it\r\n * returns null and the open-shell wiring falls through to the null\r\n * client, which surfaces the stable 'unimplemented' envelope.\r\n *\r\n * Caller injection is LAZY: the engine and `functions-shared` modules\r\n * are loaded on first adapter call, not at boot, so:\r\n * - The MCP server boots in O(ms) regardless of which deps are present.\r\n * - Tests can inject mocks via `tryCreateEngineConsensusClientFromEnvAsync`.\r\n *\r\n * Operator decisions flagged in the PR body:\r\n * - Env var names (canonical names from `functions-shared`)\r\n * - Behavior when only 1 of 3 keys is present (return null \u2192 null-client)\r\n * - Default model ids per provider (placeholders pending operator confirm)\r\n */\r\n\r\n/** Default model ids for each provider \u2014 placeholders pending operator confirm. */\r\nconst DEFAULT_MODELS: Readonly<Record<'anthropic' | 'openai' | 'google' | 'deepseek', string>> = {\r\n // These ids match the strategic-roadmap \u00A74 `newsStandard` / `newsDeep` panel\r\n // intent \u2014 current production model ids. Per handoff \u00A7C-3 these MUST come\r\n // from `CONSENSUS_PANELS` in `functions-shared/shared-model-resolvers.ts`\r\n // for V1; placeholder defaults here keep Phase 2 Lane A non-blocking.\r\n anthropic: 'claude-opus-4-7',\r\n openai: 'gpt-5',\r\n // gemini-2.5-FLASH (not -pro): flash accepts the default thinkingBudget=0 from\r\n // callGeminiWithMetrics; 2.5-pro REJECTS budget 0 (\"only works in thinking mode\").\r\n // Flash is also ~10x cheaper. 2026-06-02.\r\n google: 'gemini-2.5-flash',\r\n deepseek: 'deepseek-chat',\r\n};\r\n\r\ntype ProviderKey = 'anthropic' | 'openai' | 'google' | 'deepseek';\r\n\r\ninterface EngineFactoryShape {\r\n createAdapter(\r\n providerId: ProviderKey,\r\n options: {\r\n readonly model: string;\r\n readonly caller: (\r\n prompt: string,\r\n systemPrompt: string,\r\n model: string,\r\n maxTokens?: number,\r\n privacyOptions?: unknown,\r\n signal?: AbortSignal,\r\n options?: unknown,\r\n ) => Promise<{ content: string; inputTokens: number; outputTokens: number; totalTokens: number }>;\r\n readonly envSource?: Readonly<Record<string, string | undefined>>;\r\n },\r\n ): ConsensusModelAdapter;\r\n}\r\n\r\ninterface FunctionsSharedShape {\r\n callAnthropicWithMetrics: EngineFactoryShape['createAdapter'] extends (id: ProviderKey, o: { caller: infer C }) => unknown ? C : never;\r\n callOpenAIWithMetrics: EngineFactoryShape['createAdapter'] extends (id: ProviderKey, o: { caller: infer C }) => unknown ? C : never;\r\n callGeminiWithMetrics: EngineFactoryShape['createAdapter'] extends (id: ProviderKey, o: { caller: infer C }) => unknown ? C : never;\r\n callDeepSeekWithMetrics: EngineFactoryShape['createAdapter'] extends (id: ProviderKey, o: { caller: infer C }) => unknown ? C : never;\r\n}\r\n\r\n/** Per-env credential probe. Returns the set of providers with non-empty keys. */\r\nexport function probeProviders(\r\n env: Readonly<Record<string, string | undefined>> = process.env as Readonly<Record<string, string | undefined>>,\r\n): readonly ProviderKey[] {\r\n const out: ProviderKey[] = [];\r\n if ((env['ANTHROPIC_API_KEY'] ?? '').trim().length > 0) out.push('anthropic');\r\n if ((env['OPENAI_API_KEY'] ?? '').trim().length > 0) out.push('openai');\r\n if ((env['GOOGLE_API_KEY'] ?? '').trim().length > 0) out.push('google');\r\n if ((env['DEEPSEEK_API_KEY'] ?? '').trim().length > 0) out.push('deepseek');\r\n return out;\r\n}\r\n\r\nexport interface EngineFromEnvOptions {\r\n /** Override `process.env` lookup \u2014 primarily for tests. */\r\n readonly envSource?: Readonly<Record<string, string | undefined>>;\r\n /** Injected engine module \u2014 bypasses dynamic-import path for tests. */\r\n readonly engineModule?: EngineFactoryShape & EngineModuleShape;\r\n /** Injected functions-shared module \u2014 bypasses dynamic-import path for tests. */\r\n readonly functionsSharedModule?: FunctionsSharedShape;\r\n /** Override per-provider model ids. */\r\n readonly models?: Partial<Record<ProviderKey, string>>;\r\n /** Per-model timeout in ms; forwarded to the engine. */\r\n readonly per_model_timeout_ms?: number;\r\n}\r\n\r\n/**\r\n * Discriminated result of `loadFactoryAndCallers`. Diagnostic \u2014 the\r\n * `reason` on failure tells the operator which import or shape check\r\n * tripped (rather than a generic null). Added 2026-05-24 (audit MEDIUM \u2014\r\n * \"engine_unavailable reason-string differentiation\").\r\n */\r\ntype LoadFactoryResult =\r\n | { readonly ok: true; readonly engine: EngineFactoryShape & EngineModuleShape; readonly shared: FunctionsSharedShape }\r\n | {\r\n readonly ok: false;\r\n readonly reason:\r\n | typeof ENGINE_UNAVAILABLE_REASONS.ENGINE_PACKAGE_NOT_INSTALLED\r\n | typeof ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED;\r\n };\r\n\r\n// Plan PR #1 supersede (vo-pivot-plan-corrections \u00A75a): static imports\r\n// replace the prior dynamic-import-or-null dance now that consensus-engine\r\n// and functions-shared are workspace:* deps. Structural type guards retain\r\n// defense-in-depth against package-export drift; injection paths preserved\r\n// as higher-priority branch for tests. Diagnostic ENGINE_UNAVAILABLE_REASONS\r\n// (PR #5291) preserved end-to-end.\r\nasync function loadFactoryAndCallers(\r\n injectedEngine?: EngineFactoryShape & EngineModuleShape,\r\n injectedShared?: FunctionsSharedShape,\r\n): Promise<LoadFactoryResult> {\r\n let engineMod: unknown = injectedEngine;\r\n if (engineMod === undefined) {\r\n try {\r\n // Split specifier mirrors loadEngineModule's bundle-opacity trick.\r\n engineMod = await import(['@algosuite', 'consensus-engine'].join('/'));\r\n } catch {\r\n return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.ENGINE_PACKAGE_NOT_INSTALLED };\r\n }\r\n }\r\n if (\r\n engineMod === null ||\r\n typeof engineMod !== 'object' ||\r\n typeof (engineMod as { createAdapter?: unknown }).createAdapter !== 'function' ||\r\n typeof (engineMod as { runConsensus?: unknown }).runConsensus !== 'function'\r\n ) {\r\n return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.ENGINE_PACKAGE_NOT_INSTALLED };\r\n }\r\n\r\n let sharedMod: unknown = injectedShared;\r\n if (sharedMod === undefined) {\r\n try {\r\n sharedMod = await import(['functions', 'shared'].join('-'));\r\n } catch {\r\n return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED };\r\n }\r\n }\r\n if (\r\n sharedMod === null ||\r\n typeof sharedMod !== 'object' ||\r\n typeof (sharedMod as { callAnthropicWithMetrics?: unknown }).callAnthropicWithMetrics !== 'function' ||\r\n typeof (sharedMod as { callOpenAIWithMetrics?: unknown }).callOpenAIWithMetrics !== 'function' ||\r\n typeof (sharedMod as { callGeminiWithMetrics?: unknown }).callGeminiWithMetrics !== 'function'\r\n ) {\r\n return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED };\r\n }\r\n\r\n return {\r\n ok: true,\r\n engine: engineMod as EngineFactoryShape & EngineModuleShape,\r\n shared: sharedMod as FunctionsSharedShape,\r\n };\r\n}\r\n\r\n/**\r\n * Async variant \u2014 preferred for tests and for callers that can await.\r\n *\r\n * **2026-05-24 \u2014 audit MEDIUM diagnostic differentiation.** Previously\r\n * returned `null` for any failure (no credentials / package missing /\r\n * functions-shared missing / panel construction failed), and the cli.ts\r\n * fallback then surfaced a generic `'consensus-engine-package-pending'`\r\n * reason that didn't tell the operator which knob to turn. Now ALWAYS\r\n * returns a non-null `ConsensusEngineClient`:\r\n *\r\n * - Success \u2192 a real engine-backed client.\r\n * - Failure \u2192 a diagnostic null-client whose `run()` returns\r\n * `{ ok: false, reason: 'consensus-<specific-reason>' }`.\r\n *\r\n * The four specific reasons live in `null-client.ENGINE_UNAVAILABLE_REASONS`:\r\n * - `consensus-credentials-missing` \u2014 fewer than 2 API keys\r\n * - `consensus-engine-package-not-installed` \u2014 engine module missing/malformed\r\n * - `consensus-functions-shared-not-installed` \u2014 functions-shared missing/malformed\r\n * - `consensus-panel-construction-failed` \u2014 engine + creds + shared all\r\n * present but every per-provider adapter constructor threw\r\n *\r\n * Cross-vendor MCP clients still see the stable `unimplemented` envelope\r\n * via the tool-layer fallback; the only thing that changes is the\r\n * operator-actionable `reason` field on that envelope.\r\n */\r\nexport async function tryCreateEngineConsensusClientFromEnvAsync(\r\n options: EngineFromEnvOptions = {},\r\n): Promise<ConsensusEngineClient> {\r\n const env = options.envSource ?? (process.env as Readonly<Record<string, string | undefined>>);\r\n const providers = probeProviders(env);\r\n if (providers.length < 2) {\r\n return createNullConsensusEngineClient(ENGINE_UNAVAILABLE_REASONS.CREDENTIALS_MISSING);\r\n }\r\n\r\n const loaded = await loadFactoryAndCallers(options.engineModule, options.functionsSharedModule);\r\n if (!loaded.ok) {\r\n return createNullConsensusEngineClient(loaded.reason);\r\n }\r\n\r\n const callerByProvider: Readonly<Record<ProviderKey, FunctionsSharedShape['callAnthropicWithMetrics']>> = {\r\n anthropic: loaded.shared.callAnthropicWithMetrics,\r\n openai: loaded.shared.callOpenAIWithMetrics,\r\n google: loaded.shared.callGeminiWithMetrics,\r\n deepseek: loaded.shared.callDeepSeekWithMetrics,\r\n };\r\n const modelByProvider: Readonly<Record<ProviderKey, string>> = {\r\n anthropic: options.models?.anthropic ?? DEFAULT_MODELS.anthropic,\r\n openai: options.models?.openai ?? DEFAULT_MODELS.openai,\r\n google: options.models?.google ?? DEFAULT_MODELS.google,\r\n deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek,\r\n };\r\n\r\n const panel: ConsensusModelAdapter[] = [];\r\n for (const p of providers) {\r\n try {\r\n const adapter = loaded.engine.createAdapter(p, {\r\n model: modelByProvider[p],\r\n caller: callerByProvider[p],\r\n envSource: env,\r\n });\r\n panel.push(adapter);\r\n } catch {\r\n // Adapter construction failure for ONE provider doesn't kill the\r\n // panel \u2014 drop that provider and continue. If the surviving panel\r\n // is too small the surviving-providers branch below surfaces the\r\n // specific panel-construction-failed reason.\r\n }\r\n }\r\n if (panel.length < 2) {\r\n return createNullConsensusEngineClient(ENGINE_UNAVAILABLE_REASONS.PANEL_CONSTRUCTION_FAILED);\r\n }\r\n\r\n const clientOptions: EngineClientOptions = {\r\n panel,\r\n engineModule: loaded.engine,\r\n ...(options.per_model_timeout_ms !== undefined\r\n ? { per_model_timeout_ms: options.per_model_timeout_ms }\r\n : {}),\r\n };\r\n return createEngineConsensusClient(clientOptions);\r\n}\r\n\r\n/**\r\n * Synchronous variant for cli.ts compatibility. Performs the credential\r\n * probe synchronously and constructs a lazy client whose `run()` method\r\n * resolves the engine + functions-shared modules on first call.\r\n *\r\n * **2026-05-24 \u2014 audit MEDIUM:** now ALWAYS returns a non-null client\r\n * (matches the async variant's contract). On the failure path the client\r\n * returns `{ ok: false, reason: 'consensus-<specific>' }` so the\r\n * operator sees an actionable diagnostic. cli.ts no longer needs to\r\n * fall through to the legacy null-client; it can use the returned\r\n * client directly. The legacy null-client (`createNullConsensusEngineClient()`\r\n * with no args) remains exported for any caller that wants the original\r\n * `'consensus-engine-package-pending'` reason explicitly.\r\n *\r\n * Fast-boot is preserved: when \u22652 keys are present the returned client\r\n * lazily loads modules on first `run()`. When < 2 keys, the diagnostic\r\n * stub returns immediately with `consensus-credentials-missing` \u2014 no\r\n * module loading.\r\n */\r\nexport function tryCreateEngineConsensusClientFromEnv(\r\n options: EngineFromEnvOptions = {},\r\n): ConsensusEngineClient {\r\n const env = options.envSource ?? (process.env as Readonly<Record<string, string | undefined>>);\r\n if (probeProviders(env).length < 2) {\r\n return createNullConsensusEngineClient(ENGINE_UNAVAILABLE_REASONS.CREDENTIALS_MISSING);\r\n }\r\n\r\n // Cache the lazily-resolved client so we don't reload modules on\r\n // every consensus call.\r\n let resolved: Promise<ConsensusEngineClient> | null = null;\r\n\r\n return {\r\n async run(request) {\r\n if (resolved === null) {\r\n resolved = tryCreateEngineConsensusClientFromEnvAsync(options);\r\n }\r\n const real = await resolved;\r\n return real.run(request);\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Phase 2 Lane C \u2014 bridge `ContentHashCache` (vo-mcp's sqlite-backed\r\n * tool-response cache) to the engine's `CacheBackend` interface.\r\n *\r\n * The engine doesn't currently call into the cache through its public\r\n * `runConsensus` entry point \u2014 engine-level response caching is wired\r\n * at the open-shell tool layer (`consensus-judgment.ts` already uses\r\n * `deps.cache` directly). This adapter exists so future engine builds\r\n * can accept an injected backend and so callers outside vo-mcp (e.g.\r\n * a future direct-import Tax integration) can share the same store.\r\n *\r\n * Stored values are JSON-serialized round-trip via `ContentHashCache`'s\r\n * existing serializer. The adapter is async-over-sync \u2014 the underlying\r\n * sqlite is sync but the engine's contract is async.\r\n */\r\nexport interface CacheBackendShape {\r\n get<T>(key: string): Promise<T | null>;\r\n set<T>(key: string, value: T, ttlMs?: number): Promise<void>;\r\n delete(key: string): Promise<void>;\r\n clear(): Promise<void>;\r\n}\r\n\r\nexport function createCacheBackendAdapter(store: ContentHashCache): CacheBackendShape {\r\n return {\r\n async get<T>(key: string): Promise<T | null> {\r\n const row = store.get<T>(key);\r\n return row === null ? null : row.value;\r\n },\r\n async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {\r\n store.set(key, value, ttlMs);\r\n },\r\n async delete(key: string): Promise<void> {\r\n // PR-Q 2026-05-25: `ContentHashCache.delete()` is now a real method.\r\n // Previously this adapter silently no-op'd (the \"known limitation\"\r\n // documented in the prior implementation), which broke the engine's\r\n // CacheBackend contract at the integration boundary.\r\n store.delete(key);\r\n },\r\n async clear(): Promise<void> {\r\n // PR-Q 2026-05-25: same as `delete` \u2014 real implementation. Previously\r\n // this silently no-op'd; the engine layer documented the gap but it\r\n // would have bitten anyone calling the contract expecting actual purge.\r\n store.clear();\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Phase 2 Lane C \u2014 bridge `EventsWriter` to the engine's `LoggingBackend`\r\n * interface, merging engine-side log events with the MCP session context\r\n * needed to form a full `ConsensusCallEvent` (V1 gate #7 schema).\r\n *\r\n * Sanitization of free-text fields happens INSIDE this adapter (per\r\n * handoff \u00A7C-7) so the engine's already-sanitized excerpts can pass\r\n * through unchanged, but anything the adapter synthesizes here is\r\n * sanitized once more defensively.\r\n */\r\nexport interface LoggingBackendShape {\r\n emit(event: EngineLogEventShape): Promise<void>;\r\n}\r\n\r\n/** Mirrors the engine's `EngineLogEvent` structurally. Re-declared so the open shell has no closed import. */\r\nexport interface EngineLogEventShape {\r\n readonly ts: string;\r\n readonly input_hash: string;\r\n readonly input_excerpt: string;\r\n readonly input_size_bytes: number;\r\n readonly gate_type: string;\r\n readonly synthesizer: string;\r\n readonly per_model_verdicts: ReadonlyArray<{\r\n readonly model: string;\r\n readonly provider: string;\r\n readonly verdict: 'pass' | 'fail' | 'uncertain' | 'error';\r\n readonly confidence: number;\r\n readonly raw_response_excerpt: string;\r\n /** Per-model wall-clock duration. Optional for back-compat; defaults to 0 in the adapter. */\r\n readonly duration_ms?: number;\r\n /** Set when `verdict === 'error'` \u2014 category + message for triage. */\r\n readonly error?: { readonly category: string; readonly message: string };\r\n /** First ~500 chars of reasoning. Optional; null when not provided. */\r\n readonly reasoning_excerpt?: string;\r\n }>;\r\n readonly synthesized_verdict: {\r\n readonly verdict: 'pass' | 'fail' | 'uncertain';\r\n readonly confidence: number;\r\n readonly reasoning_excerpt: string;\r\n readonly synthesizer: string;\r\n readonly dissent_summary: string | null;\r\n } | null;\r\n readonly consensus_confidence: number | null;\r\n readonly duration_ms: number;\r\n readonly degraded: boolean;\r\n readonly engine_version: string;\r\n readonly cache_hit: boolean;\r\n /** Per-model input token counts. Optional until engine reports them. */\r\n readonly per_model_tokens_in?: Readonly<Record<string, number>>;\r\n /** Per-model output token counts. Optional until engine reports them. */\r\n readonly per_model_tokens_out?: Readonly<Record<string, number>>;\r\n /** Total cost in USD for the call. Optional until engine reports it. */\r\n readonly total_cost_usd?: number;\r\n}\r\n\r\nexport function createLoggingBackendAdapter(\r\n writer: EventsWriter,\r\n session: SessionContext,\r\n toolName = 'vo_consensus_judgment',\r\n): LoggingBackendShape {\r\n return {\r\n async emit(event: EngineLogEventShape): Promise<void> {\r\n // Translate engine-shaped event into the MCP-side ConsensusCallEvent.\r\n // V1 schema completion (2026-05-24 BLOCKER #1): preserve every field\r\n // the EngineLogEventShape carries; previously the adapter dropped 7+\r\n // fields breaking the cloud-ingestion contract.\r\n const merged: ConsensusCallEvent = {\r\n schema_version: 1,\r\n event_id: randomUUID(),\r\n ts: event.ts,\r\n tenant_id: session.tenantId,\r\n operator_id: session.operatorId,\r\n session_id: session.sessionId,\r\n client_id: session.clientId,\r\n mode: session.mode,\r\n tool: toolName,\r\n gate_type: event.gate_type,\r\n input_hash: event.input_hash,\r\n input_excerpt: sanitizeExcerpt(event.input_excerpt),\r\n input_size_bytes: event.input_size_bytes,\r\n per_model_verdicts: event.per_model_verdicts.map((v) => ({\r\n model: v.model,\r\n model_id: v.model,\r\n provider: v.provider,\r\n verdict: v.verdict,\r\n confidence: v.confidence,\r\n duration_ms: v.duration_ms ?? 0,\r\n raw_response_excerpt: sanitizeExcerpt(v.raw_response_excerpt),\r\n raw_response_hash: sha256Hex(v.raw_response_excerpt),\r\n reasoning_summary:\r\n v.reasoning_excerpt !== undefined && v.reasoning_excerpt.length > 0\r\n ? sanitizeExcerpt(v.reasoning_excerpt, 500)\r\n : null,\r\n cited_sources: [],\r\n error: v.error ?? null,\r\n })),\r\n synthesized_verdict:\r\n event.synthesized_verdict === null\r\n ? null\r\n : {\r\n verdict: event.synthesized_verdict.verdict,\r\n confidence: event.synthesized_verdict.confidence,\r\n reasoning_excerpt: sanitizeExcerpt(event.synthesized_verdict.reasoning_excerpt),\r\n },\r\n consensus_confidence: event.consensus_confidence,\r\n per_model_tokens_in: event.per_model_tokens_in ?? null,\r\n per_model_tokens_out: event.per_model_tokens_out ?? null,\r\n total_cost_usd: event.total_cost_usd ?? null,\r\n duration_ms: event.duration_ms,\r\n dev_override: null,\r\n downstream_outcome: null,\r\n vo_mcp_version: VO_MCP_VERSION,\r\n consensus_engine_version: event.engine_version,\r\n cache_hit: event.cache_hit,\r\n };\r\n writer.append(merged);\r\n },\r\n };\r\n}\r\n", "/**\r\n * Pure builders + output-mappers for the engine-client's option wiring.\r\n *\r\n * Extracted from `engine-client.ts` so the four consensus-engine features\r\n * (agreement-gate, calibrated-confidence, citation-grader, retrieval-confidence)\r\n * can be turned on / forwarded without growing the already-large client file.\r\n *\r\n * THE DEFAULTS \u2014 each documented inline (also see the dispatch report):\r\n * - Pure-additive OUTPUTS (calibrated_confidence + confidence_badge,\r\n * fan_out_diagnostics, retrieval-confidence ordinals) are forwarded WHENEVER\r\n * the engine produced them. They never change a verdict \u2014 only add fields \u2014\r\n * so they are effectively ON by default for VO gates.\r\n * - The agreement-gate EARLY-EXIT (behaviour-changing: skips part of the panel)\r\n * is OFF unless the operator opts in via `VO_CONSENSUS_AGREEMENT_GATE` (or a\r\n * client-config override). DEFAULT OFF.\r\n * - The citation-grader ESCALATION (behaviour-changing: raises an escalation\r\n * signal on a failing grade) is OFF unless the caller injects a\r\n * `ClaimClassifier` and sets `enabled: true`. DEFAULT OFF.\r\n *\r\n * Everything here is pure (no I/O) so it is trivially unit-testable.\r\n */\r\nimport type {\r\n ConsensusFanOutDiagnostics,\r\n ConsensusCitationGrade,\r\n ConsensusSourceGroundedConfig,\r\n} from './client.js';\r\n\r\n/** Env var (or `truthy` client flag) that opts INTO the agreement-gated early-exit fan-out. */\r\nexport const AGREEMENT_GATE_ENV_VAR = 'VO_CONSENSUS_AGREEMENT_GATE';\r\n\r\n/** Engine `AgreementGateOptions` shape \u2014 re-declared structurally (no closed import). */\r\nexport interface AgreementGateOptionsShape {\r\n readonly enabled: boolean;\r\n readonly probe_size?: number;\r\n readonly modal_agreement_threshold?: number;\r\n readonly min_confidence?: number;\r\n}\r\n\r\n/** Truthy-string check used for env-flag opt-ins (`1`, `true`, `yes`, `on`). */\r\nexport function isTruthyFlag(raw: string | undefined): boolean {\r\n if (raw === undefined) return false;\r\n const v = raw.trim().toLowerCase();\r\n return v === '1' || v === 'true' || v === 'yes' || v === 'on';\r\n}\r\n\r\n/**\r\n * Resolve whether the BEHAVIOUR-CHANGING agreement-gate early-exit is enabled.\r\n * Priority: explicit client config (boolean) > env var > OFF (default).\r\n *\r\n * Returns the engine `agreement_gate` options object when enabled, else\r\n * `undefined` (so the engine fans out the whole panel \u2014 unchanged behaviour).\r\n */\r\nexport function resolveAgreementGate(args: {\r\n readonly configEnabled?: boolean;\r\n readonly env?: Readonly<Record<string, string | undefined>>;\r\n readonly probeSize?: number;\r\n readonly modalAgreementThreshold?: number;\r\n readonly minConfidence?: number;\r\n}): AgreementGateOptionsShape | undefined {\r\n const env = args.env ?? (process.env as Readonly<Record<string, string | undefined>>);\r\n const enabled =\r\n args.configEnabled === true ||\r\n (args.configEnabled === undefined && isTruthyFlag(env[AGREEMENT_GATE_ENV_VAR]));\r\n if (!enabled) return undefined;\r\n return {\r\n enabled: true,\r\n ...(args.probeSize !== undefined ? { probe_size: args.probeSize } : {}),\r\n ...(args.modalAgreementThreshold !== undefined\r\n ? { modal_agreement_threshold: args.modalAgreementThreshold }\r\n : {}),\r\n ...(args.minConfidence !== undefined ? { min_confidence: args.minConfidence } : {}),\r\n };\r\n}\r\n\r\n/** The engine source-grounded `citation_grader` option shape (structural). */\r\nexport interface EngineCitationGraderOption {\r\n readonly enabled: boolean;\r\n /**\r\n * A CONCRETE classifier \u2014 never undefined at this layer. The open-shell\r\n * `ConsensusSourceGroundedConfig.citation_grader.classifier` is optional (a\r\n * caller may omit it to take the engine-client's default), but by the time the\r\n * grader option is BUILT (`buildSourceGroundedFields`) a classifier has always\r\n * been resolved, so `NonNullable` is the honest type the engine receives.\r\n */\r\n readonly classifier: NonNullable<\r\n ConsensusSourceGroundedConfig extends { citation_grader?: infer C }\r\n ? C extends { classifier?: infer K }\r\n ? K\r\n : never\r\n : never\r\n >;\r\n readonly escalate_below?: number;\r\n}\r\n\r\n/**\r\n * Translate the open-shell `source_grounded` config into the engine's\r\n * source-grounded option fields (`citation_grader`, `allow_url`).\r\n *\r\n * The grader is forwarded when `citation_grader.enabled === true` AND a\r\n * classifier is available. The classifier resolves in priority order:\r\n * 1. an explicitly INJECTED `config.citation_grader.classifier` (caller wins), else\r\n * 2. the `defaultClassifier` the engine-client builds from its own panel\r\n * adapter (a real single-model `ClaimClassifier` \u2014 see claim-classifier.ts).\r\n *\r\n * If grading is enabled but NEITHER a classifier is injected NOR a default is\r\n * supplied, the grader stays OFF (no fabricated always-'supported' stub). When\r\n * `enabled !== true` the grader is always OFF. Returns `{}` when nothing is\r\n * configured. This keeps the sourceless / grading-off paths byte-for-byte\r\n * unchanged while letting the default classifier light citation grading up when\r\n * a caller opts in.\r\n */\r\nexport function buildSourceGroundedFields(\r\n config: ConsensusSourceGroundedConfig | undefined,\r\n defaultClassifier?: EngineCitationGraderOption['classifier'],\r\n): {\r\n citation_grader?: EngineCitationGraderOption;\r\n allow_url?: (url: string) => boolean;\r\n} {\r\n if (config === undefined) return {};\r\n const out: { citation_grader?: EngineCitationGraderOption; allow_url?: (url: string) => boolean } = {};\r\n const grader = config.citation_grader;\r\n if (grader !== undefined && grader.enabled === true) {\r\n const classifier = (grader.classifier ?? defaultClassifier) as\r\n | EngineCitationGraderOption['classifier']\r\n | undefined;\r\n if (classifier !== undefined) {\r\n out.citation_grader = {\r\n enabled: true,\r\n classifier,\r\n ...(grader.escalate_below !== undefined ? { escalate_below: grader.escalate_below } : {}),\r\n };\r\n }\r\n }\r\n if (config.allow_url !== undefined) {\r\n out.allow_url = config.allow_url;\r\n }\r\n return out;\r\n}\r\n\r\n/** Engine `FanOutDiagnostics` shape (structural). */\r\nexport interface EngineFanOutDiagnosticsShape {\r\n readonly models_called: number;\r\n readonly panel_size: number;\r\n readonly early_exit: boolean;\r\n readonly refused: number;\r\n}\r\n\r\n/** Map the engine's fan-out diagnostics \u2192 the open-shell projection (additive). */\r\nexport function mapFanOutDiagnostics(\r\n fd: EngineFanOutDiagnosticsShape | undefined,\r\n): ConsensusFanOutDiagnostics | undefined {\r\n if (fd === undefined) return undefined;\r\n return {\r\n models_called: fd.models_called,\r\n panel_size: fd.panel_size,\r\n early_exit: fd.early_exit,\r\n refused: fd.refused,\r\n };\r\n}\r\n\r\n/** Engine `CitationGradeResult` shape (structural). */\r\nexport interface EngineCitationGradeShape {\r\n readonly ok: boolean;\r\n readonly unsupported_count: number;\r\n readonly uncertain_count: number;\r\n readonly graded_claims: ReadonlyArray<{\r\n readonly claim: string;\r\n readonly label: 'supported' | 'unsupported' | 'uncertain';\r\n readonly confidence: number;\r\n readonly cited_ids: readonly number[];\r\n }>;\r\n}\r\n\r\n/** Map the engine's citation grade \u2192 the open-shell projection (additive). */\r\nexport function mapCitationGrade(\r\n cg: EngineCitationGradeShape | undefined,\r\n): ConsensusCitationGrade | undefined {\r\n if (cg === undefined) return undefined;\r\n return {\r\n ok: cg.ok,\r\n unsupported_count: cg.unsupported_count,\r\n uncertain_count: cg.uncertain_count,\r\n graded_claims: cg.graded_claims.map((g) => ({\r\n claim: g.claim,\r\n label: g.label,\r\n confidence: g.confidence,\r\n cited_ids: [...g.cited_ids],\r\n })),\r\n };\r\n}\r\n", "/**\r\n * Real `ClaimClassifier` for the source-grounded citation grader (Tier-4).\r\n *\r\n * The engine's citation grader needs a `ClaimClassifierShape` \u2014 given a single\r\n * claim plus the concatenated text of the cited sources, it must answer \"is this\r\n * claim supported by these sources?\". Until now no real classifier existed: the\r\n * open shell forwarded one ONLY when a caller injected it, and there was no\r\n * default \u2014 so citation grading was dormant even on the source-grounded path.\r\n *\r\n * This module supplies the missing piece. It is a THIN wrapper over ONE model\r\n * call: it reuses the same `call({ system_prompt, user_prompt })` shape the\r\n * engine-client's `ConsensusModelAdapter` already exposes (so the engine-client\r\n * can hand it its own first panel adapter, built from real credentials), and it\r\n * maps the model's verdict back to the grader's\r\n * `{ label: 'supported'|'unsupported'|'uncertain', confidence }` contract.\r\n *\r\n * It deliberately does NOT fabricate an always-'supported' stub \u2014 an unsupported\r\n * claim must come back `unsupported` so a failing grade escalates. On any model\r\n * error / malformed output it fails SAFE to `{ label: 'uncertain', confidence: 0 }`\r\n * (per the `ClaimClassifierShape` contract: MUST NOT throw), which the engine\r\n * treats as a low-confidence claim rather than silently passing it.\r\n */\r\nimport type { ClaimClassifierShape, ConsensusVerdict } from './client.js';\r\n\r\n/**\r\n * Minimal model surface the classifier needs. A single-model caller that takes a\r\n * system + user prompt and returns a verdict-style result. The engine-client's\r\n * `ConsensusModelAdapter.call` is a structural superset of this, so the\r\n * engine-client can pass one of its own adapters directly. Tests inject a fake.\r\n */\r\nexport interface ClaimClassifierModel {\r\n call(args: {\r\n readonly system_prompt: string;\r\n readonly user_prompt: string;\r\n readonly abort_signal?: AbortSignal;\r\n }): Promise<{\r\n readonly verdict: ConsensusVerdict | 'error';\r\n readonly confidence: number;\r\n readonly error?: { readonly category: string; readonly message: string };\r\n }>;\r\n}\r\n\r\n/** Result label the engine's citation grader consumes. */\r\nexport type ClaimLabel = 'supported' | 'unsupported' | 'uncertain';\r\n\r\n// The model behind `classify` is the engine panel adapter, whose response\r\n// parser (`parseModelResponse`) only yields a real verdict when the model\r\n// concludes with the strict `VERDICT:` / `CONFIDENCE:` protocol lines. So the\r\n// classifier MUST instruct that exact format \u2014 otherwise every classification\r\n// falls through to `parse_failed \u2192 uncertain, confidence 0` and the grader can\r\n// never get a supported/unsupported signal (the gate then escalates on every\r\n// source-grounded call regardless of grounding).\r\nconst CLASSIFIER_SYSTEM_PROMPT = [\r\n 'You are a citation-grading judge. You are given a CLAIM and the full text of the SOURCES it cites.',\r\n 'Decide whether the SOURCES directly support the CLAIM. Judge ONLY against the supplied sources \u2014 do not use outside knowledge.',\r\n 'pass = the sources clearly support the claim; fail = the sources contradict or do not support it; uncertain = the sources are insufficient or ambiguous.',\r\n 'After a one-sentence justification, conclude with EXACTLY one of these lines:',\r\n ' VERDICT: pass',\r\n ' VERDICT: fail',\r\n ' VERDICT: uncertain',\r\n 'Then on a new line, exactly:',\r\n ' CONFIDENCE: <0..1>',\r\n 'Do not include any other VERDICT or CONFIDENCE lines.',\r\n].join('\\n');\r\n\r\n/**\r\n * Map a single-model verdict to the citation grader's claim label.\r\n * pass \u2192 supported\r\n * fail \u2192 unsupported\r\n * uncertain \u2192 uncertain\r\n * error \u2192 uncertain (fail-safe; the model errored, so we cannot vouch)\r\n *\r\n * Exported for direct unit testing of the mapping in isolation.\r\n */\r\nexport function verdictToLabel(verdict: ConsensusVerdict | 'error'): ClaimLabel {\r\n switch (verdict) {\r\n case 'pass':\r\n return 'supported';\r\n case 'fail':\r\n return 'unsupported';\r\n case 'uncertain':\r\n return 'uncertain';\r\n default:\r\n return 'uncertain';\r\n }\r\n}\r\n\r\n/** Clamp a model confidence into [0, 1]; coerce NaN / non-finite to 0. */\r\nfunction clampConfidence(raw: number): number {\r\n if (!Number.isFinite(raw)) return 0;\r\n if (raw < 0) return 0;\r\n if (raw > 1) return 1;\r\n return raw;\r\n}\r\n\r\n/**\r\n * Build the user prompt for one claim/sources pair. Kept pure + exported so the\r\n * exact wording is testable and the classifier stays a thin shell.\r\n */\r\nexport function buildClassifyPrompt(claim: string, sources: string): string {\r\n return [\r\n 'CLAIM:',\r\n claim,\r\n '',\r\n 'SOURCES:',\r\n sources,\r\n '',\r\n 'Does the cited SOURCES text support the CLAIM? Reply with your verdict ' +\r\n '(pass = supported, fail = unsupported, uncertain = insufficient).',\r\n ].join('\\n');\r\n}\r\n\r\n/**\r\n * Construct a real `ClaimClassifier` backed by a single model call.\r\n *\r\n * @param model The single-model caller (an engine adapter in prod, a fake in tests).\r\n * @param options.signal Optional abort signal forwarded to the model call.\r\n */\r\nexport function createClaimClassifier(\r\n model: ClaimClassifierModel,\r\n options: { readonly signal?: AbortSignal } = {},\r\n): ClaimClassifierShape {\r\n return {\r\n async classify(args: {\r\n readonly claim: string;\r\n readonly sources: string;\r\n }): Promise<{ readonly label: ClaimLabel; readonly confidence: number }> {\r\n try {\r\n const result = await model.call({\r\n system_prompt: CLASSIFIER_SYSTEM_PROMPT,\r\n user_prompt: buildClassifyPrompt(args.claim, args.sources),\r\n ...(options.signal !== undefined ? { abort_signal: options.signal } : {}),\r\n });\r\n if (result.verdict === 'error') {\r\n return { label: 'uncertain', confidence: 0 };\r\n }\r\n return {\r\n label: verdictToLabel(result.verdict),\r\n confidence: clampConfidence(result.confidence),\r\n };\r\n } catch {\r\n // Contract: MUST NOT throw. A thrown model call (network, abort, etc.)\r\n // degrades to a low-confidence uncertain claim, never a silent pass.\r\n return { label: 'uncertain', confidence: 0 };\r\n }\r\n },\r\n };\r\n}\r\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["homedir", "join", "dirname", "existsSync", "mkdirSync", "readFileSync", "writeFileSync", "chmodSync", "randomUUID", "createHash", "readFileSync", "dirname", "join", "fileURLToPath", "readdirSync", "readFileSync", "statSync", "homedir", "dirname", "join", "z", "z", "existsSync", "readFileSync", "join", "join", "path", "existsSync", "readFileSync", "path", "MAX_EXCERPT", "join", "homedir", "statSync", "readFileSync", "dirname", "readdirSync", "dirname", "fileURLToPath", "
|
|
4
|
+
"sourcesContent": ["/**\n * Auth token sources for the cloud admin client \u2014 Increment 3 of the VO Command\n * Center unification (`docs/vo/vo-command-center-unification-spec-2026-06-05.md`\n * \u00A76): retire the shared god admin-token from the LOCAL install. Instead of a\n * single static `VO_CONTROL_PLANE_ADMIN_TOKEN`, the client can authenticate as\n * the USER with a short-lived Firebase ID token (the same credential the\n * dashboard sends; the control-plane already accepts it for an allow-listed\n * operator \u2014 `cloud-run/vo-control-plane/src/firebase-auth.ts`).\n *\n * Three sources, selected by env (per-user preferred):\n * 1. `VO_USER_REFRESH_TOKEN` (+ `VO_FIREBASE_API_KEY`) \u2192 auto-refreshing Firebase\n * user token (durable; exchanges the refresh token for fresh ID tokens via\n * the PUBLIC securetoken endpoint \u2014 no backend, no god-token, no model keys).\n * 2. `VO_USER_ID_TOKEN` \u2192 a raw Firebase ID token (simplest interim; expires ~1h).\n * 3. `VO_CONTROL_PLANE_ADMIN_TOKEN` \u2192 the legacy static god-token (back-compat).\n *\n * FAIL-CLOSED: a source that cannot produce a token returns `null`; the caller\n * (admin client) then refuses the request rather than sending an empty Bearer.\n *\n * NOTE: this slice removes the god-token from the *client config* (the user's\n * own credential is sent instead). Acquiring the initial refresh/ID token via a\n * browser/device-flow `login` command, and OS-keychain storage, are follow-up\n * slices (spec \u00A77); for now the token is supplied via env.\n */\n\n/** A source of a fresh bearer token for the control-plane. */\nexport interface AuthTokenSource {\n /** Stable label for diagnostics/logs \u2014 NEVER the token value. */\n readonly kind: 'admin-token' | 'firebase-id-token' | 'firebase-refresh' | 'vo-credential';\n /** Returns a fresh bearer token, or `null` when unavailable (fail-closed). */\n getToken(): Promise<string | null>;\n}\n\ntype FetchLike = (\n url: string,\n init?: { method?: string; headers?: Record<string, string>; body?: string },\n) => Promise<{ status: number; text: () => Promise<string> }>;\n\n/** Public Google endpoint that exchanges a Firebase refresh token for a fresh ID token. */\nexport const FIREBASE_SECURETOKEN_URL = 'https://securetoken.googleapis.com/v1/token';\n\n/**\n * The Algosuite Firebase Web API key is HTTP-referrer-restricted, and Google's\n * Identity Toolkit / securetoken endpoints reject key'd requests that arrive with\n * an EMPTY Referer (HTTP 403 \"Requests from referer <empty> are blocked\"). Browser\n * callers send one automatically; Node `fetch` sends none \u2014 so every server-side\n * exchange must pin an origin the key's restriction allows. Live-diagnosed\n * 2026-06-09: without this header the whole Inc 3a refresh flow (and the Inc 3b.4b\n * vocred_ exchange that builds on it) fail-opens silently.\n */\nexport const FIREBASE_TOKEN_REFERER = 'https://algosuite.ai/';\n\n/** Refresh this many ms BEFORE the ID token actually expires (clock-skew margin). */\nconst REFRESH_SKEW_MS = 60_000;\n\n/** A fixed, already-available token (the god-token or a pasted ID token). */\nexport function createStaticTokenSource(\n token: string,\n kind: AuthTokenSource['kind'] = 'admin-token',\n): AuthTokenSource {\n const value = token.trim();\n return { kind, getToken: async () => (value.length > 0 ? value : null) };\n}\n\nexport interface FirebaseRefreshTokenSourceOptions {\n readonly refreshToken: string;\n /** The Firebase Web API key (PUBLIC, not a secret) for the Algosuite project. */\n readonly apiKey: string;\n /** Injectable clock (ms) for tests; defaults to `Date.now`. */\n readonly now?: () => number;\n /** Injectable fetch for tests; defaults to the global `fetch`. */\n readonly fetchFn?: FetchLike;\n}\n\n/**\n * Auto-refreshing Firebase user token. Exchanges the long-lived refresh token\n * for short-lived ID tokens via the public securetoken endpoint and caches the\n * ID token until shortly before it expires. Fail-closed: any error \u21D2 `null`.\n */\nexport function createFirebaseRefreshTokenSource(\n opts: FirebaseRefreshTokenSourceOptions,\n): AuthTokenSource {\n const refreshToken = opts.refreshToken.trim();\n const apiKey = opts.apiKey.trim();\n const now = opts.now ?? (() => Date.now());\n const fetchFn: FetchLike = opts.fetchFn ?? (globalThis.fetch as unknown as FetchLike);\n\n let cachedToken: string | null = null;\n let expiresAtMs = 0;\n // Collapse concurrent refreshes so N parallel tool calls trigger ONE exchange.\n let inFlight: Promise<string | null> | null = null;\n\n async function refresh(): Promise<string | null> {\n try {\n const res = await fetchFn(`${FIREBASE_SECURETOKEN_URL}?key=${encodeURIComponent(apiKey)}`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/x-www-form-urlencoded',\n referer: FIREBASE_TOKEN_REFERER,\n },\n body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`,\n });\n const text = await res.text();\n if (res.status < 200 || res.status >= 300) {\n cachedToken = null;\n return null;\n }\n const parsed = JSON.parse(text) as { id_token?: unknown; expires_in?: unknown };\n const idToken = typeof parsed.id_token === 'string' ? parsed.id_token : '';\n if (!idToken) {\n cachedToken = null;\n return null;\n }\n const expiresInSec = Number(parsed.expires_in);\n const ttlMs = Number.isFinite(expiresInSec) && expiresInSec > 0 ? expiresInSec * 1000 : 3_600_000;\n cachedToken = idToken;\n expiresAtMs = now() + ttlMs;\n return idToken;\n } catch {\n cachedToken = null;\n return null;\n }\n }\n\n return {\n kind: 'firebase-refresh',\n async getToken(): Promise<string | null> {\n if (cachedToken && now() < expiresAtMs - REFRESH_SKEW_MS) return cachedToken;\n if (!inFlight) {\n inFlight = refresh().finally(() => {\n inFlight = null;\n });\n }\n return inFlight;\n },\n };\n}\n\n/**\n * Select an auth token source from env. Per-user credentials win over the legacy\n * god-token, so a thin install configured with the user's token needs NO\n * `VO_CONTROL_PLANE_ADMIN_TOKEN`. Returns `null` when nothing is configured.\n * Throws on a half-configured refresh source (refresh token without API key).\n */\n/** Minimal stored-credential shape this selector consumes (from `vo-mcp login`). */\nexport interface StoredRefreshCredential {\n /** Firebase refresh token (optional once a scoped vocred_ is minted \u2014 Inc 3b.4b). */\n readonly refresh_token?: string;\n readonly api_key?: string;\n /** Scoped, revocable VO credential (Inc 3b.4b) \u2014 preferred over the raw refresh. */\n readonly vo_credential?: string;\n}\n\nexport function createAuthTokenSourceFromEnv(\n env: Readonly<Record<string, string | undefined>> = process.env,\n fetchFn?: FetchLike,\n /**\n * Inc 3b: a reader for a stored login credential (`vo-mcp login`). Injected so\n * the env-only callers (and tests) stay filesystem-free; production passes\n * `readStoredCredential`. A stored login credential is preferred over the legacy\n * god-token (so logging in retires it) but explicit env user-tokens still win.\n */\n readStoredCred: () => StoredRefreshCredential | null = () => null,\n): AuthTokenSource | null {\n const refreshToken = env['VO_USER_REFRESH_TOKEN']?.trim();\n const apiKey = env['VO_FIREBASE_API_KEY']?.trim();\n const idToken = env['VO_USER_ID_TOKEN']?.trim();\n const adminToken = env['VO_CONTROL_PLANE_ADMIN_TOKEN']?.trim();\n\n // 1. explicit env per-user refresh (CI / override).\n if (refreshToken || apiKey) {\n if (!refreshToken || !apiKey) {\n throw new Error(\n 'Per-user refresh auth requires BOTH VO_USER_REFRESH_TOKEN and VO_FIREBASE_API_KEY',\n );\n }\n return createFirebaseRefreshTokenSource({\n refreshToken,\n apiKey,\n ...(fetchFn ? { fetchFn } : {}),\n });\n }\n // 2. explicit env ID token.\n if (idToken) return createStaticTokenSource(idToken, 'firebase-id-token');\n // 3. stored login credential (`vo-mcp login`) \u2014 preferred over the god-token.\n const stored = readStoredCred();\n // 3a. a scoped vocred_ (Inc 3b.4b) wins \u2014 revocable, server-validated, and it\n // means no raw Firebase refresh token sits on disk. Sent as a static bearer;\n // the control-plane validates expiry/revocation (a stale one \u21D2 401 \u21D2 re-login).\n if (stored?.vo_credential && stored.vo_credential.trim()) {\n return createStaticTokenSource(stored.vo_credential.trim(), 'vo-credential');\n }\n // 3b. else the Firebase refresh credential.\n if (stored && stored.refresh_token?.trim() && stored.api_key?.trim()) {\n return createFirebaseRefreshTokenSource({\n refreshToken: stored.refresh_token.trim(),\n apiKey: stored.api_key.trim(),\n ...(fetchFn ? { fetchFn } : {}),\n });\n }\n // 4. legacy static god-token (back-compat).\n if (adminToken) return createStaticTokenSource(adminToken, 'admin-token');\n return null;\n}\n", "/**\n * Optional OS-keychain backend for the thin-client credential store (Increment\n * 3b.3 \u2014 `docs/vo/vo-command-center-inc3b-login-design-2026-06-05.md` \u00A75/\u00A76).\n *\n * Loads `@napi-rs/keyring` at runtime via `createRequire`, so it is a TRUE\n * optional dependency: if the native module is absent or fails to load\n * (unsupported platform, prebuilt binary missing, headless CI), every function\n * degrades to a no-op and the caller (`credential-store.ts`) falls back to the\n * 0600 file store. `@napi-rs/keyring`'s `Entry` API is SYNCHRONOUS, so the\n * credential store stays synchronous \u2014 no async ripple into the Inc-3a token\n * source that reads it.\n *\n * Why `createRequire` and not a static/dynamic `import`: a static import would\n * make the native module a HARD dependency (a missing prebuilt would crash the\n * MCP at startup); a dynamic `import()` is async (would force the whole read\n * path async). `createRequire(...)` inside a try/catch loads it lazily and\n * synchronously, and a load failure is just \"keychain unavailable\".\n */\nimport { createRequire } from 'node:module';\n\n/** Keychain service + account the single refresh credential is stored under. */\nconst SERVICE = 'vo-mcp';\nconst ACCOUNT = 'refresh-credential';\n\ninterface KeyringEntry {\n getPassword(): string | null;\n setPassword(password: string): void;\n deletePassword(): boolean;\n}\ninterface KeyringModule {\n Entry: new (service: string, account: string) => KeyringEntry;\n}\n\n// undefined = not yet attempted; null = attempted and unavailable.\nlet cached: KeyringModule | null | undefined;\n\nfunction loadKeyring(): KeyringModule | null {\n if (cached !== undefined) return cached;\n try {\n const req = createRequire(import.meta.url);\n const mod = req('@napi-rs/keyring') as Partial<KeyringModule>;\n cached = mod && typeof mod.Entry === 'function' ? (mod as KeyringModule) : null;\n } catch {\n cached = null;\n }\n return cached;\n}\n\n/** True when the OS keychain backend is usable in this runtime. */\nexport function keychainAvailable(): boolean {\n return loadKeyring() !== null;\n}\n\n/** Read the raw stored secret string from the OS keychain, or null. Never throws. */\nexport function keychainGet(): string | null {\n const k = loadKeyring();\n if (!k) return null;\n try {\n return new k.Entry(SERVICE, ACCOUNT).getPassword();\n } catch {\n return null;\n }\n}\n\n/** Store the raw secret string in the OS keychain. Returns true on success. Never throws. */\nexport function keychainSet(secret: string): boolean {\n const k = loadKeyring();\n if (!k) return false;\n try {\n new k.Entry(SERVICE, ACCOUNT).setPassword(secret);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Delete the stored secret from the OS keychain. Returns true if an entry was\n * removed. Never throws \u2014 a no-op (and `false`) when the backend is unavailable\n * or the entry is absent. Used to keep ONE source of truth: when the credential\n * is (re)written to the file, any stale keychain entry is cleared so it can't\n * shadow the newer file credential on read (and vice-versa).\n */\nexport function keychainDelete(): boolean {\n const k = loadKeyring();\n if (!k) return false;\n try {\n return new k.Entry(SERVICE, ACCOUNT).deletePassword();\n } catch {\n return false;\n }\n}\n\n/** Test-only seam to reset the memoised module load. */\nexport function __resetKeychainCache(): void {\n cached = undefined;\n}\n", "/**\n * Local credential store for the thin-client `vo-mcp login` flow (Increment 3b,\n * Option A \u2014 `docs/vo/vo-command-center-inc3b-login-design-2026-06-05.md`).\n *\n * Persists the per-user Firebase refresh token (the user's OWN credential, never\n * the god-token, never model keys) captured by `login`, so the auto-refreshing\n * token source (Inc 3a) can mint fresh ID tokens across MCP restarts.\n *\n * Storage precedence (Inc 3b.3):\n * 1. **OS keychain** (Windows Credential Manager / macOS Keychain / libsecret)\n * via the optional `@napi-rs/keyring` backend (`keychain.ts`). The DEFAULT\n * when available \u2014 the secret never lands in plaintext on disk.\n * 2. **0600 file** at `$VO_MCP_CREDENTIALS_PATH` or `~/.config/vo-mcp/credentials.json`.\n * The fallback when the keychain is unavailable or disabled\n * (`VO_MCP_DISABLE_KEYCHAIN`). `VO_MCP_CREDENTIALS_PATH` only sets the file\n * LOCATION; force file storage with `VO_MCP_DISABLE_KEYCHAIN`.\n *\n * `env`-supplied tokens (`VO_USER_REFRESH_TOKEN`, etc.) still win over BOTH\n * stores \u2014 that precedence lives upstream in `auth-token-source.ts`.\n *\n * **Single source of truth.** The credential lives in EITHER the keychain OR the\n * file, never both: a write to one store CLEARS the other, so a stale entry can\n * never shadow the current credential on read, and the secret never lingers in\n * plaintext after a migration to the keychain.\n *\n * **Keychain durability.** A keychain-stored credential is only readable while\n * the `@napi-rs/keyring` native module loads. If the module later becomes\n * unavailable (an ABI break across a Node upgrade, a corrupted install), the\n * credential can't be read and the user re-runs `vo-mcp login` \u2014 the same\n * behaviour as `gh` / `gcloud` / `firebase` keychain storage. We deliberately do\n * NOT mirror the secret to a plaintext file as a fallback: that would defeat the\n * entire point of keychain storage (keeping the secret off plaintext disk).\n */\nimport { homedir } from 'node:os';\nimport { join, dirname } from 'node:path';\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n chmodSync,\n rmSync,\n} from 'node:fs';\n\nimport { keychainAvailable, keychainGet, keychainSet, keychainDelete } from './keychain.js';\n\nexport interface StoredCredential {\n /**\n * Firebase refresh token (long-lived; exchanged for short-lived ID tokens).\n * OPTIONAL since Inc 3b.4b: once a scoped `vo_credential` is minted, the raw\n * refresh token is dropped, so a stored credential may carry ONLY the vocred_.\n */\n readonly refresh_token?: string;\n /** Firebase Web API key (PUBLIC) needed for the securetoken refresh exchange. */\n readonly api_key?: string;\n /**\n * Scoped, revocable VO credential (`vocred_`) minted by the control-plane\n * (Inc 3b.4b). Preferred over the raw refresh token; lets the client present a\n * revocable, server-side credential instead of the Firebase refresh token.\n */\n readonly vo_credential?: string;\n /** ISO-8601 expiry of `vo_credential` (the client re-logs-in past this). */\n readonly vo_credential_expires_at?: string;\n /** The signed-in operator email (diagnostics only). */\n readonly email?: string;\n /** ISO timestamp the credential was stored. */\n readonly stored_at?: string;\n}\n\n/**\n * Pluggable OS-keychain backend. Defaults to the real `@napi-rs/keyring` wrapper;\n * tests inject a deterministic fake so they never touch the host keychain.\n */\nexport interface KeychainBackend {\n available(): boolean;\n get(): string | null;\n set(secret: string): boolean;\n delete(): boolean;\n}\n\nconst realKeychain: KeychainBackend = {\n available: keychainAvailable,\n get: keychainGet,\n set: keychainSet,\n delete: keychainDelete,\n};\n\n/** Human-readable \"location\" returned when the credential was stored in the OS keychain. */\nexport const KEYCHAIN_LOCATION = 'OS keychain (service \"vo-mcp\")';\n\n/** Resolve the credentials file path (env override \u2192 XDG-ish default under home). */\nexport function credentialPath(env: Readonly<Record<string, string | undefined>> = process.env): string {\n const override = env['VO_MCP_CREDENTIALS_PATH']?.trim();\n if (override) return override;\n return join(homedir(), '.config', 'vo-mcp', 'credentials.json');\n}\n\n/**\n * Whether the keychain should be consulted at all (read OR write). False when the\n * native backend is unavailable or `VO_MCP_DISABLE_KEYCHAIN` is set (CI/headless).\n */\nfunction keychainEnabled(\n env: Readonly<Record<string, string | undefined>>,\n keychain: KeychainBackend,\n): boolean {\n const disabled = (env['VO_MCP_DISABLE_KEYCHAIN'] ?? '').trim().toLowerCase();\n if (disabled === '1' || disabled === 'true' || disabled === 'yes') return false;\n return keychain.available();\n}\n\n/** Parse + validate a stored credential blob. Returns null on any problem (never throws). */\nfunction deserialize(raw: string): StoredCredential | null {\n try {\n const parsed = JSON.parse(raw) as Partial<StoredCredential>;\n const refresh = typeof parsed.refresh_token === 'string' ? parsed.refresh_token.trim() : '';\n const apiKey = typeof parsed.api_key === 'string' ? parsed.api_key.trim() : '';\n const voCred = typeof parsed.vo_credential === 'string' ? parsed.vo_credential.trim() : '';\n // Valid if it carries a scoped vocred_ OR a full Firebase refresh pair.\n if (!voCred && (!refresh || !apiKey)) return null;\n return {\n ...(refresh ? { refresh_token: refresh } : {}),\n ...(apiKey ? { api_key: apiKey } : {}),\n ...(voCred ? { vo_credential: voCred } : {}),\n ...(typeof parsed.vo_credential_expires_at === 'string' ? { vo_credential_expires_at: parsed.vo_credential_expires_at } : {}),\n ...(typeof parsed.email === 'string' ? { email: parsed.email } : {}),\n ...(typeof parsed.stored_at === 'string' ? { stored_at: parsed.stored_at } : {}),\n };\n } catch {\n return null;\n }\n}\n\nfunction readFromFile(env: Readonly<Record<string, string | undefined>>): StoredCredential | null {\n try {\n const p = credentialPath(env);\n if (!existsSync(p)) return null;\n return deserialize(readFileSync(p, 'utf8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Read the stored credential, or `null` if absent/unreadable/invalid (never\n * throws). Consults an ENABLED keychain first (regardless of the write-target\n * flags, so a credential written to the keychain is found even if\n * `VO_MCP_CREDENTIALS_PATH` is later set), then the 0600 file.\n */\nexport function readStoredCredential(\n env: Readonly<Record<string, string | undefined>> = process.env,\n keychain: KeychainBackend = realKeychain,\n): StoredCredential | null {\n if (keychainEnabled(env, keychain)) {\n const raw = keychain.get();\n const fromKeychain = raw ? deserialize(raw) : null;\n if (fromKeychain) return fromKeychain;\n }\n return readFromFile(env);\n}\n\nfunction deleteFile(env: Readonly<Record<string, string | undefined>>): void {\n try {\n rmSync(credentialPath(env), { force: true });\n } catch {\n /* best-effort */\n }\n}\n\nfunction writeToFile(\n payload: StoredCredential,\n env: Readonly<Record<string, string | undefined>>,\n): string {\n const p = credentialPath(env);\n mkdirSync(dirname(p), { recursive: true });\n writeFileSync(p, `${JSON.stringify(payload, null, 2)}\\n`, { mode: 0o600 });\n // Best-effort tighten (no-op / throws on some Windows filesystems \u2014 ignore).\n try {\n chmodSync(p, 0o600);\n } catch {\n /* best-effort */\n }\n return p;\n}\n\n/**\n * Persist the credential. Prefers the OS keychain (secret never hits plaintext\n * disk); otherwise writes the 0600 file. Writing to one store CLEARS the other\n * (single source of truth \u2014 no stale shadow, no lingering plaintext). Returns the\n * location it was stored (`KEYCHAIN_LOCATION` or the file path).\n */\nexport function writeStoredCredential(\n cred: StoredCredential,\n storedAt: string,\n env: Readonly<Record<string, string | undefined>> = process.env,\n keychain: KeychainBackend = realKeychain,\n): string {\n const payload: StoredCredential = {\n ...(cred.refresh_token ? { refresh_token: cred.refresh_token } : {}),\n ...(cred.api_key ? { api_key: cred.api_key } : {}),\n ...(cred.vo_credential ? { vo_credential: cred.vo_credential } : {}),\n ...(cred.vo_credential_expires_at ? { vo_credential_expires_at: cred.vo_credential_expires_at } : {}),\n ...(cred.email ? { email: cred.email } : {}),\n stored_at: cred.stored_at ?? storedAt,\n };\n if (keychainEnabled(env, keychain) && keychain.set(JSON.stringify(payload))) {\n // Stored in the keychain \u2192 clear any stale plaintext file so the secret\n // doesn't linger on disk and can't shadow the keychain on read.\n deleteFile(env);\n return KEYCHAIN_LOCATION;\n }\n const p = writeToFile(payload, env);\n // Stored in the file \u2192 clear any stale keychain entry so it can't shadow the\n // newer file credential on read.\n if (keychainEnabled(env, keychain)) keychain.delete();\n return p;\n}\n", "/**\n * VO MCP server \u2014 registration and request handling.\n *\n * Built against `@modelcontextprotocol/sdk` 1.29.x. Stdio transport.\n *\n * Responsibilities:\n * 1. Build the `ToolDeps` runtime object (cache, events writer, ratchet\n * client, session context).\n * 2. Register tool definitions (name, description, input schema) for\n * `tools/list`.\n * 3. Dispatch `tools/call` invocations to the per-tool handlers.\n *\n * Cross-vendor design: nothing in here assumes a Claude-specific client.\n * `client_id` is read off the `initialize` handshake (`getClientVersion()`).\n */\nimport { randomUUID } from 'node:crypto';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { methodNotFound } from './tools/common.js';\n\nimport type { ContentHashCache } from './cache/sqlite-cache.js';\nimport type { AdminCallableClient } from './cloud/admin-callable-client.js';\nimport type { EventsWriter } from './logging/events-writer.js';\nimport type { RatchetClient } from './ratchets/client.js';\nimport type { ConsensusEngineClient } from './consensus/client.js';\nimport { createLocalMode } from './modes/local.js';\nimport type { SessionContext, ToolDeps } from './types.js';\n\nimport * as checkAssertionStrength from './tools/check-assertion-strength.js';\nimport * as checkHollowTest from './tools/check-hollow-test.js';\nimport * as verifyAnswer from './tools/verify-answer.js';\nimport * as consensusJudgment from './tools/consensus-judgment.js';\nimport * as architectureReview from './tools/architecture-review.js';\nimport * as checkRatchets from './tools/check-ratchets.js';\nimport * as decomposeDispatch from './tools/decompose-dispatch.js';\nimport * as triggerHeal from './tools/heal/trigger-heal.js';\nimport * as fixRetry from './tools/heal/fix-retry.js';\nimport * as fixClear from './tools/heal/fix-clear.js';\nimport * as stopWorkflow from './tools/heal/stop-workflow.js';\nimport * as getWorkflowRuns from './tools/heal/get-workflow-runs.js';\nimport * as listPendingPRs from './tools/pr/list-pending-prs.js';\nimport * as mergePR from './tools/pr/merge-pr.js';\nimport * as rejectPR from './tools/pr/reject-pr.js';\nimport * as approveAllFixes from './tools/pr/approve-all-fixes.js';\nimport * as rejectAndRetry from './tools/pr/reject-and-retry.js';\nimport * as reviewMerge from './tools/pr/review-merge.js';\nimport * as reportSessionState from './tools/session/report-session-state.js';\nimport * as spawnSuccessor from './tools/session/spawn-successor.js';\nimport * as conciergeDispatch from './tools/concierge/dispatch.js';\nimport * as syncConfig from './tools/memory/sync-config.js';\n\nexport interface CreateServerOptions {\n readonly cache: ContentHashCache;\n readonly events: EventsWriter;\n readonly ratchets: RatchetClient;\n readonly consensus: ConsensusEngineClient;\n /** Optional injection for tests; defaults to `() => new Date()`. */\n readonly now?: () => Date;\n /** Optional session ID; defaults to a fresh uuid. */\n readonly sessionId?: string;\n /**\n * Optional cloud-mode admin proxy client. When set, heal/PR family\n * tools forward to vo-control-plane's `/api/v1/admin/*` endpoints\n * instead of returning their stub envelopes. Wired by `cli.ts` when\n * `VO_CONTROL_PLANE_URL` + `VO_CONTROL_PLANE_ADMIN_TOKEN` are present.\n */\n readonly adminCallables?: AdminCallableClient | null;\n}\n\ntype ToolHandler = (\n deps: ToolDeps,\n rawInput: unknown,\n /**\n * Cancellation signal threaded from the MCP SDK's `RequestHandlerExtra`.\n * Fires when the client sends `notifications/cancelled` for this request,\n * or when the transport closes mid-call. Tool handlers MUST pass it down\n * to any awaited long-running work (consensus engine call, ratchet probe)\n * so per-model API fetches can abort. Added 2026-05-25 (audit HIGH \u2014\n * MCP `notifications/cancelled` handling).\n */\n signal?: AbortSignal,\n) => Promise<{\n content: Array<{ type: 'text'; text: string }>;\n}>;\n\ninterface RegisteredTool {\n readonly definition: Tool;\n readonly handler: ToolHandler;\n}\n\nfunction buildToolRegistry(): Record<string, RegisteredTool> {\n return {\n [checkAssertionStrength.TOOL_NAME]: {\n definition: {\n name: checkAssertionStrength.TOOL_NAME,\n description: checkAssertionStrength.description,\n inputSchema: checkAssertionStrength.inputSchema,\n },\n handler: checkAssertionStrength.handleCheckAssertionStrength,\n },\n [checkHollowTest.TOOL_NAME]: {\n definition: {\n name: checkHollowTest.TOOL_NAME,\n description: checkHollowTest.description,\n inputSchema: checkHollowTest.inputSchema,\n },\n handler: checkHollowTest.handleCheckHollowTest,\n },\n [verifyAnswer.TOOL_NAME]: {\n definition: {\n name: verifyAnswer.TOOL_NAME,\n description: verifyAnswer.description,\n inputSchema: verifyAnswer.inputSchema,\n },\n handler: verifyAnswer.handleVerifyAnswer,\n },\n [consensusJudgment.TOOL_NAME]: {\n definition: {\n name: consensusJudgment.TOOL_NAME,\n description: consensusJudgment.description,\n inputSchema: consensusJudgment.inputSchema,\n },\n handler: consensusJudgment.handleConsensusJudgment,\n },\n [architectureReview.TOOL_NAME]: {\n definition: {\n name: architectureReview.TOOL_NAME,\n description: architectureReview.description,\n inputSchema: architectureReview.inputSchema,\n },\n handler: architectureReview.handleArchitectureReview,\n },\n [decomposeDispatch.TOOL_NAME]: {\n definition: {\n name: decomposeDispatch.TOOL_NAME,\n description: decomposeDispatch.description,\n inputSchema: decomposeDispatch.inputSchema,\n },\n handler: decomposeDispatch.handleDecomposeDispatch,\n },\n [checkRatchets.TOOL_NAME]: {\n definition: {\n name: checkRatchets.TOOL_NAME,\n description: checkRatchets.description,\n inputSchema: checkRatchets.inputSchema,\n },\n handler: checkRatchets.handleCheckRatchets,\n },\n [triggerHeal.TOOL_NAME]: {\n definition: {\n name: triggerHeal.TOOL_NAME,\n description: triggerHeal.description,\n inputSchema: triggerHeal.inputSchema,\n },\n handler: triggerHeal.handleTriggerHeal,\n },\n [fixRetry.TOOL_NAME]: {\n definition: {\n name: fixRetry.TOOL_NAME,\n description: fixRetry.description,\n inputSchema: fixRetry.inputSchema,\n },\n handler: fixRetry.handleFixRetry,\n },\n [fixClear.TOOL_NAME]: {\n definition: {\n name: fixClear.TOOL_NAME,\n description: fixClear.description,\n inputSchema: fixClear.inputSchema,\n },\n handler: fixClear.handleFixClear,\n },\n [stopWorkflow.TOOL_NAME]: {\n definition: {\n name: stopWorkflow.TOOL_NAME,\n description: stopWorkflow.description,\n inputSchema: stopWorkflow.inputSchema,\n },\n handler: stopWorkflow.handleStopWorkflow,\n },\n [getWorkflowRuns.TOOL_NAME]: {\n definition: {\n name: getWorkflowRuns.TOOL_NAME,\n description: getWorkflowRuns.description,\n inputSchema: getWorkflowRuns.inputSchema,\n },\n handler: getWorkflowRuns.handleGetWorkflowRuns,\n },\n [listPendingPRs.TOOL_NAME]: {\n definition: {\n name: listPendingPRs.TOOL_NAME,\n description: listPendingPRs.description,\n inputSchema: listPendingPRs.inputSchema,\n },\n handler: listPendingPRs.handleListPendingPRs,\n },\n [mergePR.TOOL_NAME]: {\n definition: {\n name: mergePR.TOOL_NAME,\n description: mergePR.description,\n inputSchema: mergePR.inputSchema,\n },\n handler: mergePR.handleMergePR,\n },\n [rejectPR.TOOL_NAME]: {\n definition: {\n name: rejectPR.TOOL_NAME,\n description: rejectPR.description,\n inputSchema: rejectPR.inputSchema,\n },\n handler: rejectPR.handleRejectPR,\n },\n [approveAllFixes.TOOL_NAME]: {\n definition: {\n name: approveAllFixes.TOOL_NAME,\n description: approveAllFixes.description,\n inputSchema: approveAllFixes.inputSchema,\n },\n handler: approveAllFixes.handleApproveAllFixes,\n },\n [rejectAndRetry.TOOL_NAME]: {\n definition: {\n name: rejectAndRetry.TOOL_NAME,\n description: rejectAndRetry.description,\n inputSchema: rejectAndRetry.inputSchema,\n },\n handler: rejectAndRetry.handleRejectAndRetry,\n },\n [reviewMerge.TOOL_NAME]: {\n definition: {\n name: reviewMerge.TOOL_NAME,\n description: reviewMerge.description,\n inputSchema: reviewMerge.inputSchema,\n },\n handler: reviewMerge.handleReviewMerge,\n },\n [reportSessionState.TOOL_NAME]: {\n definition: {\n name: reportSessionState.TOOL_NAME,\n description: reportSessionState.description,\n inputSchema: reportSessionState.inputSchema,\n },\n handler: reportSessionState.handleReportSessionState,\n },\n [spawnSuccessor.TOOL_NAME]: {\n definition: {\n name: spawnSuccessor.TOOL_NAME,\n description: spawnSuccessor.description,\n inputSchema: spawnSuccessor.inputSchema,\n },\n handler: spawnSuccessor.handleSpawnSuccessor,\n },\n [conciergeDispatch.TOOL_NAME]: {\n definition: {\n name: conciergeDispatch.TOOL_NAME,\n description: conciergeDispatch.description,\n inputSchema: conciergeDispatch.inputSchema,\n },\n handler: conciergeDispatch.handleConciergeDispatch,\n },\n [syncConfig.TOOL_NAME]: {\n definition: {\n name: syncConfig.TOOL_NAME,\n description: syncConfig.description,\n inputSchema: syncConfig.inputSchema,\n },\n handler: syncConfig.handleSyncConfig,\n },\n };\n}\n\nexport function createServer(options: CreateServerOptions): Server {\n const sessionId = options.sessionId ?? randomUUID();\n const mode = createLocalMode();\n const now = options.now ?? ((): Date => new Date());\n\n const server = new Server(\n {\n name: 'vo-mcp',\n version: '0.1.0',\n },\n {\n capabilities: {\n tools: {},\n },\n },\n );\n\n const registry = buildToolRegistry();\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: Object.values(registry).map((t) => t.definition),\n };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (req, extra) => {\n const toolName = req.params.name;\n const entry = registry[toolName];\n if (!entry) {\n throw methodNotFound(toolName, Object.keys(registry));\n }\n\n // The MCP SDK exposes the connected client's identity via\n // `server.getClientVersion()`. Read it lazily per-call so tests\n // that don't go through a transport still work (clientId defaults\n // to 'unknown' in that case).\n const clientInfo = server.getClientVersion();\n const session: SessionContext = {\n sessionId,\n clientId: clientInfo?.name ?? 'unknown',\n mode: mode.mode,\n tenantId: mode.tenantId,\n operatorId: mode.operatorId,\n };\n\n const deps: ToolDeps = {\n session,\n cache: options.cache,\n events: options.events,\n ratchets: options.ratchets,\n consensus: options.consensus,\n now,\n adminCallables: options.adminCallables ?? null,\n };\n\n // Forward the SDK-provided cancellation signal so tool handlers can\n // thread it into the consensus engine call (and any other long-\n // running work). The SDK auto-fires this signal when the connected\n // client sends `notifications/cancelled` for this request, or when\n // the transport closes mid-call. 2026-05-25 audit HIGH \u2014 MCP\n // `notifications/cancelled` handling.\n return entry.handler(deps, req.params.arguments ?? {}, extra?.signal);\n });\n\n return server;\n}\n\n/** Exported for tests \u2014 the in-process tool registry. */\nexport function listToolNames(): readonly string[] {\n return Object.keys(buildToolRegistry());\n}\n", "/**\n * Shared helpers for tool implementations.\n */\nimport { createHash, randomUUID } from 'node:crypto';\nimport { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { Tool } from '@modelcontextprotocol/sdk/types.js';\nimport { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';\nimport type {\n ConsensusCallEvent,\n PerModelVerdict,\n SessionContext,\n SynthesizedVerdict,\n} from '../types.js';\nimport { sanitizeExcerpt } from '../logging/events-writer.js';\nimport type {\n ConsensusPerModelVerdict,\n ConsensusSuccess,\n ConsensusSynthesizedVerdict,\n} from '../consensus/client.js';\n\n/**\n * vo-mcp package semver \u2014 populated once at module init from package.json.\n * Threaded into every `ConsensusCallEvent` as `vo_mcp_version` so cloud\n * ingestion can correlate event-shape drift with package releases.\n * Falls back to `0.0.0-unknown` if package.json can't be read (test isolation).\n */\nfunction readVoMcpVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url));\n // package.json sits at different depths depending on layout:\n // source / tsc build : src|dist/tools/common.* \u2192 ../../package.json\n // esbuild publish bundle : dist/cli.js (flattened) \u2192 ../package.json\n // Walk the candidate depths nearest-first and accept ONLY the vo-mcp\n // package.json (the name guard prevents picking a parent/monorepo\n // package.json one level too far up). Without this the bundled runner\n // reports 0.0.0-unknown, hiding which version is actually running.\n for (const rel of ['..', ['..', '..'], ['..', '..', '..']] as const) {\n try {\n const segs = Array.isArray(rel) ? rel : [rel];\n const pkg = JSON.parse(readFileSync(join(here, ...segs, 'package.json'), 'utf8')) as {\n name?: string;\n version?: string;\n };\n if (pkg.name === '@algosuite/vo-mcp' && typeof pkg.version === 'string') return pkg.version;\n } catch {\n // try the next depth\n }\n }\n return '0.0.0-unknown';\n } catch {\n return '0.0.0-unknown';\n }\n}\n\nexport const VO_MCP_VERSION: string = readVoMcpVersion();\n\n/** UTF-8 byte length of a string. Use for `input_size_bytes` on events. */\nexport function bytesOf(s: string): number {\n return Buffer.byteLength(s, 'utf8');\n}\n\n/**\n * sha256 of an arbitrary string, hex-encoded. Used for `raw_response_hash`.\n * Currently hashes the response excerpt the engine returns (the engine\n * doesn't yet thread the full raw response); will hash full text once the\n * engine adds that opt-in.\n */\nexport function sha256Hex(s: string): string {\n return createHash('sha256').update(s, 'utf8').digest('hex');\n}\n\n/** The shape MCP's `Tool.inputSchema` requires. Re-exported so tool files import it. */\nexport type ToolInputSchema = Tool['inputSchema'];\n\n/**\n * Throw an MCP-compliant `InvalidParams` (-32602) error for a bad tool input.\n *\n * Cross-vendor MCP clients (Cursor, Continue, Codex) branch on `error.code`\n * per the JSON-RPC spec; throwing a plain Node `Error` surfaces as `-32603`\n * (internal error) instead, breaking automated recovery. Tool input\n * validators MUST use this helper, not `throw new Error(...)`.\n */\nexport function invalidParams(toolName: string, message: string): McpError {\n return new McpError(ErrorCode.InvalidParams, `${toolName}: ${message}`);\n}\n\n/**\n * Throw an MCP-compliant `MethodNotFound` (-32601) error for an unregistered tool.\n */\nexport function methodNotFound(toolName: string, knownTools: readonly string[]): McpError {\n return new McpError(\n ErrorCode.MethodNotFound,\n `Unknown tool: ${toolName}. Registered tools: ${knownTools.join(', ')}`,\n );\n}\n\n/**\n * Per-field input-size cap. Throws `InvalidParams` (-32602) with a precise\n * over-by-N-bytes message so the caller can trim and retry. Byte-counted\n * (not char-counted) to bound real wire/memory cost.\n *\n * Caller picks the cap per field \u2014 there is no global default because the\n * right cap depends on the tool's gate (a 1MB diff is fine for architecture\n * review; a 1MB consensus prompt would blow the model context window).\n */\nexport function assertWithinByteCap(\n toolName: string,\n fieldName: string,\n value: string,\n maxBytes: number,\n): void {\n const bytes = Buffer.byteLength(value, 'utf8');\n if (bytes > maxBytes) {\n throw invalidParams(\n toolName,\n `input field '${fieldName}' exceeds ${maxBytes}-byte cap (got ${bytes} bytes). Trim the input or split across multiple calls.`,\n );\n }\n}\n\nexport interface BuildEventArgs {\n readonly tool: string;\n /** Which gate the tool routes to. Required since V1 gate #7 schema completion. */\n readonly gateType: string;\n readonly inputHash: string;\n readonly inputExcerpt: string;\n /** UTF-8 byte length of the original input. */\n readonly inputSizeBytes: number;\n readonly session: SessionContext;\n readonly now: Date;\n /** Override `event_id` (tests). Default: `randomUUID()`. */\n readonly eventId?: string;\n}\n\n/**\n * Build the V1 gate #7 base event. Late-update fields (per_model_verdicts,\n * synthesized_verdict, consensus_confidence, duration_ms, token counts,\n * cost, engine_version) are initialized to neutral defaults and overwritten\n * by tool-specific code paths (cache replay, engine success).\n */\nexport function buildBaseEvent(args: BuildEventArgs): ConsensusCallEvent {\n return {\n schema_version: 1,\n event_id: args.eventId ?? randomUUID(),\n ts: args.now.toISOString(),\n tenant_id: args.session.tenantId,\n operator_id: args.session.operatorId,\n session_id: args.session.sessionId,\n client_id: args.session.clientId,\n mode: args.session.mode,\n tool: args.tool,\n gate_type: args.gateType,\n input_hash: args.inputHash,\n input_excerpt: sanitizeExcerpt(args.inputExcerpt),\n input_size_bytes: args.inputSizeBytes,\n per_model_verdicts: [],\n synthesized_verdict: null,\n consensus_confidence: null,\n per_model_tokens_in: null,\n per_model_tokens_out: null,\n total_cost_usd: null,\n duration_ms: null,\n dev_override: null,\n downstream_outcome: null,\n vo_mcp_version: VO_MCP_VERSION,\n consensus_engine_version: null,\n cache_hit: false,\n };\n}\n\n/**\n * MCP content[] response wrapper. Tool handlers always return a single\n * text block whose `text` is JSON-encoded `ToolResultEnvelope`.\n */\nexport function jsonContent(value: unknown): { content: Array<{ type: 'text'; text: string }> } {\n return {\n content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],\n };\n}\n\n/**\n * Project engine `per_model_verdicts` onto the open-shell `PerModelVerdict`\n * shape used inside `ConsensusCallEvent`. Re-sanitizes excerpts defensively\n * (the engine already sanitizes, but the open shell owns the contract).\n *\n * Shared by Phase 2 Lane D-1 tools (`vo_check_hollow_test`, `vo_verify_answer`,\n * `vo_architecture_review`) and the existing `vo_consensus_judgment` tool;\n * extracting it removes the original copy-paste in `consensus-judgment.ts`.\n */\nexport function toEventPerModelVerdicts(\n src: ConsensusSuccess['per_model_verdicts'],\n): readonly PerModelVerdict[] {\n return src.map((v: ConsensusPerModelVerdict): PerModelVerdict => {\n const sanitizedExcerpt = sanitizeExcerpt(v.raw_response_excerpt);\n return {\n model: v.model,\n model_id: v.model,\n provider: v.provider,\n verdict: v.verdict,\n confidence: v.confidence,\n duration_ms: v.duration_ms,\n raw_response_excerpt: sanitizedExcerpt,\n // Hash over the sanitized excerpt \u2014 engine doesn't yet thread the full\n // raw response. Documented limitation on `raw_response_hash` in types.ts.\n raw_response_hash: sha256Hex(v.raw_response_excerpt),\n reasoning_summary:\n typeof v.reasoning_excerpt === 'string' && v.reasoning_excerpt.length > 0\n ? sanitizeExcerpt(v.reasoning_excerpt, 500)\n : null,\n // Engine doesn't yet emit cited_sources; ship empty array so cloud\n // ingestion doesn't NPE on `Array.isArray()` checks.\n cited_sources: [],\n error: v.error ?? null,\n };\n });\n}\n\n/** Companion to `toEventPerModelVerdicts` \u2014 projects the synthesized verdict. */\nexport function toEventSynthesizedVerdict(src: ConsensusSynthesizedVerdict): SynthesizedVerdict {\n return {\n verdict: src.verdict,\n confidence: src.confidence,\n reasoning_excerpt: sanitizeExcerpt(src.reasoning_excerpt),\n };\n}\n", "/**\n * Consensus-call events writer (V1 launch gate #7).\n *\n * Appends one JSONL line per tool invocation to either\n * `$VO_MCP_EVENTS_PATH` or `~/.claude/vo-mcp-events.jsonl`.\n *\n * Design notes:\n * - Append-only. Never rewrite the file. Cloud ingestion can tail it.\n * - Sync `appendFileSync` \u2014 small writes, low latency, avoids backpressure\n * on the MCP request loop. If this becomes a bottleneck, swap for a\n * buffered writer; the EventsWriter interface intentionally leaves\n * room for that.\n * - PII: callers must sanitize `input_excerpt` before passing it in.\n * The writer does NOT inspect content (we don't know the tool's\n * schema). See `sanitizeExcerpt` helper below for the canonical pass.\n * - Rotation (audit HIGH 2026-05-24 fix): when the file crosses\n * `maxBytes` (default 50 MB), gzip-rotate to `<path>.<ISO-ts>.gz`,\n * reset the counter, and prune to last `keepRotated` (default 10)\n * rotated files. On disk-full (`ENOSPC`): log to stderr + DROP the\n * event (best-effort audit log; never throws into the tool handler).\n */\nimport {\n appendFileSync,\n chmodSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs';\nimport { homedir } from 'node:os';\nimport { basename, dirname, join } from 'node:path';\nimport { gzipSync } from 'node:zlib';\nimport type { ConsensusCallEvent } from '../types.js';\n\nexport interface EventsWriter {\n append(event: ConsensusCallEvent): void;\n /** Absolute path of the events file (or 'memory' for the in-memory variant). */\n path(): string;\n close(): void;\n}\n\nexport interface FileEventsWriterOptions {\n /** Override the events file location. Defaults to `$VO_MCP_EVENTS_PATH` or `~/.claude/vo-mcp-events.jsonl`. */\n readonly path?: string;\n /**\n * Rotate the file when it would exceed this many bytes after the next\n * write. Default: 50 MiB (52428800). Env override:\n * `VO_MCP_EVENTS_MAX_BYTES` (must be a positive integer).\n */\n readonly maxBytes?: number;\n /**\n * Number of rotated `.gz` files to retain after pruning. Default 10.\n * Older rotated files are unlinked. Env override:\n * `VO_MCP_EVENTS_KEEP_ROTATED` (must be a positive integer).\n */\n readonly keepRotated?: number;\n}\n\n/** Default rotation threshold: 50 MiB. Per 2026-05-24 audit HIGH. */\nexport const DEFAULT_EVENTS_MAX_BYTES = 50 * 1024 * 1024;\n/** Default rotated-file retention. */\nexport const DEFAULT_EVENTS_KEEP_ROTATED = 10;\n\nexport function defaultEventsPath(): string {\n const envPath = process.env['VO_MCP_EVENTS_PATH'];\n if (envPath && envPath.length > 0) return envPath;\n return join(homedir(), '.claude', 'vo-mcp-events.jsonl');\n}\n\n/** Parse a positive integer env var; null on missing/invalid. */\nfunction envPositiveInt(name: string): number | null {\n const raw = process.env[name];\n if (raw === undefined || raw.length === 0) return null;\n const n = Number(raw);\n return Number.isFinite(n) && n > 0 && Number.isInteger(n) ? n : null;\n}\n\n/**\n * Rotate the events file: gzip-rename the current contents to\n * `<path>.<ISO-ts>.gz`, leaving the original file removed (the next\n * append re-creates it). Best-effort: rotation failures are stderr-logged\n * and swallowed \u2014 the calling append still completes (the line is just\n * written to the non-rotated file).\n *\n * Returns true on successful rotation, false otherwise.\n */\nfunction rotateNow(filePath: string): boolean {\n try {\n if (!statSync(filePath).isFile()) return false;\n const ts = new Date().toISOString().replace(/[:.]/g, '-');\n const rotatedPath = `${filePath}.${ts}.gz`;\n const contents = readFileSync(filePath);\n writeFileSync(rotatedPath, gzipSync(contents));\n try {\n chmodSync(rotatedPath, 0o600);\n } catch {\n /* Windows / unsupported FS */\n }\n // Reset the live file by removing it; next appendFileSync re-creates.\n unlinkSync(filePath);\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[vo-mcp] events log rotation failed: ${msg}\\n`);\n return false;\n }\n}\n\n/**\n * Prune `<filePath>.*.gz` rotated files down to the newest `keep` entries.\n * Best-effort: errors are silently dropped (the worst that happens is\n * extra disk usage, not a tool-call failure).\n */\nfunction pruneRotated(filePath: string, keep: number): void {\n try {\n const dir = dirname(filePath);\n const baseName = basename(filePath);\n const prefix = `${baseName}.`;\n const rotated: Array<{ full: string; mtimeMs: number }> = [];\n for (const entry of readdirSync(dir)) {\n if (!entry.startsWith(prefix) || !entry.endsWith('.gz')) continue;\n const full = join(dir, entry);\n try {\n rotated.push({ full, mtimeMs: statSync(full).mtimeMs });\n } catch {\n /* ignore \u2014 race condition; file vanished */\n }\n }\n rotated.sort((a, b) => b.mtimeMs - a.mtimeMs); // newest first\n for (const r of rotated.slice(keep)) {\n try {\n unlinkSync(r.full);\n } catch {\n /* ignore */\n }\n }\n } catch {\n /* ignore \u2014 best-effort pruning */\n }\n}\n\nexport function createFileEventsWriter(opts: FileEventsWriterOptions = {}): EventsWriter {\n const filePath = opts.path ?? defaultEventsPath();\n const maxBytes =\n opts.maxBytes ?? envPositiveInt('VO_MCP_EVENTS_MAX_BYTES') ?? DEFAULT_EVENTS_MAX_BYTES;\n const keepRotated =\n opts.keepRotated ?? envPositiveInt('VO_MCP_EVENTS_KEEP_ROTATED') ?? DEFAULT_EVENTS_KEEP_ROTATED;\n // 0o700 dir: only the operator account can list / open it.\n mkdirSync(dirname(filePath), { recursive: true, mode: 0o700 });\n let permsApplied = false;\n // Track size in-process to avoid statSync per append. Initialize from disk\n // (handles MCP server restarts where the existing events file has content).\n let currentBytes = 0;\n try {\n currentBytes = statSync(filePath).size;\n } catch {\n /* file doesn't exist yet \u2014 currentBytes stays 0 */\n }\n return {\n append(event: ConsensusCallEvent): void {\n const line = JSON.stringify(event) + '\\n';\n const lineBytes = Buffer.byteLength(line, 'utf8');\n // Rotate BEFORE writing if the new line would push us over the threshold.\n // Edge case: a single line larger than maxBytes still goes into a fresh\n // file (rotation only protects against accumulation; one event always\n // fits even if it temporarily exceeds the cap).\n if (currentBytes > 0 && currentBytes + lineBytes > maxBytes) {\n if (rotateNow(filePath)) {\n pruneRotated(filePath, keepRotated);\n currentBytes = 0;\n permsApplied = false; // chmod on the next-created file\n }\n // If rotation failed, we fall through and write to the existing file\n // anyway \u2014 the audit log is best-effort, growing past maxBytes is\n // better than dropping events for spurious rotation failures.\n }\n try {\n appendFileSync(filePath, line, 'utf8');\n currentBytes += lineBytes;\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ENOSPC') {\n // Disk full \u2014 drop the event rather than throw. Tool calls MUST\n // succeed even when the audit log can't be written; audit-log\n // gaps are visible (line counts diverge) but tool failures are\n // not (every consensus call would error out).\n process.stderr.write(\n `[vo-mcp] events log write failed (disk full): event dropped\\n`,\n );\n return;\n }\n // Other errors (permission denied, FS read-only, etc.) \u2014 re-throw so\n // the operator notices. These are unrecoverable misconfigurations.\n throw err;\n }\n // 0o600 \u2014 events JSONL contains input_excerpt fields with code text\n // (already sanitized via sanitizeExcerpt) but still operator-sensitive.\n // Best-effort once-per-process; chmod no-ops on Windows.\n if (!permsApplied) {\n try {\n chmodSync(filePath, 0o600);\n } catch {\n /* ignore \u2014 Windows / unsupported FS */\n }\n permsApplied = true;\n }\n },\n path(): string {\n return filePath;\n },\n close(): void {\n // appendFileSync opens-and-closes per write; nothing to release.\n },\n };\n}\n\n/**\n * In-memory writer used by unit tests. Captures appended events in order.\n */\nexport interface MemoryEventsWriter extends EventsWriter {\n readonly events: ConsensusCallEvent[];\n}\n\nexport function createMemoryEventsWriter(): MemoryEventsWriter {\n const events: ConsensusCallEvent[] = [];\n return {\n events,\n append(event: ConsensusCallEvent): void {\n events.push(event);\n },\n path(): string {\n return 'memory';\n },\n close(): void {\n /* no-op */\n },\n };\n}\n\n/**\n * `sanitizeExcerpt` PII / secret scrubber \u2014 moved to `@algosuite/vo-arch-defaults`\n * in PR-U 2026-05-25 so every KB consumer (vo-mcp, the standalone\n * `vo-arch-check` CLI, the future cloud control plane) can sanitize evidence\n * excerpts without depending on vo-mcp.\n *\n * Re-exported here for back-compat with existing import sites\n * (`import { sanitizeExcerpt } from '../logging/events-writer.js'`). New\n * code should import from `@algosuite/vo-arch-defaults` directly.\n */\nexport { sanitizeExcerpt, SANITIZE_DEFAULT_MAX_LEN } from '@algosuite/vo-arch-defaults';\n", "/**\n * Zod schema for v1 architectural-default rules.\n *\n * Schema v1 is LOCKED per handoff \u00A75 \u2014 DO NOT change field names or shape.\n * Add a sibling `rule-v2.ts` and bump `schema_version` if the contract needs\n * to evolve; the version registry in `./index.ts` dispatches by version.\n *\n * Why we still validate at load time even though TS gives us static types:\n * - Corpus is JSON on disk \u2192 no compile-time guarantee\n * - Tenants edit `vo-arch-defaults.local.json` by hand \u2192 drift is expected\n * - A malformed rule that loaded silently would fire (or not) in opaque\n * ways; failing loud at load is the only honest behavior\n */\nimport { z } from 'zod';\n\nconst EvidenceMatcherKind = z.enum([\n 'regex',\n 'import-detector',\n 'package-json-field',\n 'file-size',\n 'ast-pattern',\n 'custom',\n]);\n\nconst EvidenceMatcher = z\n .object({\n kind: EvidenceMatcherKind,\n pattern: z.string().optional(),\n config: z.record(z.string(), z.unknown()).optional(),\n description: z.string().min(1),\n })\n .strict()\n .refine(\n (m) => m.kind !== 'regex' || (typeof m.pattern === 'string' && m.pattern.length > 0),\n { message: 'regex matcher requires non-empty pattern' },\n );\n\nconst ChangeType = z.enum(['new-file', 'edit', 'delete', 'rename', 'any']);\n\nconst AppliesWhen = z\n .object({\n stack: z.array(z.string().min(1)).optional(),\n change_types: z.array(ChangeType).optional(),\n file_globs: z.array(z.string().min(1)).optional(),\n not_file_globs: z.array(z.string().min(1)).optional(),\n })\n .strict();\n\nconst Reference = z\n .object({\n type: z.enum(['framework-doc', 'internal-doc', 'pr', 'incident', 'external']),\n url: z.string().min(1),\n description: z.string().min(1),\n })\n .strict();\n\n/** ISO date YYYY-MM-DD; rejects 2026-13-99 etc. via parseable check. */\nconst IsoDate = z\n .string()\n .regex(/^\\d{4}-\\d{2}-\\d{2}$/, 'last_verified must be ISO date YYYY-MM-DD')\n .refine((s) => {\n const d = new Date(s + 'T00:00:00Z');\n return !Number.isNaN(d.getTime()) && d.toISOString().startsWith(s);\n }, 'last_verified must be a real calendar date');\n\n/** Rule ID convention: `<category>/<kebab-name>`. */\nconst RuleId = z.string().regex(/^[a-z][a-z0-9-]*\\/[a-z][a-z0-9-]*$/, {\n message: 'rule_id must be `<category>/<kebab-name>`',\n});\n\nexport const ArchitecturalDefaultRuleSchema = z\n .object({\n schema_version: z.literal(1),\n rule_id: RuleId,\n rule_version: z.number().int().positive(),\n category: z.string().min(1),\n severity: z.enum(['blocker', 'warning', 'info']),\n title: z.string().min(1),\n rationale: z.string().min(1),\n applies_when: AppliesWhen,\n evidence_of_violation: z.array(EvidenceMatcher).min(1, {\n message: 'rule must declare at least one evidence matcher',\n }),\n remediation: z.string().min(1),\n references: z.array(Reference),\n last_verified: IsoDate,\n tags: z.array(z.string().min(1)),\n })\n .strict();\n\nexport type ArchitecturalDefaultRuleParsed = z.infer<\n typeof ArchitecturalDefaultRuleSchema\n>;\n\nexport function parseRule(input: unknown): ArchitecturalDefaultRuleParsed {\n return ArchitecturalDefaultRuleSchema.parse(input);\n}\n\nexport function safeParseRule(input: unknown): {\n ok: true;\n rule: ArchitecturalDefaultRuleParsed;\n} | {\n ok: false;\n error: string;\n} {\n const r = ArchitecturalDefaultRuleSchema.safeParse(input);\n if (r.success) return { ok: true, rule: r.data };\n return { ok: false, error: r.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ') };\n}\n", "/**\n * Zod schema for v1 per-tenant overrides.\n *\n * Per handoff \u00A75: tenant override has THREE knobs:\n * - `suppressed_rule_ids`: disable a bundled rule wholesale\n * - `modified_rules`: partial fields override (e.g. lower severity)\n * - `added_rules`: tenant-specific rules layered on top\n *\n * `added_rules` are full v1 rules \u2192 reuse the rule schema.\n * `modified_rules` are PARTIAL \u2192 use `.deepPartial()` to allow any subset.\n *\n * Schema v1 is LOCKED per handoff \u00A75.\n */\nimport { z } from 'zod';\nimport { ArchitecturalDefaultRuleSchema } from './rule-v1.js';\n\nconst PartialRuleSchema = ArchitecturalDefaultRuleSchema.partial();\n\nexport const TenantOverrideSchema = z\n .object({\n schema_version: z.literal(1),\n suppressed_rule_ids: z.array(z.string().min(1)),\n modified_rules: z.record(z.string().min(1), PartialRuleSchema),\n added_rules: z.array(ArchitecturalDefaultRuleSchema),\n /**\n * Per-tenant stack override (added 2026-05-24 \u2014 audit HIGH\n * \"DEFAULT_STACK hardcoded for Nexus repo\"). When set, downstream\n * consumers (vo-mcp KB pre-filter, vo-arch-check CLI) use this list\n * instead of their built-in default. Lets external tenants whose\n * repo isn't `firebase+react+pnpm` get useful KB rule matches by\n * dropping a `~/.claude/vo-arch-defaults.local.json` with their own\n * stack identifiers \u2014 no code change required.\n *\n * Open string union \u2014 values are not constrained beyond non-empty\n * strings so out-of-tree tenants can declare their own\n * (e.g. `vercel-edge`, `next-15`, `drizzle-postgres`).\n *\n * Optional. Undefined / omitted preserves pre-2026-05-24 behavior\n * (consumer falls back to its hardcoded default stack).\n */\n tenant_stack: z.array(z.string().min(1)).optional(),\n })\n .strict();\n\nexport type TenantOverrideParsed = z.infer<typeof TenantOverrideSchema>;\n\nexport function parseOverride(input: unknown): TenantOverrideParsed {\n return TenantOverrideSchema.parse(input);\n}\n\nexport function safeParseOverride(input: unknown): {\n ok: true;\n override: TenantOverrideParsed;\n} | {\n ok: false;\n error: string;\n} {\n const r = TenantOverrideSchema.safeParse(input);\n if (r.success) return { ok: true, override: r.data };\n return { ok: false, error: r.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ') };\n}\n", "/**\n * Bundled-corpus loader.\n *\n * Walks the package's `corpus/` directory (at runtime: `dist/corpus/` \u2014 the\n * build step in `scripts/copy-corpus.mjs` mirrors the source tree). Every\n * `.json` file is parsed + validated against the v1 schema. A malformed\n * rule is a HARD failure with the offending file path \u2014 we never silently\n * drop a rule (handoff \u00A7F honesty rule).\n *\n * The loader is synchronous because:\n * - corpus is small (~30 files at v1)\n * - called once at MCP server startup / CLI run\n * - async would force every consumer to await on a path lookup\n */\nimport { readdirSync, readFileSync, statSync, existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { parseRule, type ArchitecturalDefaultRuleParsed } from '../schema/index.js';\n\n/**\n * Resolve the bundled corpus directory.\n *\n * Production path: `dist/corpus/` (sibling of the compiled JS). Tests can\n * pass an explicit `corpusDir` override.\n */\nexport function resolveBundledCorpusDir(): string {\n // import.meta.url points at `dist/storage/load-bundled.js` after build,\n // or `src/storage/load-bundled.ts` under ts-node / vitest. Walk one level\n // up to find `corpus/`. Try both layouts.\n const here = dirname(fileURLToPath(import.meta.url));\n const candidates = [\n join(here, '..', 'corpus'), // dist/corpus next to dist/storage\n join(here, '..', '..', 'corpus'), // src/corpus next to src/storage (under vitest)\n ];\n for (const c of candidates) {\n if (existsSync(c) && statSync(c).isDirectory()) return c;\n }\n throw new Error(\n `vo-arch-defaults: could not locate bundled corpus directory. Tried: ${candidates.join(', ')}`,\n );\n}\n\n/** Recursive walk of all `*.json` files under `dir`. */\nfunction walkJson(dir: string, out: string[]): void {\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry);\n const st = statSync(full);\n if (st.isDirectory()) {\n walkJson(full, out);\n } else if (entry.endsWith('.json')) {\n out.push(full);\n }\n }\n}\n\nexport interface LoadBundledOptions {\n /** Override the corpus directory (tests pass fixture paths). */\n readonly corpusDir?: string;\n}\n\nexport interface LoadBundledResult {\n readonly rules: ReadonlyArray<ArchitecturalDefaultRuleParsed>;\n readonly source_paths: ReadonlyArray<string>;\n}\n\n/**\n * Load + validate every rule under the corpus directory.\n *\n * Throws on the FIRST malformed rule with a path-prefixed error message\n * so the operator can fix it quickly. There is no \"best effort\" mode \u2014\n * a silently dropped rule is a rule that doesn't fire, which is exactly\n * the failure mode this whole gate exists to prevent.\n */\nexport function loadBundledCorpus(opts: LoadBundledOptions = {}): LoadBundledResult {\n const dir = opts.corpusDir ?? resolveBundledCorpusDir();\n const files: string[] = [];\n walkJson(dir, files);\n files.sort(); // deterministic order \u2014 helps tests and CLI output\n\n const rules: ArchitecturalDefaultRuleParsed[] = [];\n const seenIds = new Set<string>();\n for (const file of files) {\n const raw = readFileSync(file, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n throw new Error(`vo-arch-defaults: invalid JSON in ${file}: ${m}`, { cause: err });\n }\n try {\n const rule = parseRule(parsed);\n if (seenIds.has(rule.rule_id)) {\n throw new Error(\n `vo-arch-defaults: duplicate rule_id '${rule.rule_id}' (second occurrence in ${file})`,\n );\n }\n seenIds.add(rule.rule_id);\n rules.push(rule);\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n throw new Error(`vo-arch-defaults: schema validation failed for ${file}: ${m}`, { cause: err });\n }\n }\n return { rules, source_paths: files };\n}\n", "/**\n * Per-tenant override loader.\n *\n * Reads a single JSON file (default: `~/.claude/vo-arch-defaults.local.json`,\n * or the path passed in). Validates against v1 override schema.\n *\n * Returns `null` when the path doesn't exist \u2014 overrides are OPT-IN.\n * Hard-throws on present-but-malformed (honesty rule: a malformed override\n * silently dropped would re-enable rules the operator thought were\n * suppressed).\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { parseOverride, type TenantOverrideParsed } from '../schema/index.js';\n\n/** Default override location. Operator-facing: `~/.claude/vo-arch-defaults.local.json`. */\nexport function defaultOverridePath(): string {\n return join(homedir(), '.claude', 'vo-arch-defaults.local.json');\n}\n\nexport interface LoadOverrideOptions {\n readonly path?: string;\n}\n\nexport interface LoadOverrideResult {\n readonly override: TenantOverrideParsed | null;\n readonly source_path: string | null;\n}\n\nexport function loadTenantOverride(\n opts: LoadOverrideOptions = {},\n): LoadOverrideResult {\n const path = opts.path ?? defaultOverridePath();\n if (!existsSync(path)) {\n return { override: null, source_path: null };\n }\n const raw = readFileSync(path, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n throw new Error(`vo-arch-defaults: invalid JSON in override ${path}: ${m}`, { cause: err });\n }\n try {\n const override = parseOverride(parsed);\n return { override, source_path: path };\n } catch (err) {\n const m = err instanceof Error ? err.message : String(err);\n throw new Error(`vo-arch-defaults: override schema validation failed for ${path}: ${m}`, { cause: err });\n }\n}\n", "/**\n * Deep-merge bundled corpus + tenant override.\n *\n * Order of operations (handoff \u00A75):\n * 1. Start with bundled rules.\n * 2. Drop any rule whose ID is in `suppressed_rule_ids`.\n * 3. For each `modified_rules[id]` partial, shallow-merge fields on top\n * of the bundled rule (excluding `rule_id` / `schema_version` \u2014 those\n * are identity, not configuration).\n * 4. Append `added_rules`. If an added rule's ID collides with a\n * bundled rule that wasn't already modified/suppressed, the\n * added_rule wins (override semantics).\n *\n * The merge is intentionally shallow on each rule object \u2014 nested fields\n * (`applies_when`, `evidence_of_violation`, etc.) are REPLACED wholesale\n * if the override sets them. Tenants who want to \"add another stack\" to\n * `applies_when.stack` must repeat the whole array. This is simpler and\n * less surprising than a recursive deep-merge of arrays.\n */\nimport type {\n ArchitecturalDefaultRule,\n TenantOverride,\n} from '../types.js';\n\nexport interface MergeResult {\n readonly rules: ReadonlyArray<ArchitecturalDefaultRule>;\n readonly suppressed_count: number;\n readonly modified_count: number;\n readonly added_count: number;\n}\n\nconst IMMUTABLE_FIELDS: ReadonlyArray<keyof ArchitecturalDefaultRule> = [\n 'schema_version',\n 'rule_id',\n];\n\nfunction applyModification(\n base: ArchitecturalDefaultRule,\n patch: Partial<ArchitecturalDefaultRule>,\n): ArchitecturalDefaultRule {\n // Strip immutable identity fields from the patch.\n const cleanPatch: Partial<ArchitecturalDefaultRule> = { ...patch };\n for (const k of IMMUTABLE_FIELDS) {\n delete (cleanPatch as Record<string, unknown>)[k];\n }\n return { ...base, ...cleanPatch };\n}\n\nexport function mergeCorpusWithOverride(\n bundled: ReadonlyArray<ArchitecturalDefaultRule>,\n override: TenantOverride | null,\n): MergeResult {\n if (override === null) {\n return {\n rules: bundled,\n suppressed_count: 0,\n modified_count: 0,\n added_count: 0,\n };\n }\n\n const suppressed = new Set(override.suppressed_rule_ids);\n let suppressedCount = 0;\n let modifiedCount = 0;\n\n const afterFilter: ArchitecturalDefaultRule[] = [];\n for (const rule of bundled) {\n if (suppressed.has(rule.rule_id)) {\n suppressedCount += 1;\n continue;\n }\n const patch = override.modified_rules[rule.rule_id];\n if (patch !== undefined) {\n afterFilter.push(applyModification(rule, patch));\n modifiedCount += 1;\n } else {\n afterFilter.push(rule);\n }\n }\n\n // Append added_rules. If an ID already exists (only possible when the\n // tenant added a rule that ISN'T in the bundled corpus and ISN'T\n // suppressed \u2014 i.e. brand-new), it's appended. If the ID DOES collide\n // with a still-present bundled rule, the added_rule replaces the\n // bundled one (consistent with override-semantics).\n const byId = new Map<string, ArchitecturalDefaultRule>(\n afterFilter.map((r) => [r.rule_id, r]),\n );\n let addedCount = 0;\n for (const rule of override.added_rules) {\n if (suppressed.has(rule.rule_id)) {\n // Operator suppressed it AND added it \u2014 suppression wins. Honest\n // behavior: prefer the more restrictive setting.\n continue;\n }\n byId.set(rule.rule_id, rule);\n addedCount += 1;\n }\n\n return {\n rules: Array.from(byId.values()),\n suppressed_count: suppressedCount,\n modified_count: modifiedCount,\n added_count: addedCount,\n };\n}\n", "/**\n * Stack-applicability filter.\n *\n * Rule applies when:\n * - rule.applies_when.stack is undefined or empty \u2192 stack-agnostic, always TRUE\n * - otherwise \u2192 at least one stack in the caller's `stacks` is in the rule's set\n */\nimport type { ArchitecturalDefaultRule, Stack } from '../types.js';\n\nexport function appliesToStack(\n rule: ArchitecturalDefaultRule,\n stacks: ReadonlyArray<Stack>,\n): boolean {\n const required = rule.applies_when.stack;\n if (!required || required.length === 0) return true;\n if (stacks.length === 0) return false;\n const want = new Set(required);\n for (const s of stacks) {\n if (want.has(s)) return true;\n }\n return false;\n}\n", "/**\n * Change-type-applicability filter.\n *\n * Rule applies when:\n * - rule.applies_when.change_types is undefined or empty \u2192 applies to all\n * - rule.applies_when.change_types contains 'any' \u2192 applies to all\n * - rule.applies_when.change_types contains the caller's `change_type` \u2192 TRUE\n * - otherwise FALSE\n *\n * Caller passes a single change_type (the kind of edit being reviewed).\n * 'any' on the rule side means \"fires regardless of change shape\" \u2014 for\n * caller-side 'any', we expand to ALL kinds.\n */\nimport type { ArchitecturalDefaultRule, ChangeType } from '../types.js';\n\nexport function appliesToChangeType(\n rule: ArchitecturalDefaultRule,\n changeType: ChangeType,\n): boolean {\n const required = rule.applies_when.change_types;\n if (!required || required.length === 0) return true;\n if (required.includes('any')) return true;\n if (changeType === 'any') return true;\n return required.includes(changeType);\n}\n", "/**\n * Minimal glob \u2192 RegExp.\n *\n * We intentionally do NOT depend on `picomatch` / `minimatch` here:\n * - the rule corpus uses a small subset (`**` / `*` / brace alternations)\n * - depending on a glob library widens the supply-chain attack surface\n * for a package whose whole job is \"say no\"\n * - the open shell is meant to be re-implementable by other clients;\n * a hand-rolled converter is portable\n *\n * Supported syntax:\n * - `**` matches any number of path segments (including zero)\n * - `*` matches any chars except `/`\n * - `?` matches any single char except `/`\n * - `{a,b}` matches any literal option (added PR-R 2026-05-25). Options\n * are LITERAL strings \u2014 no globs / nested braces inside.\n * Example: `**\\/*.{ts,tsx}` matches `src/foo.ts` and `src/foo.tsx`.\n * Empty `{}` is treated as a literal nothing.\n * Unmatched `{` is treated as a literal `{`.\n * - everything else is a literal (regex metacharacters escaped)\n *\n * NOT supported (deliberately): `[abc]` char classes, `!negation`,\n * nested braces `{a,{b,c}}`, glob expressions inside braces `{*.ts,foo}`.\n * Keep the surface small. If a rule needs more, declare multiple file_globs.\n */\n\nconst REGEX_META = /[.+^${}()|[\\]\\\\]/g;\n\nexport function globToRegExp(glob: string): RegExp {\n let out = '';\n let i = 0;\n while (i < glob.length) {\n const c = glob[i] ?? '';\n if (c === '*') {\n const next = glob[i + 1];\n if (next === '*') {\n // ** \u2014 match across segments\n // Consume an optional trailing `/`\n const after = glob[i + 2];\n if (after === '/') {\n out += '(?:.*/)?';\n i += 3;\n } else {\n out += '.*';\n i += 2;\n }\n } else {\n out += '[^/]*';\n i += 1;\n }\n } else if (c === '?') {\n out += '[^/]';\n i += 1;\n } else if (c === '{') {\n // Brace expansion (PR-R 2026-05-25). Find matching `}`; split inner on\n // unescaped commas; each option is a LITERAL string (regex-escaped).\n // No nested-brace / no glob-in-options support \u2014 keeps the implementation\n // tiny and the contract obvious. If `{` is unmatched, fall through as\n // literal.\n const closeIdx = glob.indexOf('}', i + 1);\n if (closeIdx < 0) {\n // Unmatched `{` \u2014 treat as literal so the caller sees a regex that\n // matches the original glob string itself (defensive: don't silently\n // accept malformed input).\n out += '\\\\{';\n i += 1;\n } else {\n const inner = glob.slice(i + 1, closeIdx);\n // Empty `{}` \u2014 emit nothing, consume to past `}`. Matches the glob\n // with the braces dropped entirely.\n if (inner.length === 0) {\n i = closeIdx + 1;\n } else {\n const opts = inner.split(',');\n const escapedOpts = opts.map((o) => o.replace(REGEX_META, '\\\\$&'));\n out += '(?:' + escapedOpts.join('|') + ')';\n i = closeIdx + 1;\n }\n }\n } else {\n out += c.replace(REGEX_META, '\\\\$&');\n i += 1;\n }\n }\n return new RegExp('^' + out + '$');\n}\n\nexport function matchesAnyGlob(\n path: string,\n globs: ReadonlyArray<string>,\n): boolean {\n for (const g of globs) {\n if (globToRegExp(g).test(path)) return true;\n }\n return false;\n}\n", "/**\n * File-path-applicability filter.\n *\n * Rule applies when:\n * - applies_when.file_globs is undefined/empty AND not_file_globs is\n * undefined/empty \u2192 file-path-agnostic, TRUE\n * - otherwise \u2192 AT LEAST ONE caller `file_paths` entry must satisfy BOTH\n * (a) include (matches a file_globs entry, OR rule declares no include)\n * (b) NOT excluded (doesn't match any not_file_globs entry)\n *\n * If the caller provides zero file_paths AND the rule declares file_globs,\n * the rule doesn't apply (no surface to fire on).\n *\n * ## Per-file semantics (bug fix 2026-05-25 \u2014 see PR-P)\n *\n * Pre-fix behavior was a wholesale-drop on any exclude match: if ANY file in\n * the diff matched `not_file_globs`, the rule was dropped from the entire\n * PR \u2014 even when other files in the diff matched `file_globs` and weren't\n * excluded. This failed open on the most common security-rule pattern:\n *\n * rule `security/no-hardcoded-secrets`:\n * file_globs: [\"**\\/*.ts\", \"**\\/*.tsx\", \"**\\/*.js\", \"**\\/*.mjs\", ...]\n * not_file_globs: [\"**\\/*.test.ts\", \"**\\/fixtures/**\", \"**\\/__mocks__/**\"]\n *\n * Failure case under the old semantics: a PR adds a hardcoded `sk_live_...`\n * key to `src/foo.ts` AND adds `src/foo.test.ts` to test the new code. The\n * test file matched the exclude, the entire rule was dropped, and the\n * blocker-severity secret-detection rule never fired on `foo.ts`. PR ships\n * with a leaked secret.\n *\n * The exclude list semantically means \"don't SCAN these files\" (per-file\n * scope), not \"skip the entire rule if any of these files are touched\"\n * (whole-rule scope). This implementation matches that intent.\n *\n * The rule-level applies_when filter answers \"does this rule have ANY\n * surface to fire on in this diff?\" \u2014 true if at least one included,\n * non-excluded file exists. The per-file evidence-matcher loop in\n * `run-query.ts` still re-runs `appliesToFiles(rule, [file.path])` per\n * file so evidence only fires on the right files. Single-path semantics\n * are unchanged: one file that's both included and not excluded \u2192 true;\n * one file that's excluded \u2192 false. Same as before.\n */\nimport type { ArchitecturalDefaultRule } from '../types.js';\nimport { matchesAnyGlob } from './glob.js';\n\nexport function appliesToFiles(\n rule: ArchitecturalDefaultRule,\n filePaths: ReadonlyArray<string>,\n): boolean {\n const include = rule.applies_when.file_globs;\n const exclude = rule.applies_when.not_file_globs;\n const noInclude = !include || include.length === 0;\n const noExclude = !exclude || exclude.length === 0;\n if (noInclude && noExclude) return true;\n if (filePaths.length === 0) {\n // No paths to evaluate against. If the rule narrows by include glob,\n // it can't apply. If only exclude is declared, default-true.\n return noInclude;\n }\n // Per-file: rule applies if AT LEAST ONE caller file is both included\n // (or no include declared) AND not excluded.\n for (const p of filePaths) {\n const isIncluded = noInclude || (include !== undefined && matchesAnyGlob(p, include));\n if (!isIncluded) continue;\n const isExcluded =\n !noExclude && exclude !== undefined && matchesAnyGlob(p, exclude);\n if (isExcluded) continue;\n return true;\n }\n return false;\n}\n", "/**\n * Minimal unified-diff parser.\n *\n * We need just enough structure to:\n * - know which files the diff touches (for `applies_when.file_globs`)\n * - know whether each file is new / deleted / modified / renamed\n * (for `applies_when.change_types`)\n * - feed regex matchers per-file's added lines (so we don't match an\n * UNCHANGED Gen1 import that's only in the file for context)\n *\n * Intentionally hand-rolled \u2014 see `query/glob.ts` for why we avoid pulling\n * in a `parse-diff` dep. Format reference: `git diff --no-color` unified.\n */\n\nexport type ParsedChangeType = 'new-file' | 'edit' | 'delete' | 'rename' | 'any';\n\nexport interface ParsedFile {\n readonly path: string;\n readonly previous_path: string | null;\n readonly change_type: ParsedChangeType;\n /**\n * Added lines (after-state). Each entry preserves the 1-based line\n * number on the new file. Removed lines / context lines are NOT included\n * \u2014 evidence matchers should fire only on what the diff is ADDING.\n */\n readonly added_lines: ReadonlyArray<{ line: number; text: string }>;\n}\n\nexport interface ParsedDiff {\n readonly files: ReadonlyArray<ParsedFile>;\n}\n\ninterface MutFile {\n path: string;\n previous_path: string | null;\n change_type: ParsedChangeType;\n added_lines: { line: number; text: string }[];\n}\n\n/** Strip the `a/`/`b/` git-diff prefix when present. */\nfunction stripPathPrefix(p: string): string {\n if (p.startsWith('a/') || p.startsWith('b/')) return p.slice(2);\n return p;\n}\n\nexport function parseUnifiedDiff(text: string): ParsedDiff {\n const lines = text.split(/\\r?\\n/);\n const files: MutFile[] = [];\n let current: MutFile | null = null;\n let newLine = 0;\n\n for (let i = 0; i < lines.length; i++) {\n const raw = lines[i] ?? '';\n if (raw.startsWith('diff --git ')) {\n // diff --git a/foo b/foo\n const m = /^diff --git (\\S+) (\\S+)$/.exec(raw);\n if (m && m[1] && m[2]) {\n current = {\n path: stripPathPrefix(m[2]),\n previous_path: stripPathPrefix(m[1]),\n change_type: 'edit', // refined as we see new file mode / deleted file mode / rename\n added_lines: [],\n };\n if (current.previous_path === current.path) current.previous_path = null;\n files.push(current);\n }\n newLine = 0;\n continue;\n }\n if (!current) continue;\n if (raw.startsWith('new file mode ')) {\n current.change_type = 'new-file';\n continue;\n }\n if (raw.startsWith('deleted file mode ')) {\n current.change_type = 'delete';\n continue;\n }\n if (raw.startsWith('rename from ')) {\n current.previous_path = raw.slice('rename from '.length).trim();\n current.change_type = 'rename';\n continue;\n }\n if (raw.startsWith('rename to ')) {\n current.path = raw.slice('rename to '.length).trim();\n current.change_type = 'rename';\n continue;\n }\n // hunk header: @@ -a,b +c,d @@\n const hunk = /^@@ -\\d+(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/.exec(raw);\n if (hunk && hunk[1]) {\n newLine = parseInt(hunk[1], 10);\n continue;\n }\n if (raw.startsWith('+++') || raw.startsWith('---')) {\n // file header \u2014 already captured from `diff --git`\n continue;\n }\n if (raw.startsWith('+') && !raw.startsWith('+++')) {\n current.added_lines.push({ line: newLine, text: raw.slice(1) });\n newLine += 1;\n continue;\n }\n if (raw.startsWith('-') && !raw.startsWith('---')) {\n // removal \u2014 does not advance new-file line counter\n continue;\n }\n if (raw.startsWith('\\\\')) {\n // \"\\"\n continue;\n }\n // context line: advances new-file line counter\n if (newLine > 0) newLine += 1;\n }\n return { files };\n}\n", "/**\n * Regex matcher \u2014 runs `pattern` against every added line of every\n * applicable file. Per-file results carry the 1-based new-file line number.\n *\n * Per handoff \u00A7F: the matcher MUST be honest about what it can and can't\n * detect. We compile with `g` so all matches are surfaced; if a rule's\n * regex is too broad and false-positives, the rule's authors are expected\n * to narrow it.\n */\nimport type { ParsedFile } from '../diff-parser.js';\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\n\nconst MAX_EXCERPT = 200;\n\nfunction sanitizeLine(s: string): string {\n // Lightweight excerpt cleanup \u2014 strip leading whitespace, cap length.\n let out = s.replace(/^\\s+/, '');\n if (out.length > MAX_EXCERPT) out = out.slice(0, MAX_EXCERPT) + '\u2026';\n return out;\n}\n\nexport function runRegexMatcher(\n matcher: EvidenceMatcher,\n file: ParsedFile,\n): ReadonlyArray<EvidenceHit> {\n if (matcher.kind !== 'regex' || !matcher.pattern) return [];\n let re: RegExp;\n try {\n re = new RegExp(matcher.pattern);\n } catch {\n return [];\n }\n const hits: EvidenceHit[] = [];\n for (const { line, text } of file.added_lines) {\n if (re.test(text)) {\n hits.push({\n matcher_kind: 'regex',\n matcher_description: matcher.description,\n file: file.path,\n line,\n excerpt: sanitizeLine(text),\n });\n }\n }\n return hits;\n}\n", "/**\n * Import-detector matcher.\n *\n * `config.from` is the bare module specifier (or regex) the rule is\n * concerned with \u2014 e.g. `firebase-functions/v1`, `firebase-functions`,\n * or a regex like `^firebase-functions(/v1)?$`.\n *\n * `config.match` controls whether to fire on:\n * - 'exact' \u2014 only literal `from '<module>'`\n * - 'prefix' \u2014 fires on `from '<module>/...'` and the bare spec\n * - 'regex' \u2014 `config.from` is interpreted as a regex\n *\n * Fires on `import ... from '<module>'` and `import('<module>')` patterns\n * in added lines.\n */\nimport type { ParsedFile } from '../diff-parser.js';\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\n\n// CodeQL #198: original `[^'\"]*?from\\s+` is polynomial \u2014 the lazy `*?`\n// backtracks against the trailing `from\\s+` on inputs where no `from`\n// follows. The replacement bounds the wildcard with `{0,200}?` (real\n// import statements stay well under 200 chars between `import` and `from`)\n// AND excludes `\\n` so a `[^'\"]*?` cross-line match is impossible. This\n// keeps every existing test case green (verified by import-detector.test.ts)\n// while running in linear time on adversarial input.\nconst STATIC_IMPORT = /import\\s+(?:[^'\"\\n]{0,200}?from\\s+)?(['\"])([^'\"]+)\\1/;\nconst DYNAMIC_IMPORT = /import\\(\\s*(['\"])([^'\"]+)\\1\\s*\\)/;\nconst REQUIRE_CALL = /require\\(\\s*(['\"])([^'\"]+)\\1\\s*\\)/;\n\nconst MAX_EXCERPT = 200;\nfunction sanitize(s: string): string {\n let out = s.replace(/^\\s+/, '');\n if (out.length > MAX_EXCERPT) out = out.slice(0, MAX_EXCERPT) + '\u2026';\n return out;\n}\n\nfunction tryExtractSpec(line: string): string | null {\n const m = STATIC_IMPORT.exec(line) ?? DYNAMIC_IMPORT.exec(line) ?? REQUIRE_CALL.exec(line);\n if (!m || !m[2]) return null;\n return m[2];\n}\n\nfunction specMatches(spec: string, matcher: EvidenceMatcher): boolean {\n const cfg = (matcher.config ?? {}) as { from?: unknown; match?: unknown };\n const from = typeof cfg.from === 'string' ? cfg.from : null;\n const mode = typeof cfg.match === 'string' ? cfg.match : 'exact';\n if (from === null) return false;\n if (mode === 'regex') {\n try {\n return new RegExp(from).test(spec);\n } catch {\n return false;\n }\n }\n if (mode === 'prefix') {\n return spec === from || spec.startsWith(from + '/');\n }\n // exact\n return spec === from;\n}\n\nexport function runImportDetector(\n matcher: EvidenceMatcher,\n file: ParsedFile,\n): ReadonlyArray<EvidenceHit> {\n if (matcher.kind !== 'import-detector') return [];\n const hits: EvidenceHit[] = [];\n for (const { line, text } of file.added_lines) {\n const spec = tryExtractSpec(text);\n if (spec === null) continue;\n if (!specMatches(spec, matcher)) continue;\n hits.push({\n matcher_kind: 'import-detector',\n matcher_description: matcher.description,\n file: file.path,\n line,\n excerpt: sanitize(text),\n });\n }\n return hits;\n}\n", "/**\n * File-size matcher.\n *\n * `config.max_lines` \u2014 the cap; rule fires when the file (per the diff)\n * adds enough lines to PROBABLY exceed this. We can't observe the full\n * post-state from a diff alone (we don't have the unchanged context\n * line count), so this matcher fires when the diff ADDS more than\n * `config.max_added_lines` lines (default = `max_lines`) \u2014 i.e. \"this\n * single change is bigger than the whole file is supposed to be\".\n *\n * Honesty note (handoff \u00A7F): this matcher is heuristic. A truly\n * accurate check would require the full pre-state line count, which is\n * out of scope for a diff-only consumer. The rule rationale should\n * document this limitation; the CLI prints `(heuristic)` next to such\n * hits.\n */\nimport type { ParsedFile } from '../diff-parser.js';\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\n\nexport function runFileSizeMatcher(\n matcher: EvidenceMatcher,\n file: ParsedFile,\n): ReadonlyArray<EvidenceHit> {\n if (matcher.kind !== 'file-size') return [];\n const cfg = (matcher.config ?? {}) as {\n max_lines?: unknown;\n max_added_lines?: unknown;\n };\n const maxLines = typeof cfg.max_lines === 'number' ? cfg.max_lines : null;\n const explicitMaxAdded =\n typeof cfg.max_added_lines === 'number' ? cfg.max_added_lines : null;\n const cap = explicitMaxAdded ?? maxLines;\n if (cap === null || cap <= 0) return [];\n if (file.added_lines.length <= cap) return [];\n const sample = file.added_lines[0]?.text ?? '';\n return [\n {\n matcher_kind: 'file-size',\n matcher_description: `${matcher.description} (heuristic: ${file.added_lines.length} added lines > cap ${cap})`,\n file: file.path,\n excerpt: sample.slice(0, 100),\n },\n ];\n}\n", "/**\n * Package-json-field matcher.\n *\n * Fires when ANY added line in a `package.json` change contains a\n * forbidden value for a given field. Limited heuristic \u2014 we don't try\n * to parse incomplete JSON hunks; we look for the field-value pair\n * appearing as a literal JSON key/value on an added line.\n *\n * `config.field` \u2014 required, the JSON key name (e.g. 'packageManager').\n * `config.forbidden_prefix` \u2014 fires when value starts with this string.\n * `config.forbidden_exact` \u2014 fires when value equals this string.\n * `config.forbidden_regex` \u2014 fires when value matches this regex.\n *\n * Only one of the forbidden_* checks needs to be configured. We DO NOT\n * fire on the full file content because diff hunks are partial \u2014 false\n * positives on shared keys would be common. The honesty note in the\n * rule rationale should call out that this matcher requires the\n * change to actually touch the package.json field line.\n */\nimport type { ParsedFile } from '../diff-parser.js';\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\n\nfunction valueMatches(\n value: string,\n cfg: {\n forbidden_prefix?: unknown;\n forbidden_exact?: unknown;\n forbidden_regex?: unknown;\n },\n): boolean {\n if (typeof cfg.forbidden_prefix === 'string' && value.startsWith(cfg.forbidden_prefix)) {\n return true;\n }\n if (typeof cfg.forbidden_exact === 'string' && value === cfg.forbidden_exact) {\n return true;\n }\n if (typeof cfg.forbidden_regex === 'string') {\n try {\n if (new RegExp(cfg.forbidden_regex).test(value)) return true;\n } catch {\n /* invalid regex in config \u2014 silently skip */\n }\n }\n return false;\n}\n\nexport function runPackageJsonMatcher(\n matcher: EvidenceMatcher,\n file: ParsedFile,\n): ReadonlyArray<EvidenceHit> {\n if (matcher.kind !== 'package-json-field') return [];\n // Only fire on package.json files.\n if (!file.path.endsWith('package.json')) return [];\n const cfg = (matcher.config ?? {}) as {\n field?: unknown;\n forbidden_prefix?: unknown;\n forbidden_exact?: unknown;\n forbidden_regex?: unknown;\n };\n const field = typeof cfg.field === 'string' ? cfg.field : null;\n if (field === null) return [];\n const fieldRe = new RegExp(`\"${field}\"\\\\s*:\\\\s*\"([^\"]+)\"`);\n const hits: EvidenceHit[] = [];\n for (const { line, text } of file.added_lines) {\n const m = fieldRe.exec(text);\n if (!m || !m[1]) continue;\n if (valueMatches(m[1], cfg)) {\n hits.push({\n matcher_kind: 'package-json-field',\n matcher_description: matcher.description,\n file: file.path,\n line,\n excerpt: text.trim().slice(0, 200),\n });\n }\n }\n return hits;\n}\n", "/**\n * Evidence-matcher dispatch.\n *\n * Routes by `matcher.kind`. Unrecognized kinds (`ast-pattern`, `custom`)\n * return an empty hit list \u2014 honest about the limitation rather than\n * silently passing. Rules that depend on those kinds must declare it\n * in their rationale.\n */\nimport type { ParsedFile } from '../diff-parser.js';\nimport type { EvidenceHit, EvidenceMatcher } from '../../types.js';\nimport { runRegexMatcher } from './regex-matcher.js';\nimport { runImportDetector } from './import-detector.js';\nimport { runFileSizeMatcher } from './file-size.js';\nimport { runPackageJsonMatcher } from './package-json.js';\n\nexport function runEvidenceMatcher(\n matcher: EvidenceMatcher,\n file: ParsedFile,\n): ReadonlyArray<EvidenceHit> {\n switch (matcher.kind) {\n case 'regex':\n return runRegexMatcher(matcher, file);\n case 'import-detector':\n return runImportDetector(matcher, file);\n case 'file-size':\n return runFileSizeMatcher(matcher, file);\n case 'package-json-field':\n return runPackageJsonMatcher(matcher, file);\n case 'ast-pattern':\n case 'custom':\n // Documented limitation: v1 has no built-in AST runner. Rules of\n // these kinds rely on external tooling that calls the query API\n // and supplies its own evidence. We surface the rule as applicable\n // (via applies_when) without inventing fake hits.\n return [];\n default:\n return [];\n }\n}\n", "/**\n * `computeStaleRules` \u2014 flag rules whose `last_verified` date has aged out.\n *\n * Added 2026-05-24 (audit MEDIUM \"no signal that an architectural rule is\n * stale\"). The schema already requires every rule to carry an ISO\n * `last_verified` date; this helper turns that into an OBSERVATIONAL\n * signal callers can surface so operators know a rule needs review.\n *\n * Important semantics \u2014 DO NOT change without coordinator approval:\n *\n * - **Stale rules still fire.** A rule that hasn't been verified in 18\n * months is more likely to be wrong, not less likely to apply. We do\n * NOT drop stale rules from `result.applicable` \u2014 they go through the\n * normal stack/change/files filter pipeline like any other rule. The\n * staleness signal is a separate `result.stale_rules` array the\n * operator can act on independently (e.g. emit a stderr warning, or\n * nag during `vo-arch-check --staleness-fail`).\n *\n * - **Threshold default = 365 days.** Chosen because the v1 corpus was\n * last fully reviewed 2026-05-22, so a 365-day default gives operators\n * a year before the first \"review me\" signal fires. Caller can pass\n * `Infinity` to disable, or any other day count.\n *\n * - **`now` is injectable for tests.** Defaults to `new Date()` so\n * production code calls without ceremony.\n *\n * - **Pure function \u2014 no I/O.** `findApplicableRules` calls this with\n * the post-merge (post-suppression) rule set, so tenant-suppressed\n * rules never appear in `stale_rules`.\n *\n * - **Malformed `last_verified` is impossible at this layer** because\n * the Zod schema rejects anything that isn't a real ISO date at load\n * time. We still defensively skip un-parseable dates rather than\n * throw, so a future schema relaxation can't cascade into a runtime\n * crash inside the query path.\n */\nimport type { ArchitecturalDefaultRule } from '../types.js';\n\n/** Default staleness threshold. See \"Threshold default\" in the module docstring. */\nexport const DEFAULT_STALENESS_THRESHOLD_DAYS = 365;\n\nexport interface StaleRule {\n readonly rule_id: string;\n /** Original ISO date from the rule. */\n readonly last_verified: string;\n /** Calendar days between `last_verified` and `now`. Always > threshold. */\n readonly days_stale: number;\n}\n\nexport interface ComputeStaleRulesOptions {\n /** Days before a rule is considered stale. Default 365. Pass `Infinity` to disable. */\n readonly thresholdDays?: number;\n /** Reference date for the calculation. Default `new Date()`. Tests inject a fixed instant. */\n readonly now?: Date;\n}\n\n/**\n * Return the subset of `rules` whose `last_verified` is older than\n * `thresholdDays`. Empty array when nothing is stale, or when the\n * threshold is `Infinity`.\n *\n * Day-count math: `Math.floor((now - last_verified) / 86_400_000)`. This\n * is a calendar-day approximation, not a wall-clock calculation \u2014 DST\n * boundaries and leap seconds are ignored. Adequate for a >365-day signal.\n */\nexport function computeStaleRules(\n rules: ReadonlyArray<ArchitecturalDefaultRule>,\n opts: ComputeStaleRulesOptions = {},\n): ReadonlyArray<StaleRule> {\n const threshold = opts.thresholdDays ?? DEFAULT_STALENESS_THRESHOLD_DAYS;\n // `Infinity` (or any non-finite) \u2192 staleness disabled entirely.\n if (!Number.isFinite(threshold)) return [];\n // Negative or zero threshold \u2192 would mark everything stale. Treat as\n // \"disable\" rather than \"panic the operator with 36 entries on day 1.\"\n if (threshold <= 0) return [];\n\n const now = opts.now ?? new Date();\n const nowMs = now.getTime();\n if (!Number.isFinite(nowMs)) return [];\n\n const out: StaleRule[] = [];\n for (const rule of rules) {\n const verifiedMs = Date.parse(rule.last_verified + 'T00:00:00Z');\n if (!Number.isFinite(verifiedMs)) continue;\n const daysStale = Math.floor((nowMs - verifiedMs) / 86_400_000);\n if (daysStale > threshold) {\n out.push({\n rule_id: rule.rule_id,\n last_verified: rule.last_verified,\n days_stale: daysStale,\n });\n }\n }\n // Sort most-stale first so operators see worst offenders at the top.\n out.sort((a, b) => b.days_stale - a.days_stale);\n return out;\n}\n", "/**\n * Main query entry \u2014 `findApplicableRules`.\n *\n * Pipeline:\n * 1. Load bundled corpus.\n * 2. Load tenant override (if path provided / default exists).\n * 3. Merge \u2192 effective rule list.\n * 4. For each rule, apply stack / change_type / file_globs filters.\n * 5. If a diff_excerpt was provided, parse it and run evidence matchers\n * for each applicable rule \u00D7 touched file.\n * 6. Return `QueryResult` (the applicable rules + per-rule hits +\n * bookkeeping counts).\n *\n * Important honesty rule: a rule that is \"applicable\" but produced zero\n * evidence hits IS STILL SURFACED in the result \u2014 the senior-architect\n * panel may want to consider it. We never drop applicable rules just\n * because we couldn't detect evidence.\n */\nimport type {\n ArchitecturalDefaultRule,\n EvidenceHit,\n QueryHit,\n QueryInput,\n QueryResult,\n TenantOverride,\n} from '../types.js';\nimport { loadBundledCorpus } from '../storage/load-bundled.js';\nimport { loadTenantOverride } from '../storage/load-override.js';\nimport { mergeCorpusWithOverride } from '../storage/merge.js';\nimport { appliesToStack } from './applies-to-stack.js';\nimport { appliesToChangeType } from './applies-to-change.js';\nimport { appliesToFiles } from './applies-to-files.js';\nimport { parseUnifiedDiff } from '../analyze/diff-parser.js';\nimport { runEvidenceMatcher } from '../analyze/evidence-matchers/index.js';\nimport { computeStaleRules } from './staleness.js';\n\nexport interface FindApplicableRulesOptions {\n /** Use this in-memory rule list instead of loading from disk. Tests use this. */\n readonly rules?: ReadonlyArray<ArchitecturalDefaultRule>;\n /** Use this override directly instead of reading from disk. */\n readonly override?: TenantOverride | null;\n /** Override the corpus directory (passed to bundled loader). */\n readonly corpusDir?: string;\n /** Override file for the tenant override loader. */\n readonly tenantOverridePath?: string;\n /**\n * Staleness threshold in days for `result.stale_rules`. Default 365.\n * Pass `Infinity` to disable the staleness signal entirely. See\n * `src/query/staleness.ts` for full semantics. Added 2026-05-24.\n */\n readonly stalenessThresholdDays?: number;\n /** Reference date for the staleness calculation. Default `new Date()`. */\n readonly now?: Date;\n}\n\nexport function findApplicableRules(\n input: QueryInput,\n opts: FindApplicableRulesOptions = {},\n): QueryResult {\n // --- Load ---------------------------------------------------------\n const bundled =\n opts.rules !== undefined\n ? opts.rules\n : loadBundledCorpus({ corpusDir: opts.corpusDir }).rules;\n const override =\n opts.override !== undefined\n ? opts.override\n : loadTenantOverride({ path: opts.tenantOverridePath }).override;\n const merged = mergeCorpusWithOverride(bundled, override);\n\n // --- Optional diff parse for evidence -----------------------------\n const parsedDiff = input.diff_excerpt\n ? parseUnifiedDiff(input.diff_excerpt)\n : null;\n\n // --- Filter + collect --------------------------------------------\n // Pre-compute metadata filter Sets for O(1) lookup. `undefined` means\n // \"no filter at this level\" (current behavior preserved). Empty array\n // produces a Set of size 0 \u2014 matches nothing (deliberate narrow query).\n const categoryFilter =\n input.categories !== undefined ? new Set<string>(input.categories) : null;\n const tagAnyFilter = input.tags_any !== undefined ? new Set<string>(input.tags_any) : null;\n\n const applicable: QueryHit[] = [];\n for (const rule of merged.rules) {\n const stackOk = appliesToStack(rule, input.stack);\n const changeOk = appliesToChangeType(rule, input.change_type);\n const filesOk = appliesToFiles(rule, input.file_paths);\n if (!stackOk || !changeOk || !filesOk) continue;\n\n // Metadata filters (added 2026-05-24). Both run AFTER the stack/change/\n // files filters so the existing applicability semantics are unchanged\n // for callers that don't pass categories / tags_any.\n if (categoryFilter !== null && !categoryFilter.has(rule.category)) continue;\n if (tagAnyFilter !== null) {\n // Intersect: rule passes if ANY of its tags is in tag_any. Empty\n // tag_any \u2192 no intersection possible \u2192 rule filtered out.\n let tagHit = false;\n for (const t of rule.tags) {\n if (tagAnyFilter.has(t)) {\n tagHit = true;\n break;\n }\n }\n if (!tagHit) continue;\n }\n\n const hits: EvidenceHit[] = [];\n if (parsedDiff !== null) {\n for (const file of parsedDiff.files) {\n // Per-file applicability: a rule with file_globs only fires\n // evidence against files that match those globs.\n const filePathArr: ReadonlyArray<string> = [file.path];\n if (!appliesToFiles(rule, filePathArr)) continue;\n for (const matcher of rule.evidence_of_violation) {\n const fileHits = runEvidenceMatcher(matcher, file);\n for (const h of fileHits) hits.push(h);\n }\n }\n }\n applicable.push({\n rule,\n reason: {\n stack_matched: stackOk,\n change_type_matched: changeOk,\n file_glob_matched: filesOk,\n },\n evidence: hits,\n });\n }\n\n // Staleness signal \u2014 observational only, computed over the merged\n // (post-suppression) rule set so suppressed rules never show up. Default\n // threshold lives in `staleness.ts` (365 days). Caller can disable by\n // passing `Infinity`.\n const stale_rules = computeStaleRules(merged.rules, {\n ...(opts.stalenessThresholdDays !== undefined\n ? { thresholdDays: opts.stalenessThresholdDays }\n : {}),\n ...(opts.now !== undefined ? { now: opts.now } : {}),\n });\n\n return {\n applicable,\n considered_count: merged.rules.length,\n suppressed_count: merged.suppressed_count,\n stale_rules,\n };\n}\n", "/**\n * Compute a stable version hash of the loaded rule corpus.\n *\n * Consumers (notably the vo-mcp content-hash cache) mix this hash into the\n * cache-key namespace so that adding / modifying / removing a rule\n * invalidates any cached envelope whose verdict was computed without\n * knowledge of the changed rule. Pure rule_id+rule_version pairs (NOT\n * file contents) are hashed: whitespace edits to rule JSON don't bust\n * the cache; bumping a rule_version or adding a rule does.\n *\n * The hash is intentionally SHORT (16 hex chars) \u2014 embedded in a cache\n * key namespace string that's already opaque; full sha256 length adds\n * no real collision resistance for the universe of corpus snapshots.\n *\n * Pure function \u2014 no I/O. Caller supplies the already-loaded rules.\n */\nimport { createHash } from 'node:crypto';\nimport type { ArchitecturalDefaultRule } from '../types.js';\n\n/**\n * Hash the corpus version. Input is the rules array from `loadBundledCorpus`\n * (already validated). Order-insensitive: rules are sorted by rule_id before\n * hashing so two different file-system orderings of the same corpus yield\n * the same hash.\n *\n * Returns a 16-char lowercase hex string (first 64 bits of sha256). Stable\n * across processes + platforms.\n *\n * Empty rule list \u2192 returns the well-known zero-corpus hash (the sha256\n * of the empty string, first 16 chars). This avoids the namespace flipping\n * between \"no corpus\" and \"empty corpus\" representations.\n */\nexport function computeCorpusVersionHash(\n rules: ReadonlyArray<Pick<ArchitecturalDefaultRule, 'rule_id' | 'rule_version'>>,\n): string {\n // Sort by rule_id (stable, locale-independent string compare).\n const tuples = rules\n .map((r) => `${r.rule_id}@${r.rule_version}`)\n .sort();\n const hash = createHash('sha256');\n for (const tuple of tuples) {\n hash.update(tuple);\n hash.update('\\n'); // separator \u2014 prevents 'a@1' + 'b@2' colliding with 'a@1b@2'\n }\n return hash.digest('hex').slice(0, 16);\n}\n", "/**\n * `resolveStack` \u2014 pick the effective stack list for a KB query.\n *\n * Added 2026-05-24 for audit HIGH \"DEFAULT_STACK hardcoded for Nexus repo\".\n * Consumers (vo-mcp KB pre-filter, vo-arch-check CLI) call this once at\n * startup with the tenant override + their built-in fallback. When the\n * tenant has dropped a `~/.claude/vo-arch-defaults.local.json` with\n * `tenant_stack: [...]`, that list wins; otherwise the consumer's fallback\n * applies (preserves pre-2026-05-24 behavior for tenants that haven't\n * configured one).\n *\n * Pure function \u2014 no I/O. The override loader (`loadTenantOverride`) is\n * the caller's responsibility; this helper just picks between the two\n * resolved values. Empty `tenant_stack: []` is honored as-is (means\n * \"match only stack-agnostic rules\" \u2014 the consumer might want this).\n */\nimport type { Stack, TenantOverride } from '../types.js';\n\nexport function resolveStack(\n override: TenantOverride | null,\n fallback: ReadonlyArray<Stack>,\n): ReadonlyArray<Stack> {\n if (override !== null && override.tenant_stack !== undefined) {\n return override.tenant_stack;\n }\n return fallback;\n}\n", "/**\n * PII / secret scrubber for excerpts that ship to third-party model providers\n * or land in the on-disk audit log.\n *\n * **Location rationale (PR-U 2026-05-25).** This used to live in vo-mcp's\n * `logging/events-writer.ts`. It moved here so every KB consumer (current +\n * future) can sanitize evidence excerpts without taking a dependency on\n * vo-mcp. The KB pre-filter (`architecture-review-kb-prefilter.ts`,\n * `kb-metadata-prefilter.ts`) needs this BEFORE inlining matched-rule\n * evidence into a senior-architect prompt \u2014 `security/no-hardcoded-secrets`\n * fires on lines that contain real secrets, and we must not echo those\n * verbatim to third-party model providers.\n *\n * vo-mcp's `events-writer.ts` re-exports `sanitizeExcerpt` from this module\n * for back-compat with existing import sites; downstream callers don't need\n * to migrate.\n *\n * ## Patterns scrubbed\n *\n * - JWTs (three base64url segments joined by `.`)\n * - AWS access keys (`AKIA` / `ASIA` prefix + 16 chars)\n * - Google API keys (`AIza` + 35 chars)\n * - GitHub tokens (`ghp_` / `gho_` / `ghu_` / `ghs_` / `ghr_` + 36+ chars)\n * - Stripe secret keys (`sk_live_` / `sk_test_` + 20+ chars; `pk_` NOT\n * scrubbed because publishable keys are intentionally public)\n * - Generic `sk-` prefixed keys (Anthropic / OpenAI style)\n * - Bearer tokens\n * - Emails\n * - US SSN format (ddd-dd-dddd)\n * - File paths revealing usernames (`/Users/<name>/`,\n * `C:\\Users\\<name>\\`, `/home/<name>/`)\n *\n * ## What is kept\n *\n * - Code text \u2014 we want to debug failures; tool inputs and matched-rule\n * evidence ARE code text.\n *\n * Returns a string at most `maxLen` chars long. Default per handoff \u00A76: 200.\n */\n\n/** Default excerpt length cap \u2014 handoff \u00A76. */\nexport const SANITIZE_DEFAULT_MAX_LEN = 200;\n\nexport function sanitizeExcerpt(raw: string, maxLen: number = SANITIZE_DEFAULT_MAX_LEN): string {\n let s = raw;\n // JWTs first \u2014 broad pattern, run before narrower base64-ish patterns\n s = s.replace(/eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{5,}/g, '[REDACTED_JWT]');\n // AWS access keys \u2014 well-defined fixed-length prefix family\n s = s.replace(/\\b(?:AKIA|ASIA)[A-Z0-9]{16}\\b/g, '[REDACTED_AWS_KEY]');\n // Google API keys \u2014 fixed prefix + 35 char body\n s = s.replace(/\\bAIza[0-9A-Za-z_-]{35}\\b/g, '[REDACTED_GOOGLE_KEY]');\n // GitHub tokens \u2014 gh[pousr]_ + 36+ char body\n s = s.replace(/\\bgh[pousr]_[A-Za-z0-9]{36,}\\b/g, '[REDACTED_GITHUB_TOKEN]');\n // Stripe secret keys \u2014 sk_live_ / sk_test_ + 20+ chars\n s = s.replace(/\\bsk_(?:live|test)_[A-Za-z0-9_-]{20,}/g, '[REDACTED_STRIPE_KEY]');\n // Generic sk- (Anthropic/OpenAI style) \u2014 runs AFTER Stripe so sk_live_ doesn't\n // double-match (different separator: underscore vs hyphen)\n s = s.replace(/sk-[A-Za-z0-9]{16,}/g, '[REDACTED_KEY]');\n // Bearer\n s = s.replace(/Bearer\\s+[A-Za-z0-9_\\-.]{16,}/g, 'Bearer [REDACTED]');\n // Emails\n s = s.replace(/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g, '[REDACTED_EMAIL]');\n // US SSN format (ddd-dd-dddd). Word boundaries avoid matching the middle of\n // longer digit runs (e.g. credit-card-ish 12-345-6789 segments).\n s = s.replace(/\\b\\d{3}-\\d{2}-\\d{4}\\b/g, '[REDACTED_SSN]');\n // File paths revealing usernames. Keeps the directory-type hint (/Users/ vs\n // /home/) so debugging context isn't fully destroyed; replaces only the\n // username segment. Cross-platform: matches forward AND back slashes.\n // Word-boundary front-anchor avoids matching mid-token (e.g. 'getUsers/foo').\n s = s.replace(/(?<![A-Za-z0-9])([\\\\/])(Users|home)\\1[^\\\\/\\s'\"`]+/g, '$1$2$1[USER]');\n // Truncate\n if (s.length > maxLen) s = s.slice(0, maxLen) + '\u2026';\n return s;\n}\n", "/**\n * Local mode \u2014 no cloud control plane.\n *\n * `isCloudConnected()` returns false. `tenant_id` and `operator_id`\n * default to `'local'`.\n */\nexport interface LocalModeContext {\n readonly mode: 'local';\n readonly tenantId: 'local';\n readonly operatorId: 'local';\n isCloudConnected(): boolean;\n}\n\nexport function createLocalMode(): LocalModeContext {\n return {\n mode: 'local',\n tenantId: 'local',\n operatorId: 'local',\n isCloudConnected(): boolean {\n return false;\n },\n };\n}\n", "/**\n * Tool: `vo_check_assertion_strength`\n *\n * Phase 1 status: **implemented** against the stub ratchet client.\n * Real thresholds come from `@algosuite/ratchets-generic` in Phase 2.\n *\n * Contract:\n * input: { source: string, file_path?: string, language?: string }\n * output: ToolResultEnvelope<AssertionStrengthPayload>\n *\n * Behavior:\n * 1. Hash the canonicalized input. If the cache has a hit, return it\n * (with `cache.hit = true`) WITHOUT re-running the ratchet.\n * 2. Otherwise call the ratchet client, build the envelope, write it\n * to the cache, and return it (with `cache.hit = false`).\n * 3. EVERY call also writes a consensus-call event line (gate #7).\n * For non-consensus tools, `per_model_verdicts` is empty by design.\n */\nimport type { ToolDeps, ToolResultEnvelope, AssertionStrengthPayload } from '../types.js';\nimport {\n assertWithinByteCap,\n buildBaseEvent,\n bytesOf,\n invalidParams,\n jsonContent,\n type ToolInputSchema,\n} from './common.js';\n\nexport const TOOL_NAME = 'vo_check_assertion_strength';\n\n/** Static-ratchet tool \u2014 not consensus-engine routed. */\nconst GATE_TYPE = 'ratchet' as const;\n\n/** 512 KB \u2014 typical test file is <10 KB; a 500 KB ceiling catches accidental whole-file/whole-repo pastes. */\nconst MAX_SOURCE_BYTES = 512 * 1024;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n source: {\n type: 'string',\n description: 'Full test file body to analyze.',\n },\n file_path: {\n type: 'string',\n description: 'Optional path hint for nicer findings messages.',\n },\n language: {\n type: 'string',\n enum: ['ts', 'tsx', 'js', 'jsx', 'py'],\n description: 'Optional language hint.',\n },\n },\n required: ['source'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Analyzes a test file's assertions and returns a strength score (0-100), an overall verdict (strong/weak/hollow), and per-assertion findings. Hollow patterns flagged include toBeDefined()-only checks, toBeTruthy/toBeFalsy against literals, and tautological self-comparisons. Strong patterns rewarded include toBe, toEqual, toMatchObject, toContain, toHaveBeenCalledWith, toStrictEqual. Use BEFORE accepting a generated or hand-written test, to catch tests that 'pass' without verifying product truth. Open-shell ratchet \u2014 institutional thresholds load from a closed ratchet library; the scoring shape is stable across versions.\";\n\ninterface ToolInput {\n readonly source: string;\n readonly file_path?: string;\n readonly language?: string;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['source'] !== 'string') return false;\n if (o['file_path'] !== undefined && typeof o['file_path'] !== 'string') return false;\n if (o['language'] !== undefined && typeof o['language'] !== 'string') return false;\n return true;\n}\n\nexport async function handleCheckAssertionStrength(\n deps: ToolDeps,\n rawInput: unknown,\n // Accepted for ToolHandler signature parity with the other handlers\n // (2026-05-25 audit HIGH \u2014 MCP cancellation). This tool only calls the\n // ratchet stub, which is synchronous and short \u2014 cancellation has no\n // meaningful effect mid-call. Eslint-prefixed underscore marks the\n // parameter as deliberately unused.\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Required: { source: string }. Optional: file_path, language.',\n );\n }\n assertWithinByteCap(TOOL_NAME, 'source', rawInput.source, MAX_SOURCE_BYTES);\n const key = deps.cache.keyFor(TOOL_NAME, rawInput);\n const excerpt = rawInput.source.slice(0, 300);\n\n const inputSizeBytes = bytesOf(rawInput.source);\n const cached = deps.cache.get<ToolResultEnvelope<AssertionStrengthPayload>>(key);\n if (cached) {\n const envelope: ToolResultEnvelope<AssertionStrengthPayload> = {\n ...cached.value,\n cache: { hit: true, key },\n };\n deps.events.append({\n ...buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n }),\n cache_hit: true,\n });\n return jsonContent(envelope);\n }\n\n const result = await deps.ratchets.checkAssertionStrength({\n source: rawInput.source,\n ...(rawInput.file_path !== undefined ? { filePath: rawInput.file_path } : {}),\n ...(rawInput.language !== undefined ? { language: rawInput.language } : {}),\n });\n\n const payload: AssertionStrengthPayload = {\n score: result.score,\n max_score: result.maxScore,\n verdict: result.verdict,\n per_assertion_findings: result.findings,\n summary: result.summary,\n };\n\n const envelope: ToolResultEnvelope<AssertionStrengthPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n\n // Store the canonical (non-hit) version so subsequent gets reflect cache.hit=true.\n deps.cache.set(key, envelope);\n\n deps.events.append(\n buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n }),\n );\n\n return jsonContent(envelope);\n}\n", "/**\n * KB pre-filter for `vo_architecture_review` (Gate #5 vo-arch-defaults wire-up).\n *\n * Extracted from `architecture-review.ts` 2026-05-24 to keep the main tool\n * file under the 400-line cap (CLAUDE.md MANDATORY: File Size Limits).\n *\n * Three concerns:\n * 1. Look up which architectural-defaults rules apply to a diff\n * (`findRulesForDiff`).\n * 2. Sort + cap the matched rules so a wide-touch diff doesn't blow the\n * prompt budget (`prepareKBRules`, `MAX_KB_RULES = 10`).\n * 3. Render the matched rules as a prompt fragment, sanitizing any PII\n * from evidence excerpts before they ship to third-party models\n * (`formatRulesForPrompt`).\n *\n * All three are exported so the architecture-review-kb test file can unit-test\n * them directly.\n */\nimport {\n findApplicableRules,\n loadTenantOverride,\n parseUnifiedDiff,\n resolveStack,\n // sanitizeExcerpt moved into vo-arch-defaults in PR-U 2026-05-25 \u2014 direct\n // import here removes the cross-package indirection through vo-mcp's\n // events-writer re-export. Same function, single source.\n sanitizeExcerpt,\n type QueryHit,\n} from '@algosuite/vo-arch-defaults';\n\n/**\n * Default stack profile for the Nexus repo. Open string union; out-of-tree\n * tenants override this list via `tenant_stack` in their\n * `~/.claude/vo-arch-defaults.local.json` \u2014 see audit HIGH 2026-05-24.\n * Keep this list narrow: extra stack entries cost evidence-matcher CPU per\n * call but never suppress rules incorrectly (stack is OR-intersect).\n */\nexport const DEFAULT_STACK: ReadonlyArray<string> = [\n 'node',\n 'node-pnpm-monorepo',\n 'typescript-strict',\n 'firebase-cloud-functions',\n 'react-frontend',\n];\n\n/**\n * Resolve the effective stack for KB queries. Tenant `~/.claude/\n * vo-arch-defaults.local.json` with `tenant_stack: [...]` wins; otherwise\n * `DEFAULT_STACK` applies.\n *\n * Cached at module level \u2014 the tenant override file is read once per MCP\n * server process. Server restart picks up changes (the common operator\n * workflow). Tests use `__resetEffectiveStackCacheForTests` to invalidate\n * between fixture swaps.\n */\nlet _cachedEffectiveStack: ReadonlyArray<string> | null = null;\n\nexport function getEffectiveStack(): ReadonlyArray<string> {\n if (_cachedEffectiveStack === null) {\n try {\n const override = loadTenantOverride().override;\n _cachedEffectiveStack = resolveStack(override, DEFAULT_STACK);\n } catch {\n // Override file unreadable / malformed \u2192 log via the same channel\n // findRulesForDiff uses; fall back to DEFAULT_STACK so the KB still\n // works against the bundled-corpus assumed stack.\n process.stderr.write(\n `[vo-mcp] vo-arch-defaults tenant override unreadable; falling back to DEFAULT_STACK\\n`,\n );\n _cachedEffectiveStack = DEFAULT_STACK;\n }\n }\n return _cachedEffectiveStack;\n}\n\n/** Test-only: invalidate the cached effective-stack so the next call re-reads. */\nexport function __resetEffectiveStackCacheForTests(): void {\n _cachedEffectiveStack = null;\n}\n\n/**\n * Maximum architectural-rules surfaced into the consensus prompt. The v1 corpus\n * is ~36 rules; a wide-touch diff can match a dozen+ at once. Cap to bound\n * prompt token cost and keep the panel focused on the highest-severity items.\n * Lower-severity rules truncated \u2192 surfaced as `kb_rules_truncated` on the\n * envelope so operators can tune diff scope.\n */\nexport const MAX_KB_RULES = 10;\n\n/** Severity ordering \u2014 lower number = higher priority (surfaced first). */\nconst SEVERITY_RANK: Record<string, number> = {\n blocker: 0,\n warning: 1,\n info: 2,\n};\n\n/** Result of a KB lookup \u2014 `null` error means lookup succeeded, even if rules is `[]`. */\nexport interface KBLookupResult {\n readonly rules: ReadonlyArray<QueryHit>;\n /** Human-readable error if the KB threw; `null` on success. */\n readonly error: string | null;\n}\n\n/**\n * Look up architectural-defaults rules that apply to this diff.\n *\n * Catches all errors (corpus load failure, override parse failure,\n * malformed diff) \u2014 KB unavailability must NOT block the architecture\n * review itself; the engine still runs without rule context. But UNLIKE\n * the original silent-swallow implementation (audit HIGH 2026-05-23), this\n * variant SURFACES the error: stderr line for operator visibility + an\n * `error` field on the return value so callers can flag the envelope.\n */\nexport function findRulesForDiff(diff: string): KBLookupResult {\n try {\n const parsed = parseUnifiedDiff(diff);\n const filePaths = parsed.files.map((f) => f.path);\n if (filePaths.length === 0) return { rules: [], error: null };\n const result = findApplicableRules({\n stack: getEffectiveStack(),\n change_type: 'any',\n file_paths: filePaths,\n diff_excerpt: diff,\n });\n return { rules: result.applicable, error: null };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n // Operator-visible diagnostic. cli.ts already uses stderr for warnings,\n // so this lands in the same place a sysadmin would look.\n process.stderr.write(`[vo-mcp] vo-arch-defaults KB unavailable: ${message}\\n`);\n return { rules: [], error: message };\n }\n}\n\n/**\n * Sort rules by severity (blocker \u2192 warning \u2192 info \u2192 unknown), with rule_id\n * as a stable tiebreaker, then cap to `MAX_KB_RULES`. Returns the top-N rules\n * and the count of rules dropped (for `kb_rules_truncated` surfacing).\n */\nexport function prepareKBRules(hits: ReadonlyArray<QueryHit>): {\n topRules: ReadonlyArray<QueryHit>;\n truncated: number;\n} {\n const sorted = [...hits].sort((a, b) => {\n const sa = SEVERITY_RANK[a.rule.severity] ?? 99;\n const sb = SEVERITY_RANK[b.rule.severity] ?? 99;\n if (sa !== sb) return sa - sb;\n return a.rule.rule_id.localeCompare(b.rule.rule_id);\n });\n const topRules = sorted.slice(0, MAX_KB_RULES);\n const truncated = Math.max(0, sorted.length - topRules.length);\n return { topRules, truncated };\n}\n\n/**\n * Render matched rules as a prompt fragment. Returns empty string when no\n * rules matched \u2014 we deliberately don't emit a \"[no rules matched]\" line,\n * to keep prompt token cost minimal on diffs the KB doesn't cover.\n *\n * Each evidence excerpt is run through `sanitizeExcerpt` (events-writer's\n * PII scrubber) BEFORE inlining into the prompt: rules like\n * `security/no-hardcoded-secrets` fire on lines that contain real secrets,\n * and we must not echo those verbatim to third-party model providers.\n * (Audit HIGH 2026-05-23 \u2014 \"PII redaction on KB evidence excerpts.\")\n */\nexport function formatRulesForPrompt(\n hits: ReadonlyArray<QueryHit>,\n truncated: number,\n /**\n * Domain label inserted into the section header. Default 'ARCHITECTURAL'\n * preserves the architecture-review prompt header. Other tools pass their\n * own label (e.g. 'TESTING', 'SECURITY') so the panel sees a category\n * cue in the prompt block.\n */\n domainLabel: string = 'ARCHITECTURAL',\n): string {\n if (hits.length === 0) return '';\n const lines: string[] = ['', `---${domainLabel} RULES MATCHED FROM KB---`];\n for (const hit of hits) {\n const evidenceTag =\n hit.evidence.length > 0\n ? ` (${hit.evidence.length} evidence hit${hit.evidence.length === 1 ? '' : 's'} from mechanical detector)`\n : '';\n lines.push(\n `- [${hit.rule.severity}] ${hit.rule.rule_id}: ${hit.rule.title}${evidenceTag}`,\n );\n lines.push(` Rationale: ${hit.rule.rationale}`);\n if (hit.evidence.length > 0) {\n for (const e of hit.evidence.slice(0, 3)) {\n const loc = e.line !== undefined ? `${e.file}:${e.line}` : e.file;\n lines.push(` - ${loc} \u2014 ${sanitizeExcerpt(e.excerpt)}`);\n }\n }\n }\n if (truncated > 0) {\n lines.push(\n `... and ${truncated} more lower-severity rule${truncated === 1 ? '' : 's'} truncated to bound prompt size.`,\n );\n }\n lines.push('---END KB RULES---');\n return lines.join('\\n');\n}\n", "/**\n * KB pre-filter for tools that don't have a diff input \u2014 `vo_check_hollow_test`\n * and `vo_verify_answer` deep mode. Generalizes the pattern from\n * `architecture-review-kb-prefilter.ts` for the metadata-driven case.\n *\n * Architecture-review uses `findRulesForDiff` because it has a diff to parse +\n * run evidence matchers against. Hollow-test (sees a test source) and verify-\n * answer (sees expected/observed values) don't have a diff \u2014 they want to\n * surface rules by `category` (and optionally `tags`) regardless of file paths.\n *\n * Reuses `prepareKBRules` + `formatRulesForPrompt` + `KBLookupResult` from the\n * architecture-review helper so both pre-filter flavors render identically.\n * Uses the same DEFAULT_STACK so stack-gated rules surface consistently.\n */\nimport {\n loadBundledCorpus,\n loadTenantOverride,\n mergeCorpusWithOverride,\n type QueryHit,\n} from '@algosuite/vo-arch-defaults';\nimport { type KBLookupResult } from './architecture-review-kb-prefilter.js';\n\n/**\n * Options for the metadata-driven KB lookup.\n *\n * Either `category` or `tagsAny` (or both) MUST be provided \u2014 without at least\n * one filter, every rule in the corpus would surface (~30+ at v1) and blow\n * the prompt budget. Tools call with hardcoded values:\n *\n * `vo_check_hollow_test` \u2192 `{ category: 'testing' }`\n * `vo_verify_answer` deep \u2192 `{ category: 'security' }` (or `{ tagsAny: ['source-grounded'] }` for narrower)\n */\nexport interface FindRulesByMetadataOptions {\n /** Restrict to rules whose `category` equals this value. */\n readonly category?: string;\n /** Restrict to rules whose `tags` intersect this list (any-of semantics). */\n readonly tagsAny?: ReadonlyArray<string>;\n}\n\n/**\n * Look up KB rules by metadata (category and/or tags). Mirrors\n * `findRulesForDiff` graceful-degrade contract: catches all errors, surfaces\n * via stderr + `error` field on the return so the caller can flag the\n * envelope (`kb_unavailable: true`).\n *\n * Bypasses the stack / change_type / file_globs filters of\n * `findApplicableRules` \u2014 those gates are designed for diff-driven queries.\n * Metadata-driven queries (hollow-test, verify-answer) don't have a diff;\n * filtering by stack/files would incorrectly drop rules whose `applies_when`\n * narrows to specific file paths (e.g. testing rules with\n * `file_globs: ['**\\/*.test.ts']`).\n *\n * Loads the corpus + tenant override directly + filters by category + tags\n * with the same any-of semantics as `QueryInput.categories` / `tags_any`.\n */\nexport function findRulesByMetadata(opts: FindRulesByMetadataOptions): KBLookupResult {\n // Defensive: require at least one filter so the prompt doesn't get every\n // rule in the corpus.\n if (opts.category === undefined && opts.tagsAny === undefined) {\n return {\n rules: [],\n error: 'findRulesByMetadata called without category or tagsAny \u2014 refusing to surface every rule',\n };\n }\n try {\n const bundled = loadBundledCorpus().rules;\n const override = loadTenantOverride().override;\n const merged = mergeCorpusWithOverride(bundled, override);\n\n const tagAnyFilter = opts.tagsAny !== undefined ? new Set<string>(opts.tagsAny) : null;\n const hits: QueryHit[] = [];\n for (const rule of merged.rules) {\n if (opts.category !== undefined && rule.category !== opts.category) continue;\n if (tagAnyFilter !== null) {\n let tagHit = false;\n for (const t of rule.tags) {\n if (tagAnyFilter.has(t)) {\n tagHit = true;\n break;\n }\n }\n if (!tagHit) continue;\n }\n hits.push({\n rule,\n // No applies-context for metadata queries; report all matched=true so\n // downstream KBLookupResult consumers don't NPE on the field.\n reason: { stack_matched: true, change_type_matched: true, file_glob_matched: true },\n evidence: [], // No diff \u2192 no evidence matchers run.\n });\n }\n return { rules: hits, error: null };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[vo-mcp] vo-arch-defaults KB unavailable: ${message}\\n`);\n return { rules: [], error: message };\n }\n}\n", "/**\n * Tool: `vo_check_hollow_test` \u2014 Phase 2 Lane D-1.\n *\n * Detects 'hollow' test patterns where a test passes without verifying\n * product behavior. Runs the consensus engine (gate `plan-review`,\n * majority-vote synthesizer, 3-model cheap panel per Lane B gate config)\n * to grade the supplied test source for hollow-ness.\n *\n * Behavior parallels `vo_check_assertion_strength` (static-analysis\n * ratchet) but uses LLM judgment for patterns the static analyzer can't\n * detect \u2014 e.g. mocks that return whatever the test expects, fixtures\n * substituting for real outputs, tests that exercise no real code path.\n *\n * Contract (mirrors `consensus-judgment.ts`):\n * 1. Cache lookup on canonicalized input. Hit \u2192 return cached envelope\n * with cache.hit=true, still emit event (replay).\n * 2. Engine.run() with a hollow-test detection prompt. Miss \u2192 cache\n * and return real verdict + emit enriched event.\n * 3. Engine unavailable / throws \u2192 fall back to `verdict: 'unimplemented'`\n * envelope with `degraded_mode: true` (transient errors aren't cached).\n */\nimport type {\n ToolDeps,\n ToolResultEnvelope,\n HollowTestPayload,\n ConsensusCallEvent,\n} from '../types.js';\nimport type { QueryHit } from '@algosuite/vo-arch-defaults';\nimport {\n assertWithinByteCap,\n buildBaseEvent,\n bytesOf,\n invalidParams,\n jsonContent,\n toEventPerModelVerdicts,\n toEventSynthesizedVerdict,\n type ToolInputSchema,\n} from './common.js';\nimport {\n formatRulesForPrompt,\n prepareKBRules,\n} from './architecture-review-kb-prefilter.js';\nimport { findRulesByMetadata } from './kb-metadata-prefilter.js';\n\nexport const TOOL_NAME = 'vo_check_hollow_test';\n\n/** This tool routes to the Phase 2 `plan-review` gate (majority-vote, panel=3, cheap). */\nconst GATE_TYPE = 'plan-review' as const;\n\n/** 512 KB cap \u2014 same shape as check-assertion-strength: bounds a single test file. */\nconst MAX_SOURCE_BYTES = 512 * 1024;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n source: { type: 'string', description: 'Full test file body to analyze.' },\n file_path: { type: 'string', description: 'Optional path hint for nicer findings messages.' },\n },\n required: ['source'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Detects 'hollow' test patterns where a test passes without verifying product behavior \u2014 e.g. mocks that return whatever the test expects, assertions on local fixtures rather than real outputs, tests that exercise no real code path. Returns a consensus verdict (pass/fail/uncertain) with per-model reasoning. Complements vo_check_assertion_strength (static ratchet); this tool uses LLM judgment for patterns static analysis can't reach. Falls back to 'unimplemented' when no engine credentials are configured; the call is always logged.\";\n\ninterface ToolInput {\n readonly source: string;\n readonly file_path?: string;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['source'] !== 'string') return false;\n if (o['file_path'] !== undefined && typeof o['file_path'] !== 'string') return false;\n return true;\n}\n\n/**\n * Build the user prompt sent to each model. The system_prompt is supplied\n * by the engine's gate config; this function shapes the user-facing input.\n *\n * KB pre-filter (audit HIGH \u2014 generalized from architecture-review):\n * matched testing-category rules are inlined as deterministic context so\n * the panel reasons WITH the rule catalog instead of re-deriving it.\n */\nfunction buildPrompt(\n input: ToolInput,\n kbRules: ReadonlyArray<QueryHit>,\n kbTruncated: number,\n): string {\n const pathHint = input.file_path !== undefined ? `File: ${input.file_path}\\n` : '';\n const rulesBlock = formatRulesForPrompt(kbRules, kbTruncated, 'TESTING');\n return (\n `${pathHint}Analyze the following test source for HOLLOW patterns \u2014 tests that pass without verifying product truth. ` +\n `Hollow patterns include: assertions on locally-defined fixtures (not real product output), mocks ` +\n `that return the expected value verbatim, toBeDefined()/toBeTruthy() against literal values, ` +\n `tests that exercise NO real product code path, tautological self-comparisons (expect(x).toBe(x)).\\n\\n` +\n `Respond with verdict 'fail' if the test is HOLLOW (does not verify product truth), ` +\n `'pass' if the test is genuinely verifying product behavior, ` +\n `or 'uncertain' if the determination cannot be made from the source alone.${rulesBlock}\\n\\n` +\n `---TEST SOURCE---\\n${input.source}\\n---END---`\n );\n}\n\nexport async function handleCheckHollowTest(\n deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(TOOL_NAME, 'invalid input. Required: { source: string }.');\n }\n assertWithinByteCap(TOOL_NAME, 'source', rawInput.source, MAX_SOURCE_BYTES);\n const key = deps.cache.keyFor(TOOL_NAME, rawInput);\n const excerpt = rawInput.source.slice(0, 300);\n const inputSizeBytes = bytesOf(rawInput.source);\n\n // Cache hit \u2014 return cached envelope verbatim, still emit event.\n const cached = deps.cache.get<ToolResultEnvelope<HollowTestPayload>>(key);\n if (cached !== null) {\n const replayEvent: ConsensusCallEvent = {\n ...buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n }),\n per_model_verdicts: cached.value.payload.per_model_verdicts,\n synthesized_verdict: cached.value.payload.synthesized_verdict ?? null,\n consensus_confidence: cached.value.payload.synthesized_verdict?.confidence ?? null,\n consensus_engine_version: cached.value.payload.engine_version ?? null,\n cache_hit: true,\n };\n deps.events.append(replayEvent);\n return jsonContent({ ...cached.value, cache: { hit: true, key } });\n }\n\n // KB pre-filter \u2014 pull testing-category rules from vo-arch-defaults.\n // findRulesByMetadata catches all errors and surfaces them via stderr +\n // the `error` field; engine still runs without rule context on failure.\n const kbResult = findRulesByMetadata({ category: 'testing' });\n const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);\n\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n });\n\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\n let engineThrew = false;\n try {\n engineResult = await deps.consensus.run({\n gate_type: GATE_TYPE,\n prompt: buildPrompt(rawInput, topRules, kbTruncated),\n ...(signal !== undefined ? { signal } : {}),\n });\n } catch (err) {\n engineThrew = true;\n const message = err instanceof Error ? err.message : String(err);\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\n }\n\n // 2026-05-25 audit HIGH \u2014 MCP cancellation. See consensus-judgment.ts\n // for the full rationale. Emit audit event then throw AbortError.\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\n deps.events.append({\n ...baseEvent,\n downstream_outcome: {\n status: 'cancelled',\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\n },\n });\n const e = new Error('Request cancelled by client (notifications/cancelled)');\n e.name = 'AbortError';\n throw e;\n }\n\n if (!engineResult.ok) {\n const payload: HollowTestPayload = {\n verdict: 'unimplemented',\n reason: engineResult.reason,\n per_model_verdicts: [],\n gate_type: GATE_TYPE,\n ...(engineThrew ? { degraded_mode: true } : {}),\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\n };\n const envelope: ToolResultEnvelope<HollowTestPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n // Do NOT cache !ok envelopes (HP2-1 hardening, parity with\n // vo_consensus_judgment). Caching unimplemented envelopes makes the\n // unwired\u2192wired transition invisible to the cache \u2014 every prompt the\n // operator already asked would short-circuit to 'unimplemented' even\n // after credentials are configured.\n deps.events.append(baseEvent);\n return jsonContent(envelope);\n }\n\n const perModelForEvent = toEventPerModelVerdicts(engineResult.per_model_verdicts);\n const synthForEvent = toEventSynthesizedVerdict(engineResult.synthesized_verdict);\n\n const enrichedEvent: ConsensusCallEvent = {\n ...baseEvent,\n per_model_verdicts: perModelForEvent,\n synthesized_verdict: synthForEvent,\n consensus_confidence: engineResult.synthesized_verdict.confidence,\n duration_ms: engineResult.duration_ms,\n consensus_engine_version: engineResult.engine_version,\n };\n\n const payload: HollowTestPayload = {\n verdict: engineResult.synthesized_verdict.verdict,\n reason:\n engineResult.synthesized_verdict.dissent_summary ??\n `panel agreed (${engineResult.per_model_verdicts.length} models, engine=${engineResult.engine_version})`,\n per_model_verdicts: perModelForEvent,\n synthesized_verdict: synthForEvent,\n engine_version: engineResult.engine_version,\n degraded: engineResult.degraded,\n gate_type: GATE_TYPE,\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\n };\n const envelope: ToolResultEnvelope<HollowTestPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n deps.cache.set(key, envelope);\n deps.events.append(enrichedEvent);\n return jsonContent(envelope);\n}\n", "/**\n * Tool: `vo_verify_answer` \u2014 Phase 2 Lane D-1.\n *\n * Compares an expected value against an observed value via consensus\n * fan-out \u2014 semantic equivalence rather than literal equality. The engine\n * judges whether `observed` matches `expected` despite formatting drift,\n * unit conversions, paraphrase, etc.\n *\n * Gate routing (Lane B):\n * - default \u2192 `mid-exec-verify` (weighted synthesizer, panel=3, cheap)\n * - when caller sets `deep: true` \u2192 `final-deep-verify` (deliberation\n * synthesizer, panel=4, expensive, max 2 rounds)\n *\n * Falls back to `verdict: 'unimplemented'` envelope when engine unavailable.\n */\nimport type {\n ToolDeps,\n ToolResultEnvelope,\n VerifyAnswerPayload,\n ConsensusCallEvent,\n} from '../types.js';\nimport type { QueryHit } from '@algosuite/vo-arch-defaults';\nimport {\n assertWithinByteCap,\n buildBaseEvent,\n bytesOf,\n invalidParams,\n jsonContent,\n toEventPerModelVerdicts,\n toEventSynthesizedVerdict,\n type ToolInputSchema,\n} from './common.js';\nimport {\n formatRulesForPrompt,\n prepareKBRules,\n} from './architecture-review-kb-prefilter.js';\nimport { findRulesByMetadata } from './kb-metadata-prefilter.js';\n\nexport const TOOL_NAME = 'vo_verify_answer';\n\n/** Default gate \u2014 quick comparison. */\nconst SHALLOW_GATE = 'mid-exec-verify' as const;\n/** Deep gate \u2014 when caller passes `deep: true`. */\nconst DEEP_GATE = 'final-deep-verify' as const;\n\n/** 256 KB per side \u2014 well above any normal expected/observed but below the model context window. */\nconst MAX_VALUE_BYTES = 256 * 1024;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n expected: {\n description: 'The expected value (string, number, or structured JSON).',\n },\n observed: {\n description: 'The observed value to compare against expected.',\n },\n domain: {\n type: 'string',\n description: 'Domain hint for semantic comparison (e.g. tax, code, prose).',\n },\n deep: {\n type: 'boolean',\n description:\n 'When true, routes to the final-deep-verify gate (deliberation synthesizer, 4-model expensive panel, max 2 rounds). Default false \u2192 mid-exec-verify gate (weighted, 3-model cheap panel).',\n },\n },\n required: ['expected', 'observed'],\n additionalProperties: false,\n};\n\nexport const description =\n 'Compares an expected value against an observed value via consensus fan-out and returns whether they are semantically equivalent (handles formatting drift, unit conversions, paraphrase). Default routes to the mid-exec-verify gate (cheap 3-model panel); pass deep:true to escalate to final-deep-verify (4-model deliberation). Returns per-model verdicts and a synthesized pass/fail/uncertain. Falls back to \"unimplemented\" when engine credentials are absent; the call is always logged.';\n\ninterface ToolInput {\n readonly expected: unknown;\n readonly observed: unknown;\n readonly domain?: string;\n readonly deep?: boolean;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (!('expected' in o) || !('observed' in o)) return false;\n if (o['domain'] !== undefined && typeof o['domain'] !== 'string') return false;\n if (o['deep'] !== undefined && typeof o['deep'] !== 'boolean') return false;\n return true;\n}\n\nfunction safeStringify(v: unknown): string {\n try {\n return JSON.stringify(v);\n } catch {\n return String(v);\n }\n}\n\n/**\n * Build the user prompt \u2014 frames expected vs observed for semantic comparison.\n *\n * KB pre-filter (audit HIGH \u2014 generalized from architecture-review):\n * for DEEP-mode runs only, matched security-category rules are inlined\n * as deterministic context so the panel reasons WITH the source-grounded\n * + secrets-handling catalog. Shallow runs skip KB lookup to stay fast.\n */\nfunction buildPrompt(\n input: ToolInput,\n kbRules: ReadonlyArray<QueryHit>,\n kbTruncated: number,\n): string {\n const domain = input.domain !== undefined ? ` (domain: ${input.domain})` : '';\n const rulesBlock = formatRulesForPrompt(kbRules, kbTruncated, 'SECURITY');\n return (\n `Judge whether the OBSERVED value is semantically equivalent to the EXPECTED value${domain}. ` +\n `Treat formatting drift (whitespace, casing, currency symbols, unit notation) and paraphrase as ` +\n `equivalent; treat numerically different values, wrong units, or wrong factual content as NOT equivalent.\\n\\n` +\n `Respond with verdict 'pass' when equivalent, 'fail' when not equivalent, or 'uncertain' when the ` +\n `determination requires information not present in the inputs.${rulesBlock}\\n\\n` +\n `---EXPECTED---\\n${safeStringify(input.expected)}\\n---OBSERVED---\\n${safeStringify(input.observed)}\\n---END---`\n );\n}\n\nexport async function handleVerifyAnswer(\n deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(TOOL_NAME, 'invalid input. Required: { expected, observed }.');\n }\n // Per-side cap on the stringified value. Stringify once and reuse for the\n // size check + the cache excerpt below; safeStringify is identity for\n // strings so the byte count matches what the engine sees.\n const expectedStr = safeStringify(rawInput.expected);\n const observedStr = safeStringify(rawInput.observed);\n assertWithinByteCap(TOOL_NAME, 'expected', expectedStr, MAX_VALUE_BYTES);\n assertWithinByteCap(TOOL_NAME, 'observed', observedStr, MAX_VALUE_BYTES);\n const gateType = rawInput.deep === true ? DEEP_GATE : SHALLOW_GATE;\n\n // Cache key includes the resolved gate type so deep and shallow runs don't\n // collide on the same expected/observed pair.\n const cacheInput = {\n expected: rawInput.expected,\n observed: rawInput.observed,\n ...(rawInput.domain !== undefined ? { domain: rawInput.domain } : {}),\n gate_type: gateType,\n };\n const key = deps.cache.keyFor(TOOL_NAME, cacheInput);\n const excerpt = safeStringify(rawInput).slice(0, 300);\n const inputSizeBytes = bytesOf(expectedStr) + bytesOf(observedStr);\n\n // KB pre-filter (deep mode only). Shallow mid-exec-verify stays fast +\n // skips KB lookup; deep final-deep-verify pulls security-category rules\n // (no-hardcoded-secrets, no-env-var-leak, source-grounded-fetch-hardening,\n // no-pii-in-logs) so the panel has the deterministic safety catalog.\n const isDeep = gateType === DEEP_GATE;\n const kbResult = isDeep\n ? findRulesByMetadata({ category: 'security' })\n : { rules: [] as ReadonlyArray<QueryHit>, error: null };\n const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);\n\n // Cache hit replay.\n const cached = deps.cache.get<ToolResultEnvelope<VerifyAnswerPayload>>(key);\n if (cached !== null) {\n const replayEvent: ConsensusCallEvent = {\n ...buildBaseEvent({\n tool: TOOL_NAME,\n gateType,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n }),\n per_model_verdicts: cached.value.payload.per_model_verdicts,\n synthesized_verdict: cached.value.payload.synthesized_verdict ?? null,\n consensus_confidence: cached.value.payload.synthesized_verdict?.confidence ?? null,\n consensus_engine_version: cached.value.payload.engine_version ?? null,\n cache_hit: true,\n };\n deps.events.append(replayEvent);\n return jsonContent({ ...cached.value, cache: { hit: true, key } });\n }\n\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\n tool: TOOL_NAME,\n gateType,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n });\n\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\n let engineThrew = false;\n try {\n engineResult = await deps.consensus.run({\n gate_type: gateType,\n prompt: buildPrompt(rawInput, topRules, kbTruncated),\n ...(signal !== undefined ? { signal } : {}),\n });\n } catch (err) {\n engineThrew = true;\n const message = err instanceof Error ? err.message : String(err);\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\n }\n\n // 2026-05-25 audit HIGH \u2014 MCP cancellation. See consensus-judgment.ts.\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\n deps.events.append({\n ...baseEvent,\n downstream_outcome: {\n status: 'cancelled',\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\n },\n });\n const e = new Error('Request cancelled by client (notifications/cancelled)');\n e.name = 'AbortError';\n throw e;\n }\n\n if (!engineResult.ok) {\n const payload: VerifyAnswerPayload = {\n verdict: 'unimplemented',\n reason: engineResult.reason,\n per_model_verdicts: [],\n gate_type: gateType,\n ...(engineThrew ? { degraded_mode: true } : {}),\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\n };\n const envelope: ToolResultEnvelope<VerifyAnswerPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n // Do NOT cache !ok envelopes (HP2-1 hardening). See vo_consensus_judgment\n // for rationale: caching unwired envelopes makes the unwired\u2192wired\n // transition invisible to the cache.\n deps.events.append(baseEvent);\n return jsonContent(envelope);\n }\n\n const perModelForEvent = toEventPerModelVerdicts(engineResult.per_model_verdicts);\n const synthForEvent = toEventSynthesizedVerdict(engineResult.synthesized_verdict);\n const enrichedEvent: ConsensusCallEvent = {\n ...baseEvent,\n per_model_verdicts: perModelForEvent,\n synthesized_verdict: synthForEvent,\n consensus_confidence: engineResult.synthesized_verdict.confidence,\n duration_ms: engineResult.duration_ms,\n consensus_engine_version: engineResult.engine_version,\n };\n\n const payload: VerifyAnswerPayload = {\n verdict: engineResult.synthesized_verdict.verdict,\n reason:\n engineResult.synthesized_verdict.dissent_summary ??\n `panel agreed (${engineResult.per_model_verdicts.length} models, engine=${engineResult.engine_version})`,\n per_model_verdicts: perModelForEvent,\n synthesized_verdict: synthForEvent,\n engine_version: engineResult.engine_version,\n degraded: engineResult.degraded,\n gate_type: gateType,\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\n };\n const envelope: ToolResultEnvelope<VerifyAnswerPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n deps.cache.set(key, envelope);\n deps.events.append(enrichedEvent);\n return jsonContent(envelope);\n}\n", "/**\n * Accepted `gate_type` values for the consensus protocol surface.\n *\n * These are part of vo-mcp's OPEN protocol surface \u2014 the public contract a\n * caller sees in the `vo_consensus_judgment` tool's `gate_type` enum. They are\n * intentionally defined HERE (the open package) rather than imported from the\n * closed `@algosuite/consensus-engine`, so the open MCP shell carries no\n * dependency on the closed engine. (Was imported from the engine pre-extraction;\n * see docs/oss-publication-readiness.md.)\n *\n * The values MUST stay in lock-step with the engine's gate registry\n * (`packages/consensus-engine/src/gates/index.ts`). A future shared\n * `@algosuite/consensus-protocol` package could host this single source and be\n * imported by both; until then it is a small, stable, duplicated list.\n */\n\n/** Phase-1 legacy gate names (snake_case). */\nexport const LEGACY_GATE_TYPES = [\n 'test_assertion',\n 'architecture_review',\n 'factual_grounding',\n 'verify_answer',\n 'other',\n] as const;\nexport type LegacyGateType = (typeof LEGACY_GATE_TYPES)[number];\n\n/** Phase-2 gate names (kebab-case) \u2014 the engine's recognized cost-tiered gates. */\nexport const KNOWN_GATE_TYPES = [\n 'plan-review',\n 'mid-exec-verify',\n 'final-deep-verify',\n 'architecture-review',\n] as const;\nexport type Phase2GateType = (typeof KNOWN_GATE_TYPES)[number];\n\n/** Every gate name the engine recognizes (legacy + Phase 2). */\nexport const ALL_RECOGNIZED_GATE_TYPES = [\n ...LEGACY_GATE_TYPES,\n ...KNOWN_GATE_TYPES,\n] as const;\nexport type RecognizedGateType = LegacyGateType | Phase2GateType;\n\nexport function isRecognizedGateType(gateType: string): gateType is RecognizedGateType {\n return (ALL_RECOGNIZED_GATE_TYPES as readonly string[]).includes(gateType);\n}\n", "/**\n * Source-grounded input plumbing for the `vo_consensus_judgment` tool.\n *\n * Extracted from `consensus-judgment.ts` to keep that file under its size cap\n * while lighting up the previously-dormant Tier-4 path. Owns:\n * - the `source_urls` / `source_grounded` input shape + type guards, and\n * - `normalizeSourceGrounded()`, the pure mapping from the flat tool input to\n * the engine request fields (`source_urls`, `source_grounded`) plus the\n * cache-key fragments that must distinguish a sourceless from a\n * source-grounded call over the same prompt.\n *\n * Everything here is PURE (no I/O) and keeps the sourceless path inert: when no\n * non-empty source URL is supplied, `active` is false and the caller forwards\n * nothing new to the engine \u2014 byte-for-byte the old behaviour.\n */\nimport type { ConsensusSourceGroundedConfig } from '../consensus/client.js';\n\n/** Flat `source_grounded` toggle block accepted on the tool input. */\nexport interface ToolSourceGrounded {\n readonly enable_citation_grading?: boolean;\n readonly escalate_below?: number;\n}\n\nexport function isStringArray(v: unknown): v is readonly string[] {\n return Array.isArray(v) && v.every((e) => typeof e === 'string');\n}\n\nexport function isToolSourceGrounded(v: unknown): v is ToolSourceGrounded {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (\n o['enable_citation_grading'] !== undefined &&\n typeof o['enable_citation_grading'] !== 'boolean'\n ) {\n return false;\n }\n if (o['escalate_below'] !== undefined && typeof o['escalate_below'] !== 'number') return false;\n return true;\n}\n\n/** Resolved, normalized source-grounded request fields. */\nexport interface NormalizedSourceGrounded {\n /** True only when at least one non-empty source URL was supplied. */\n readonly active: boolean;\n /** Trimmed, non-empty source URLs (empty array when inactive). */\n readonly sourceUrls: readonly string[];\n /** True when sources are active AND the caller opted into citation grading. */\n readonly gradingEnabled: boolean;\n /**\n * The `source_grounded` config to forward to the engine when grading is on.\n * Carries NO classifier \u2014 the engine-client supplies the default classifier\n * built from a live panel adapter. `undefined` when grading is off.\n */\n readonly config?: ConsensusSourceGroundedConfig;\n /**\n * Extra cache-key fragments. Folded into the cache input so a sourceless call\n * and a source-grounded call over the same prompt never collide on one entry.\n */\n readonly cacheFragments: Readonly<Record<string, unknown>>;\n}\n\n/**\n * Pure normalizer. Given the raw (already type-validated) source fields,\n * produce the engine request fields + cache fragments.\n */\nexport function normalizeSourceGrounded(input: {\n readonly source_urls?: readonly string[];\n readonly source_grounded?: ToolSourceGrounded;\n}): NormalizedSourceGrounded {\n const sourceUrls =\n input.source_urls !== undefined\n ? input.source_urls.filter((u) => u.trim().length > 0)\n : [];\n const active = sourceUrls.length > 0;\n const gradingEnabled = active && input.source_grounded?.enable_citation_grading === true;\n const escalateBelow = input.source_grounded?.escalate_below;\n\n const config: ConsensusSourceGroundedConfig | undefined = gradingEnabled\n ? {\n citation_grader: {\n enabled: true,\n ...(escalateBelow !== undefined ? { escalate_below: escalateBelow } : {}),\n },\n }\n : undefined;\n\n const cacheFragments: Record<string, unknown> = {};\n if (active) cacheFragments['source_urls'] = sourceUrls;\n if (gradingEnabled) {\n cacheFragments['citation_grading'] = true;\n if (escalateBelow !== undefined) cacheFragments['escalate_below'] = escalateBelow;\n }\n\n return {\n active,\n sourceUrls,\n gradingEnabled,\n ...(config !== undefined ? { config } : {}),\n cacheFragments,\n };\n}\n", "/**\n * Tool: `vo_consensus_judgment`\n *\n * Phase 2 Lane C status:\n * - Engine-backed when wired (Lane A's `tryCreateEngineConsensusClientFromEnv`).\n * - Phase 2 gate types (`plan-review`, `mid-exec-verify`, `final-deep-verify`,\n * `architecture-review`) accepted and forwarded to the engine via the\n * gate_type field. Default = `plan-review` when caller omits it.\n * - Cache integration uses the existing `deps.cache` (tool response cache \u2014\n * V1 launch gate #8). On a hit the cached envelope is returned with\n * `cache.hit = true`. The engine itself is not re-invoked.\n * - Engine-throw fallback: when the engine client throws unexpectedly OR\n * all models err out, the tool returns the unimplemented envelope with\n * `degraded_mode: true` so cross-vendor MCP clients always see a\n * stable shape.\n *\n * Event-emit contract (unchanged): exactly one event line per call. The\n * cache-hit path also emits an event (with `cache_hit` shape preserved\n * via the input_hash and the engine_version/etc populated from the\n * cached payload). This keeps the V1 gate #7 dataset moat intact for\n * cache replays.\n */\nimport type {\n ToolDeps,\n ToolResultEnvelope,\n ConsensusJudgmentPayload,\n ConsensusCallEvent,\n} from '../types.js';\nimport {\n assertWithinByteCap,\n buildBaseEvent,\n bytesOf,\n invalidParams,\n jsonContent,\n toEventPerModelVerdicts,\n toEventSynthesizedVerdict,\n type ToolInputSchema,\n} from './common.js';\n// The accepted gate_type set is the OPEN protocol surface \u2014 defined in vo-mcp\n// (gate-types.ts), NOT imported from the closed @algosuite/consensus-engine, so\n// the open MCP shell carries no dependency on the closed engine. The list mirrors\n// the engine's gate registry (see gate-types.ts). (Was imported from the engine\n// pre-extraction \u2014 see docs/oss-publication-readiness.md.)\nimport {\n ALL_RECOGNIZED_GATE_TYPES,\n type RecognizedGateType,\n} from '../consensus/gate-types.js';\nimport {\n isStringArray,\n isToolSourceGrounded,\n normalizeSourceGrounded,\n type ToolSourceGrounded,\n} from './consensus-judgment-source-grounded.js';\n\nexport const TOOL_NAME = 'vo_consensus_judgment';\n\n/** 64 KB prompt cap \u2014 keeps a 4-model fan-out under ~256 KB total. */\nconst MAX_PROMPT_BYTES = 64 * 1024;\n/** 256 KB context cap \u2014 larger than prompt because context is stringified JSON. */\nconst MAX_CONTEXT_BYTES = 256 * 1024;\n\n/**\n * Accepted gate_type values. Sourced from `@algosuite/consensus-engine`'s\n * `ALL_RECOGNIZED_GATE_TYPES` constant (PR-S 2026-05-25) \u2014 Phase 1 legacy\n * names + Phase 2 kebab-case names. Adding a new gate type now happens in\n * ONE place (the engine's gates module) instead of two.\n */\nconst ALL_GATE_TYPES = ALL_RECOGNIZED_GATE_TYPES;\ntype AcceptedGateType = RecognizedGateType;\n\nconst DEFAULT_GATE_TYPE: AcceptedGateType = 'plan-review';\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n prompt: {\n type: 'string',\n description: 'The judgment prompt \u2014 what you want multiple models to assess.',\n },\n gate_type: {\n type: 'string',\n enum: [...ALL_GATE_TYPES],\n description:\n 'Which gate this judgment serves. Phase 2 kebab-case names (plan-review, mid-exec-verify, final-deep-verify, architecture-review) drive the engine\u2019s synthesizer choice. Phase 1 underscore names back-compat to strict-consensus. Default: plan-review.',\n },\n context: {\n type: 'object',\n description: 'Tool-specific context (e.g. source file, diff, observed value).',\n additionalProperties: true,\n },\n source_urls: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Authoritative source URLs to ground the judgment against (Tier-4). When NON-EMPTY, the engine routes to its source-grounded path: it fetches the sources, flags low-confidence retrievals (surfaced as low_confidence_sources), and \u2014 when source_grounded.enable_citation_grading is true \u2014 grades each claim against the cited source text. When absent/empty the call uses the unchanged sourceless consensus path.',\n },\n source_grounded: {\n type: 'object',\n description:\n 'Optional source-grounded behaviour toggles. Only meaningful when source_urls is non-empty.',\n properties: {\n enable_citation_grading: {\n type: 'boolean',\n description:\n 'Opt IN to deterministic citation grading (default OFF). A failing grade raises an escalation signal, so this is behaviour-changing. When true, a real claim classifier (a single cheap model call) grades each claim against the cited sources.',\n },\n escalate_below: {\n type: 'number',\n description:\n 'Uncertain-claim escalation floor [0..1]; an uncertain claim with confidence below this triggers escalation. Engine default 0.85 when omitted.',\n },\n },\n additionalProperties: false,\n },\n },\n required: ['prompt'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Submit a prompt to multiple LLMs and return a synthesized consensus verdict with per-model verdicts visible. Use for high-stakes judgment calls where a single model's verdict is too risky (architecture reviews, weak-vs-strong-assertion calls, factual grounding against authoritative sources). Returns a real synthesized verdict when the closed consensus engine is wired in with credentials; falls back to 'unimplemented' otherwise. The call IS always logged for dataset and unit-economics audit (V1 launch gate #7).\";\n\ninterface ToolInput {\n readonly prompt: string;\n readonly gate_type?: string;\n readonly context?: Record<string, unknown>;\n readonly source_urls?: readonly string[];\n readonly source_grounded?: ToolSourceGrounded;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['prompt'] !== 'string') return false;\n if (o['gate_type'] !== undefined && typeof o['gate_type'] !== 'string') return false;\n if (o['source_urls'] !== undefined && !isStringArray(o['source_urls'])) return false;\n if (o['source_grounded'] !== undefined && !isToolSourceGrounded(o['source_grounded'])) return false;\n return true;\n}\n\nfunction isAcceptedGateType(v: string): v is AcceptedGateType {\n return (ALL_GATE_TYPES as readonly string[]).includes(v);\n}\n\nexport async function handleConsensusJudgment(\n deps: ToolDeps,\n rawInput: unknown,\n /**\n * Cancellation signal threaded from `server.ts` via the MCP SDK's\n * `RequestHandlerExtra.signal`. Forwarded to the consensus engine call\n * so per-model API fetches abort when the client cancels.\n * 2026-05-25 audit HIGH.\n */\n signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Required: { prompt: string, gate_type?: string }.',\n );\n }\n assertWithinByteCap(TOOL_NAME, 'prompt', rawInput.prompt, MAX_PROMPT_BYTES);\n let contextStrForSize = '';\n if (rawInput.context !== undefined) {\n try {\n contextStrForSize = JSON.stringify(rawInput.context);\n } catch {\n // Non-serializable context (cycles, BigInt, etc.) \u2014 reject early with a clear message.\n throw invalidParams(TOOL_NAME, \"input field 'context' is not JSON-serializable.\");\n }\n assertWithinByteCap(TOOL_NAME, 'context', contextStrForSize, MAX_CONTEXT_BYTES);\n }\n const rawGate = rawInput.gate_type ?? DEFAULT_GATE_TYPE;\n const gateType: AcceptedGateType = isAcceptedGateType(rawGate) ? rawGate : DEFAULT_GATE_TYPE;\n const inputSizeBytes = bytesOf(rawInput.prompt) + bytesOf(contextStrForSize);\n\n // Source-grounded resolution. Sources are only \"active\" when non-empty;\n // an empty/absent array leaves the call on the unchanged sourceless path\n // (no source_urls / source_grounded forwarded \u2192 engine-client never builds\n // a classifier, exactly as before). Citation grading is OFF unless the\n // caller opted in via source_grounded.enable_citation_grading.\n const sg = normalizeSourceGrounded({\n ...(rawInput.source_urls !== undefined ? { source_urls: rawInput.source_urls } : {}),\n ...(rawInput.source_grounded !== undefined ? { source_grounded: rawInput.source_grounded } : {}),\n });\n\n // Cache key includes the resolved gate type so different gate routing\n // doesn't collide on the same prompt. Source URLs + grading flag are also\n // folded in (via sg.cacheFragments) so a sourceless call and a\n // source-grounded call over the same prompt do not collide on one entry.\n const cacheInput = {\n prompt: rawInput.prompt,\n gate_type: gateType,\n ...(rawInput.context !== undefined ? { context: rawInput.context } : {}),\n ...sg.cacheFragments,\n };\n const key = deps.cache.keyFor(TOOL_NAME, cacheInput);\n const excerpt = rawInput.prompt.slice(0, 300);\n\n // Cache hit short-circuit \u2014 return the cached envelope verbatim (with\n // `cache.hit: true`) and still emit an event so the gate #7 dataset\n // records the replay. Engine is NOT re-invoked.\n const cached = deps.cache.get<ToolResultEnvelope<ConsensusJudgmentPayload>>(key);\n if (cached !== null) {\n const replayEvent: ConsensusCallEvent = buildBaseEvent({\n tool: TOOL_NAME,\n gateType,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n });\n // Carry the cached payload's verdict shape into the replay event so\n // downstream consumers see consistent verdicts on replays.\n const enrichedReplay: ConsensusCallEvent = {\n ...replayEvent,\n per_model_verdicts: cached.value.payload.per_model_verdicts,\n synthesized_verdict: cached.value.payload.synthesized_verdict ?? null,\n consensus_confidence: cached.value.payload.synthesized_verdict?.confidence ?? null,\n consensus_engine_version: cached.value.payload.engine_version ?? null,\n cache_hit: true,\n };\n deps.events.append(enrichedReplay);\n const replayEnvelope: ToolResultEnvelope<ConsensusJudgmentPayload> = {\n ...cached.value,\n cache: { hit: true, key },\n };\n return jsonContent(replayEnvelope);\n }\n\n // Run the engine. The engine-client never throws by contract \u2014 but we\n // defensively wrap in case a custom client subclass does, and degrade\n // gracefully to the unimplemented envelope with degraded_mode=true.\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\n tool: TOOL_NAME,\n gateType,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n });\n\n // Source-grounded forwarding. When sources are present but grading is OFF,\n // we still forward source_urls (so retrieval-confidence flagging activates)\n // but no source_grounded config \u2014 the engine-client then never builds a\n // classifier. When grading is ON, `sg.config` carries `citation_grader:\n // { enabled: true }` with NO classifier; the engine-client supplies the\n // default classifier built from a live panel adapter (the tool has no model\n // handle of its own).\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\n let engineThrew = false;\n try {\n engineResult = await deps.consensus.run({\n gate_type: gateType,\n prompt: rawInput.prompt,\n ...(rawInput.context !== undefined ? { caller_context: rawInput.context } : {}),\n ...(signal !== undefined ? { signal } : {}),\n ...(sg.active ? { source_urls: sg.sourceUrls } : {}),\n ...(sg.config !== undefined ? { source_grounded: sg.config } : {}),\n });\n } catch (err) {\n engineThrew = true;\n const message = err instanceof Error ? err.message : String(err);\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\n }\n\n // Cancellation short-circuit (2026-05-25 audit HIGH). When the engine\n // returns `reason: 'cancelled'` it means the SDK fired `extra.signal`\n // mid-call. Emit ONE event with `downstream_outcome.status='cancelled'`\n // so the audit trail (gate #7) is complete, then re-throw so the\n // request-handler error path returns a JSON-RPC error to the (already\n // disinterested) client. We do NOT cache cancelled envelopes.\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\n deps.events.append({\n ...baseEvent,\n downstream_outcome: {\n status: 'cancelled',\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\n },\n });\n const e = new Error('Request cancelled by client (notifications/cancelled)');\n e.name = 'AbortError';\n throw e;\n }\n\n if (!engineResult.ok) {\n // Engine unavailable (no credentials, panel-too-small, or throw fallback).\n const payload: ConsensusJudgmentPayload = {\n verdict: 'unimplemented',\n reason: engineResult.reason,\n per_model_verdicts: [],\n gate_type: gateType,\n ...(engineThrew ? { degraded_mode: true } : {}),\n };\n const envelope: ToolResultEnvelope<ConsensusJudgmentPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n // Do NOT cache !ok envelopes (hardening 2026-05-22 \u2014 finding HP2-1).\n //\n // Original heuristic was: cache stable failures (no credentials), skip\n // transient throws. Problem: BOTH classes are operationally transient\n // from the operator's perspective. \"No credentials\" gets resolved by\n // setting env vars + restarting the MCP server. If unimplemented\n // envelopes were cached on disk (sqlite-backed cache), the operator's\n // env-var change would have no effect \u2014 `cache.hit: true` would short-\n // circuit before the engine retry on every previously-asked prompt.\n //\n // Concretely: the cache key includes prompt + gate_type + context but\n // NOT the engine availability state, so transition unwired\u2192wired is\n // invisible to the cache. Solution: never persist failure envelopes;\n // every !ok call re-tries the engine on the next invocation.\n //\n // Trade-off: tiny perf cost (engine retried on every \"no credentials\"\n // call) for correctness when wiring is added. Same applies to\n // engine-threw transient errors. `engineThrew` remains in scope as the\n // `degraded_mode` flag in the response payload (see line ~205).\n deps.events.append(baseEvent);\n return jsonContent(envelope);\n }\n\n // Engine ran \u2014 populate the rich payload, cache it, emit enriched event.\n const perModelForEvent = toEventPerModelVerdicts(engineResult.per_model_verdicts);\n const synthForEvent = toEventSynthesizedVerdict(engineResult.synthesized_verdict);\n\n const enrichedEvent: ConsensusCallEvent = {\n ...baseEvent,\n consensus_confidence: engineResult.synthesized_verdict.confidence,\n duration_ms: engineResult.duration_ms,\n consensus_engine_version: engineResult.engine_version,\n per_model_verdicts: perModelForEvent,\n synthesized_verdict: synthForEvent,\n };\n\n const payload: ConsensusJudgmentPayload = {\n verdict: engineResult.synthesized_verdict.verdict,\n reason:\n engineResult.synthesized_verdict.dissent_summary ??\n `panel agreed (${engineResult.per_model_verdicts.length} models, engine=${engineResult.engine_version})`,\n per_model_verdicts: perModelForEvent,\n synthesized_verdict: synthForEvent,\n engine_version: engineResult.engine_version,\n degraded: engineResult.degraded,\n gate_type: gateType,\n // \u2500\u2500\u2500 Consensus-engine feature outputs (additive; 2026-06-13) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Feature 2 (calibrated-confidence) \u2014 ON by default; the engine attaches\n // calibrated_confidence + confidence_badge to the synthesized verdict on the\n // plain runConsensus path. Surface them at the top level so callers don't\n // have to know the engine's internal SynthesizedVerdict shape.\n ...(engineResult.synthesized_verdict.calibrated_confidence !== undefined\n ? { calibrated_confidence: engineResult.synthesized_verdict.calibrated_confidence }\n : {}),\n ...(engineResult.synthesized_verdict.confidence_badge !== undefined\n ? { confidence_badge: engineResult.synthesized_verdict.confidence_badge }\n : {}),\n // Feature 1 (agreement-gate) \u2014 fan-out diagnostics (present iff the gate ran).\n ...(engineResult.fan_out_diagnostics !== undefined\n ? { fan_out_diagnostics: engineResult.fan_out_diagnostics }\n : {}),\n // Stage A7-shadow \u2014 adaptive-vs-incumbent comparison (PII-free; present iff shadow on).\n ...(engineResult.shadow_synthesis !== undefined\n ? { shadow_synthesis: engineResult.shadow_synthesis }\n : {}),\n // Source-grounded Tier-4 outputs (present iff the call was source-grounded).\n ...(engineResult.source_grounded === true ? { source_grounded: true } : {}),\n ...(engineResult.citation_grade !== undefined\n ? { citation_grade: engineResult.citation_grade }\n : {}),\n ...(engineResult.low_confidence_sources !== undefined\n ? { low_confidence_sources: engineResult.low_confidence_sources }\n : {}),\n // Escalation (from citation grade or human-tiebreak synthesizer).\n ...(engineResult.escalation_required !== undefined\n ? { escalation_required: engineResult.escalation_required }\n : {}),\n ...(engineResult.escalation_reason !== undefined\n ? { escalation_reason: engineResult.escalation_reason }\n : {}),\n };\n const envelope: ToolResultEnvelope<ConsensusJudgmentPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n\n // Cache successful envelopes \u2014 the next identical call short-circuits.\n deps.cache.set(key, envelope);\n deps.events.append(enrichedEvent);\n return jsonContent(envelope);\n}\n", "/**\n * Tool: `vo_architecture_review` \u2014 Phase 2 Lane D-1 + Gate #5 KB wire-up.\n *\n * Senior-architect review of a proposed diff against the project's\n * architectural defaults (naming, boundary discipline, error-handling\n * shape, test honesty). Runs the engine on the `architecture-review`\n * gate (Lane B: human-tiebreak synthesizer, panel=3, expensive).\n *\n * KB pre-filter (Gate #5 \u2014 vo-arch-defaults): before invoking the\n * consensus engine, query the architectural-defaults KB for rules\n * applicable to this diff's stack/files. Matched rules (and any\n * mechanical evidence hits) are included in the prompt as additional\n * context so the panel reasons WITH the deterministic-rule KB instead\n * of re-deriving its content from scratch. KB lookup failures are\n * caught and degrade silently \u2014 the engine still runs.\n *\n * The `human-tiebreak` synthesizer surfaces an ESCALATION SIGNAL when\n * panel members disagree \u2014 the verdict still resolves via internal\n * majority-vote, but the engine sets `escalation_required` + an\n * `escalation_reason`. This tool forwards both via an optional\n * `escalation` field on the envelope payload (additive \u2014 schema_version\n * stays at 1, fields are optional). Callers can route the escalation to\n * a human reviewer; absent escalation field means panel agreed.\n *\n * Falls back to `verdict: 'unimplemented'` envelope when engine is\n * unavailable, matching the consensus-judgment pattern.\n */\nimport type {\n ToolDeps,\n ToolResultEnvelope,\n ArchitectureReviewPayload,\n ConsensusCallEvent,\n} from '../types.js';\nimport {\n assertWithinByteCap,\n buildBaseEvent,\n bytesOf,\n invalidParams,\n jsonContent,\n toEventPerModelVerdicts,\n toEventSynthesizedVerdict,\n type ToolInputSchema,\n} from './common.js';\nimport { sanitizeExcerpt } from '../logging/events-writer.js';\nimport type { QueryHit } from '@algosuite/vo-arch-defaults';\nimport {\n findRulesForDiff,\n formatRulesForPrompt,\n prepareKBRules,\n} from './architecture-review-kb-prefilter.js';\n\n// Re-export for backwards-compatible test imports.\nexport {\n findRulesForDiff,\n formatRulesForPrompt,\n prepareKBRules,\n} from './architecture-review-kb-prefilter.js';\nexport type { KBLookupResult } from './architecture-review-kb-prefilter.js';\n\nexport const TOOL_NAME = 'vo_architecture_review';\n\nconst GATE_TYPE = 'architecture-review' as const;\n\n/** 1 MB diff cap \u2014 typical PR diff is <100 KB; ceiling rejects whole-repo diffs. */\nconst MAX_DIFF_BYTES = 1024 * 1024;\n/** 16 KB summary cap \u2014 paragraph-scale text only. */\nconst MAX_SUMMARY_BYTES = 16 * 1024;\n\nconst ACCEPTED_CHANGE_TYPES = [\n 'refactor',\n 'new_feature',\n 'bug_fix',\n 'dep_update',\n 'config',\n 'docs',\n 'other',\n] as const;\ntype AcceptedChangeType = (typeof ACCEPTED_CHANGE_TYPES)[number];\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n diff: {\n type: 'string',\n description: 'Unified diff of the proposed change.',\n },\n change_type: {\n type: 'string',\n enum: [...ACCEPTED_CHANGE_TYPES],\n description: 'Classification of the change.',\n },\n summary: {\n type: 'string',\n description: 'One-paragraph summary of intent / motivation.',\n },\n },\n required: ['diff', 'change_type'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Senior-architect review of a proposed diff against the project's architectural defaults \u2014 naming conventions, boundary discipline, error-handling shape, test honesty. Runs the architecture-review gate (human-tiebreak synthesizer, expensive 3-model panel). When the panel disagrees, the response includes an `escalation` field recommending operator review (the verdict still resolves via majority-vote \u2014 the engine does NOT block). Falls back to 'unimplemented' when engine credentials are absent; the call is always logged.\";\n\ninterface ToolInput {\n readonly diff: string;\n readonly change_type: string;\n readonly summary?: string;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['diff'] !== 'string' || typeof o['change_type'] !== 'string') return false;\n if (o['summary'] !== undefined && typeof o['summary'] !== 'string') return false;\n return true;\n}\n\nfunction isAcceptedChangeType(v: string): v is AcceptedChangeType {\n return (ACCEPTED_CHANGE_TYPES as readonly string[]).includes(v);\n}\n\n/** Build the user prompt \u2014 frames the diff for senior-architect-style review. */\nfunction buildPrompt(\n input: ToolInput,\n changeType: AcceptedChangeType,\n rules: ReadonlyArray<QueryHit>,\n truncated: number,\n): string {\n const summary = input.summary !== undefined ? `Summary: ${input.summary}\\n` : '';\n const rulesBlock = formatRulesForPrompt(rules, truncated);\n return (\n `Senior architecture review (change type: ${changeType}).\\n${summary}` +\n `Review the following diff against architectural defaults: naming conventions, boundary discipline ` +\n `(no leaky abstractions across packages), error-handling shape (typed errors, no swallowed throws), ` +\n `test honesty (assertions verify product truth \u2014 not mocks-of-mocks).\\n\\n` +\n `Respond with verdict 'pass' if the change is sound, 'fail' if it introduces architectural ` +\n `regressions or breaks defaults, or 'uncertain' if context outside the diff is needed.${rulesBlock}\\n\\n` +\n `---DIFF---\\n${input.diff}\\n---END---`\n );\n}\n\nexport async function handleArchitectureReview(\n deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Required: { diff: string, change_type: string }.',\n );\n }\n assertWithinByteCap(TOOL_NAME, 'diff', rawInput.diff, MAX_DIFF_BYTES);\n if (rawInput.summary !== undefined) {\n assertWithinByteCap(TOOL_NAME, 'summary', rawInput.summary, MAX_SUMMARY_BYTES);\n }\n // Normalize change_type \u2014 unknown values fall back to 'other'.\n const changeType: AcceptedChangeType = isAcceptedChangeType(rawInput.change_type)\n ? rawInput.change_type\n : 'other';\n\n const cacheInput = {\n diff: rawInput.diff,\n change_type: changeType,\n ...(rawInput.summary !== undefined ? { summary: rawInput.summary } : {}),\n gate_type: GATE_TYPE,\n };\n const key = deps.cache.keyFor(TOOL_NAME, cacheInput);\n const excerpt = rawInput.diff.slice(0, 300);\n const inputSizeBytes =\n bytesOf(rawInput.diff) + (rawInput.summary !== undefined ? bytesOf(rawInput.summary) : 0);\n\n // Cache hit replay.\n const cached = deps.cache.get<ToolResultEnvelope<ArchitectureReviewPayload>>(key);\n if (cached !== null) {\n const replayEvent: ConsensusCallEvent = {\n ...buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n }),\n per_model_verdicts: cached.value.payload.per_model_verdicts,\n synthesized_verdict: cached.value.payload.synthesized_verdict ?? null,\n consensus_confidence: cached.value.payload.synthesized_verdict?.confidence ?? null,\n consensus_engine_version: cached.value.payload.engine_version ?? null,\n cache_hit: true,\n };\n deps.events.append(replayEvent);\n return jsonContent({ ...cached.value, cache: { hit: true, key } });\n }\n\n // Gate #5 pre-filter \u2014 pull matching rules from the vo-arch-defaults KB.\n // findRulesForDiff catches KB errors but SURFACES them: stderr log fires\n // and `kbResult.error` is non-null so we can flag the envelope.\n // prepareKBRules sorts by severity + caps at MAX_KB_RULES so the prompt\n // stays bounded on wide-touch diffs.\n const kbResult = findRulesForDiff(rawInput.diff);\n const { topRules, truncated: kbTruncated } = prepareKBRules(kbResult.rules);\n\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n });\n\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\n let engineThrew = false;\n try {\n engineResult = await deps.consensus.run({\n gate_type: GATE_TYPE,\n prompt: buildPrompt(rawInput, changeType, topRules, kbTruncated),\n ...(signal !== undefined ? { signal } : {}),\n });\n } catch (err) {\n engineThrew = true;\n const message = err instanceof Error ? err.message : String(err);\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\n }\n\n // 2026-05-25 audit HIGH \u2014 MCP cancellation. See consensus-judgment.ts.\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\n deps.events.append({\n ...baseEvent,\n downstream_outcome: {\n status: 'cancelled',\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\n },\n });\n const e = new Error('Request cancelled by client (notifications/cancelled)');\n e.name = 'AbortError';\n throw e;\n }\n\n if (!engineResult.ok) {\n const payload: ArchitectureReviewPayload = {\n verdict: 'unimplemented',\n reason: engineResult.reason,\n per_model_verdicts: [],\n gate_type: GATE_TYPE,\n ...(engineThrew ? { degraded_mode: true } : {}),\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\n };\n const envelope: ToolResultEnvelope<ArchitectureReviewPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n // Do NOT cache !ok envelopes (HP2-1 hardening). See vo_consensus_judgment\n // for rationale.\n deps.events.append(baseEvent);\n return jsonContent(envelope);\n }\n\n const perModelForEvent = toEventPerModelVerdicts(engineResult.per_model_verdicts);\n const synthForEvent = toEventSynthesizedVerdict(engineResult.synthesized_verdict);\n const enrichedEvent: ConsensusCallEvent = {\n ...baseEvent,\n per_model_verdicts: perModelForEvent,\n synthesized_verdict: synthForEvent,\n consensus_confidence: engineResult.synthesized_verdict.confidence,\n duration_ms: engineResult.duration_ms,\n consensus_engine_version: engineResult.engine_version,\n };\n\n // Escalation: surfaced when the human-tiebreak synthesizer raised the\n // signal (panel disagreement). Falls back to inferring from dissent_summary\n // if the engine variant didn't populate the dedicated field \u2014 keeps the\n // tool robust against engine versions that pre-date the explicit signal.\n const escalationRequired =\n engineResult.escalation_required === true ||\n (engineResult.escalation_required === undefined &&\n engineResult.synthesized_verdict.dissent_summary !== null);\n const escalationReason =\n engineResult.escalation_reason ?? engineResult.synthesized_verdict.dissent_summary ?? '';\n\n const payload: ArchitectureReviewPayload = {\n verdict: engineResult.synthesized_verdict.verdict,\n reason:\n engineResult.synthesized_verdict.dissent_summary ??\n `panel agreed (${engineResult.per_model_verdicts.length} models, engine=${engineResult.engine_version})`,\n per_model_verdicts: perModelForEvent,\n synthesized_verdict: synthForEvent,\n engine_version: engineResult.engine_version,\n degraded: engineResult.degraded,\n gate_type: GATE_TYPE,\n ...(escalationRequired\n ? {\n escalation: {\n required: true as const,\n reason: sanitizeExcerpt(\n escalationReason.length > 0\n ? escalationReason\n : 'panel disagreement \u2014 operator review recommended',\n ),\n },\n }\n : {}),\n ...(kbResult.error !== null ? { kb_unavailable: true as const } : {}),\n ...(kbTruncated > 0 ? { kb_rules_truncated: kbTruncated } : {}),\n };\n const envelope: ToolResultEnvelope<ArchitectureReviewPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n deps.cache.set(key, envelope);\n deps.events.append(enrichedEvent);\n return jsonContent(envelope);\n}\n", "// Zod-validated config loader for the vo-ratchets library.\n//\n// Two surfaces:\n// 1. `resolveConfig(user)` \u2014 apply defaults, validate, return the resolved\n// shape every detector consumes.\n// 2. `loadConfigFromFile(path)` \u2014 read a `vo-ratchets.config.json` (or\n// `.js`/`.mjs` for the future) and resolve it.\n//\n// The defaults intentionally enable every detector. CI-on-first-install is\n// supposed to fail loudly on hollow tests; opt-out is explicit.\n\nimport { readFile } from 'node:fs/promises';\nimport path from 'node:path';\nimport { z } from 'zod';\nimport type {\n RatchetId,\n ResolvedRatchetConfig,\n UserRatchetConfig,\n} from './types.js';\n\nconst RATCHET_IDS: readonly RatchetId[] = [\n 'assertion-strength',\n 'hollow-tests',\n 'verify-answer',\n 'fix-strength',\n 'qa-honesty',\n] as const;\n\nconst ratchetIdSchema = z.enum([\n 'assertion-strength',\n 'hollow-tests',\n 'verify-answer',\n 'fix-strength',\n 'qa-honesty',\n]);\n\nconst thresholdSchema = z.enum(['strict', 'medium', 'permissive']);\n\nconst userConfigSchema = z\n .object({\n // zod v4: z.record with ENUM keys validates exhaustively (every key\n // required); these are user OVERRIDE maps merged over DEFAULT_CONFIG,\n // so partial-by-design \u2014 z.partialRecord restores the v3 semantics.\n enabled: z.partialRecord(ratchetIdSchema, z.boolean()).optional(),\n thresholds: z.partialRecord(ratchetIdSchema, thresholdSchema).optional(),\n allowlist: z\n .object({\n paths: z.array(z.string()).optional(),\n rules: z.array(z.string()).optional(),\n })\n .optional(),\n reportOnly: z.boolean().optional(),\n paths: z.array(z.string()).optional(),\n })\n .strict();\n\nexport const DEFAULT_CONFIG: ResolvedRatchetConfig = {\n enabled: {\n 'assertion-strength': true,\n 'hollow-tests': true,\n 'verify-answer': true,\n 'fix-strength': true,\n 'qa-honesty': true,\n },\n thresholds: {\n 'assertion-strength': 'medium',\n 'hollow-tests': 'medium',\n 'verify-answer': 'medium',\n 'fix-strength': 'medium',\n 'qa-honesty': 'medium',\n },\n allowlist: {\n paths: [],\n rules: [],\n },\n reportOnly: false,\n paths: [],\n};\n\n/**\n * Validate a user-supplied config object and merge it with defaults.\n * Throws a `ZodError` on shape violations \u2014 callers should let it propagate\n * so the operator sees the validation message.\n */\nexport function resolveConfig(\n userConfig: UserRatchetConfig | undefined,\n): ResolvedRatchetConfig {\n const parsed = userConfigSchema.parse(userConfig ?? {});\n const enabled: Record<RatchetId, boolean> = {\n ...DEFAULT_CONFIG.enabled,\n ...(parsed.enabled ?? {}),\n };\n const thresholds: Record<RatchetId, ResolvedRatchetConfig['thresholds'][RatchetId]> = {\n ...DEFAULT_CONFIG.thresholds,\n ...(parsed.thresholds ?? {}),\n };\n const allowlist = {\n paths: parsed.allowlist?.paths ?? [],\n rules: parsed.allowlist?.rules ?? [],\n };\n return {\n enabled,\n thresholds,\n allowlist,\n reportOnly: parsed.reportOnly ?? DEFAULT_CONFIG.reportOnly,\n paths: parsed.paths ?? DEFAULT_CONFIG.paths,\n };\n}\n\n/**\n * Read a JSON config from disk and resolve it. Missing file is not an error\n * \u2014 caller decides how to handle (the CLI treats missing-file as \"use\n * defaults\", explicit `--config` flag promotes it to an error).\n */\nexport async function loadConfigFromFile(\n filePath: string,\n): Promise<ResolvedRatchetConfig> {\n const text = await readFile(filePath, 'utf8');\n const parsed: unknown = JSON.parse(text);\n return resolveConfig(parsed as UserRatchetConfig);\n}\n\n/**\n * Resolve `only=<ratchet>` semantics. When `only` is set, every other\n * detector is force-disabled in the returned config; the `only` detector\n * is forced enabled. Used by the CLI's `--only` flag and the `runRatchets`\n * programmatic API.\n */\nexport function applyOnlyFilter(\n config: ResolvedRatchetConfig,\n only: RatchetId | undefined,\n): ResolvedRatchetConfig {\n if (!only) return config;\n const enabled: Record<RatchetId, boolean> = {\n 'assertion-strength': false,\n 'hollow-tests': false,\n 'verify-answer': false,\n 'fix-strength': false,\n 'qa-honesty': false,\n };\n enabled[only] = true;\n return { ...config, enabled };\n}\n\n/** Resolve a path-like config value against the run's cwd. */\nexport function resolvePathAgainst(cwd: string, target: string): string {\n return path.isAbsolute(target) ? target : path.resolve(cwd, target);\n}\n\nexport const __test = {\n RATCHET_IDS,\n userConfigSchema,\n};\n", "// Generic project file walker.\n//\n// Detectors share this: walk the tree, skip the obvious junk directories,\n// return forward-slash-normalized relative paths so the resulting findings\n// match across Windows and POSIX consumers (the cloud control plane runs on\n// Linux; the desktop MCP runs on Windows).\n\nimport { readdir, readFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nconst SKIP_DIRS = new Set<string>([\n 'node_modules',\n '.git',\n 'dist',\n 'build',\n 'out',\n 'lib',\n '.next',\n '.nuxt',\n '.svelte-kit',\n '.turbo',\n 'coverage',\n '.coverage',\n '.cache',\n '.parcel-cache',\n '.vscode',\n '.idea',\n '.claude',\n '.agent-worktrees',\n 'test-results',\n]);\n\n/** Normalize a path to forward slashes \u2014 Windows-safe. */\nexport function toForwardSlashes(p: string): string {\n return p.replace(/\\\\/g, '/');\n}\n\n/**\n * Walk `cwd` recursively, returning relative paths (forward-slash) that pass\n * the supplied filter. The walker itself skips the `SKIP_DIRS` set; the\n * filter applies on top of that.\n *\n * Yields no directories, just files. Symlinks are not followed \u2014 keeps the\n * walk simple and safe under untrusted-project usage (cloud control plane\n * will scan customer code).\n */\nexport async function walkFiles(\n cwd: string,\n filter: (relativePath: string) => boolean,\n): Promise<string[]> {\n const results: string[] = [];\n await walkDir(cwd, cwd, filter, results);\n results.sort();\n return results;\n}\n\nasync function walkDir(\n root: string,\n current: string,\n filter: (relativePath: string) => boolean,\n results: string[],\n): Promise<void> {\n let entries;\n try {\n entries = await readdir(current, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const absolute = path.join(current, entry.name);\n if (entry.isDirectory()) {\n if (SKIP_DIRS.has(entry.name)) continue;\n await walkDir(root, absolute, filter, results);\n continue;\n }\n if (!entry.isFile()) continue;\n const relative = toForwardSlashes(path.relative(root, absolute));\n if (!filter(relative)) continue;\n results.push(relative);\n }\n}\n\n/**\n * Read a file relative to `cwd` and return its content as UTF-8. Returns\n * an empty string if the file is unreadable \u2014 detectors then treat it as\n * \"no findings\" rather than crashing the run.\n */\nexport async function readRelative(\n cwd: string,\n relativePath: string,\n): Promise<string> {\n try {\n return await readFile(path.resolve(cwd, relativePath), 'utf8');\n } catch {\n return '';\n }\n}\n\n/**\n * Match a path against a list of simple glob-ish patterns. Supports `*`\n * (single segment, except `/`) and `**` (any segments). No brace expansion \u2014\n * keep it predictable.\n */\nexport function pathMatchesAny(\n relativePath: string,\n patterns: readonly string[],\n): boolean {\n for (const pattern of patterns) {\n if (matchGlob(relativePath, pattern)) return true;\n }\n return false;\n}\n\nfunction matchGlob(input: string, pattern: string): boolean {\n const regexSource = globToRegex(pattern);\n return new RegExp(`^${regexSource}$`).test(input);\n}\n\nfunction globToRegex(pattern: string): string {\n // Two-pass: convert `**` to a placeholder, escape other regex metachars,\n // then convert `*` to single-segment wildcard, finally restore `**`.\n const doubleStarToken = '\u0000DOUBLE_STAR\u0000';\n const singleStarToken = '\u0000SINGLE_STAR\u0000';\n let work = pattern.replace(/\\*\\*/g, doubleStarToken).replace(/\\*/g, singleStarToken);\n // CodeQL #194: include `?` (regex metachar \u2014 previously passed through and\n // silently turned `?` glob into the \"0-or-1\" regex quantifier) and `/`\n // (folds the separate line-128 pass back into a single escape step).\n work = work.replace(/[.+?^${}()|[\\]\\\\/]/g, (m) => `\\\\${m}`);\n work = work.replace(new RegExp(singleStarToken, 'g'), '[^/]*');\n work = work.replace(new RegExp(doubleStarToken, 'g'), '.*');\n return work;\n}\n\nexport const __test = {\n SKIP_DIRS,\n globToRegex,\n};\n", "// Shared test-file matchers.\n//\n// Detectors use these to decide whether a given path is a test file at all.\n// Centralized so the regex behavior is consistent across detectors and the\n// CLI integration tests can stub it without redeclaring patterns.\n\nimport { toForwardSlashes } from './walk.js';\n\nconst TEST_FILE_RE = /(?:^|\\/)[^/]+\\.(?:test|spec)\\.(?:ts|tsx|js|jsx|mjs|cjs)$/u;\nconst TEST_TSX_RE = /\\.test\\.tsx$/u;\n\n/** True when `filePath` looks like a Vitest/Jest test file. */\nexport function isTestFile(filePath: string): boolean {\n return TEST_FILE_RE.test(toForwardSlashes(filePath));\n}\n\n/** True when `filePath` is a React-component-style test (matters for hollow-tests Rule b). */\nexport function isComponentTestFile(filePath: string): boolean {\n return TEST_TSX_RE.test(toForwardSlashes(filePath));\n}\n\n/**\n * Mask string and comment content so regex passes don't false-positive on\n * sample code inside docstrings. Returns a string the same length as the\n * input, with quoted/commented characters replaced by spaces (preserving\n * line breaks so line numbers still line up).\n */\nexport function maskStringsAndComments(content: string): string {\n let out = '';\n let quote: string | null = null;\n let escaped = false;\n let lineComment = false;\n let blockComment = false;\n const text = String(content || '');\n\n for (let index = 0; index < text.length; index += 1) {\n const char = text[index] ?? '';\n const next = text[index + 1] ?? '';\n\n if (lineComment) {\n if (char === '\\r' || char === '\\n') {\n out += char;\n lineComment = false;\n } else {\n out += ' ';\n }\n continue;\n }\n if (blockComment) {\n if (char === '*' && next === '/') {\n out += ' ';\n index += 1;\n blockComment = false;\n } else if (char === '\\r' || char === '\\n') {\n out += char;\n } else {\n out += ' ';\n }\n continue;\n }\n if (!quote) {\n if (char === '/' && next === '/') {\n out += ' ';\n index += 1;\n lineComment = true;\n continue;\n }\n if (char === '/' && next === '*') {\n out += ' ';\n index += 1;\n blockComment = true;\n continue;\n }\n if (char === '\"' || char === \"'\" || char === '`') {\n quote = char;\n out += char;\n continue;\n }\n out += char;\n continue;\n }\n // Inside a quoted string.\n if (char === '\\r' || char === '\\n') {\n out += char;\n if (quote !== '`') {\n quote = null;\n escaped = false;\n }\n continue;\n }\n if (escaped) {\n out += ' ';\n escaped = false;\n continue;\n }\n if (char === '\\\\') {\n out += ' ';\n escaped = true;\n continue;\n }\n if (char === quote) {\n out += char;\n quote = null;\n continue;\n }\n out += ' ';\n }\n return out;\n}\n\n/** Count regex matches without mutating the regex's lastIndex state. */\nexport function countMatches(content: string, regex: RegExp): number {\n const re = new RegExp(regex.source, regex.flags);\n return (String(content || '').match(re) || []).length;\n}\n\nexport const __test = {\n TEST_FILE_RE,\n TEST_TSX_RE,\n};\n", "// assertion-strength detector \u2014 flags tests that \"pass\" without verifying\n// the unit under test produces the right answer.\n//\n// Pattern set (per handoff \u00A75.1, ported from Algosuite's\n// scripts/ci/check-assertion-strength-ratchet-core.mjs + test-strength-inventory):\n//\n// trivial-only every expect() matcher in the file is trivial\n// (toBeDefined / toBeTruthy / toBeNull / toBeUndefined /\n// toBe(true|false) / not.toThrow).\n// to-have-been-called `expect(fn).toHaveBeenCalled()` with no\n// `toHaveBeenCalledWith` or value-comparing matcher\n// anywhere in the same test file.\n// not-to-throw-only file's only assertion-style line is\n// `expect(() => fn()).not.toThrow()`.\n// mock-calls-length-only every assertion targets `.mock.calls.length`\n// and nothing else.\n// literal-true `expect(true)` or `assert(true)` \u2014 passes by\n// construction.\n//\n// Allowlist marker (file-scope, anywhere before the first import):\n// // vo-ratchets-allow-assertion-strength: <reason>\n//\n// Eats own dog food: every test of this detector under test/detectors/ uses\n// value-comparing matchers (toEqual / toBe(<value>) / toHaveLength) \u2014 never\n// trivial-only.\n\nimport type {\n Detector,\n DetectorResult,\n DetectorRunInput,\n RatchetFinding,\n RatchetSeverity,\n RatchetThreshold,\n} from '../types.js';\nimport {\n countMatches,\n isTestFile,\n maskStringsAndComments,\n} from '../util/test-files.js';\nimport { readRelative, walkFiles } from '../util/walk.js';\n\nconst RATCHET_ID = 'assertion-strength' as const;\n\nconst ALLOW_MARKER_RE = /\\/\\/\\s*vo-ratchets-allow-assertion-strength\\s*:\\s*\\S/iu;\nconst ANY_MATCHER_RE = /\\.(?:not\\s*\\.\\s*)?to[A-Z]\\w*\\s*\\(/gu;\nconst TRIVIAL_MATCHER_RE = new RegExp(\n '\\\\.(?:toBeDefined|toBeTruthy|toBeFalsy|toBeNull|toBeUndefined)\\\\s*\\\\(\\\\s*\\\\)'\n + '|\\\\.toBe\\\\s*\\\\(\\\\s*(?:true|false)\\\\s*\\\\)'\n + '|\\\\.(?:not\\\\s*\\\\.\\\\s*)?toThrow\\\\s*\\\\(\\\\s*\\\\)',\n 'gu',\n);\nconst TO_HAVE_BEEN_CALLED_RE = /\\.toHaveBeenCalled\\s*\\(\\s*\\)/u;\nconst TO_HAVE_BEEN_CALLED_WITH_RE = /\\.toHaveBeenCalledWith\\s*\\(/u;\nconst MOCK_CALLS_LENGTH_RE = /\\.mock\\.calls\\.length\\b/u;\nconst NOT_TO_THROW_RE = /\\.not\\s*\\.\\s*toThrow\\s*\\(/u;\nconst LITERAL_TRUE_RE = /\\b(?:expect|assert(?:\\.ok)?)\\s*\\(\\s*true\\s*\\)/u;\n\ninterface RuleHit {\n rule: string;\n message: string;\n severity: RatchetSeverity;\n}\n\nexport function hasAllowMarker(content: string): boolean {\n return ALLOW_MARKER_RE.test(content);\n}\n\nexport function inspectFile({\n filePath,\n content,\n threshold,\n}: {\n filePath: string;\n content: string;\n threshold: RatchetThreshold;\n}): RuleHit[] {\n if (!isTestFile(filePath)) return [];\n if (hasAllowMarker(content)) return [];\n\n const masked = maskStringsAndComments(content);\n const hits: RuleHit[] = [];\n\n // literal-true: catches `expect(true)` no matter the threshold.\n if (LITERAL_TRUE_RE.test(masked)) {\n hits.push({\n rule: 'literal-true',\n severity: 'error',\n message:\n 'Found `expect(true)` or `assert(true)`. Literal-true assertions pass '\n + 'by construction and prove no behavior. Replace with a value-comparing '\n + 'matcher against the actual output.',\n });\n }\n\n // mock-calls-length-only: every assertion targets `.mock.calls.length`.\n const totalMatchers = countMatches(masked, ANY_MATCHER_RE);\n if (totalMatchers > 0 && countMockCallsLengthAssertions(masked) === totalMatchers) {\n hits.push({\n rule: 'mock-calls-length-only',\n severity: 'error',\n message:\n 'Every assertion in this file checks `.mock.calls.length`. Verifying '\n + 'a mock was called N times without verifying the arguments or the '\n + 'effect on the unit under test does not prove behavior. Add at '\n + 'least one assertion against the system\\'s actual output.',\n });\n }\n\n // not-to-throw-only: the only assertion is `.not.toThrow()`.\n if (totalMatchers > 0 && isNotToThrowOnly(masked, totalMatchers)) {\n hits.push({\n rule: 'not-to-throw-only',\n severity: 'error',\n message:\n 'The only assertion is `expect(...).not.toThrow()`. \"Doesn\\'t crash\" '\n + 'is a smoke check, not a behavioral test. Verify the return value '\n + 'or side effect, not just the absence of an exception.',\n });\n }\n\n // to-have-been-called without -With: weakest threshold tolerates one\n // bare call followed by separate behavioral assertions; medium and strict\n // require pairing with `toHaveBeenCalledWith` or a value-comparing matcher\n // somewhere in the file.\n if (shouldFlagBareToHaveBeenCalled(masked, threshold)) {\n hits.push({\n rule: 'to-have-been-called',\n severity: threshold === 'permissive' ? 'warning' : 'error',\n message:\n '`toHaveBeenCalled()` appears without any `toHaveBeenCalledWith(...)` '\n + 'or value-comparing assertion in this file. Verifying that a mock '\n + 'fired tells you nothing about whether it was called correctly. '\n + 'Pair with `toHaveBeenCalledWith(...)` or assert on the resulting '\n + 'state.',\n });\n }\n\n // trivial-only: every expect matcher is trivial.\n if (totalMatchers > 0) {\n const trivialMatchers = countMatches(masked, TRIVIAL_MATCHER_RE);\n if (trivialMatchers === totalMatchers && threshold !== 'permissive') {\n hits.push({\n rule: 'trivial-only',\n severity: 'error',\n message:\n `All ${totalMatchers} expect() matcher${totalMatchers === 1 ? '' : 's'} `\n + 'are trivial (toBeDefined / toBeTruthy / toBeFalsy / toBeNull / '\n + 'toBeUndefined / toBe(true|false) / not.toThrow). These prove the '\n + 'file imported and ran but verify no behavior. Add at least one '\n + 'value-comparing matcher: toEqual, toStrictEqual, toBe(<value>), '\n + 'toMatch, toMatchObject, toContain, toHaveLength, toHaveProperty.',\n });\n }\n }\n\n return hits;\n}\n\nfunction countMockCallsLengthAssertions(masked: string): number {\n let count = 0;\n for (const line of masked.split(/\\r?\\n/u)) {\n if (!MOCK_CALLS_LENGTH_RE.test(line)) continue;\n if (!/\\.(?:not\\s*\\.\\s*)?to[A-Z]\\w*\\s*\\(/u.test(line)) continue;\n count += 1;\n }\n return count;\n}\n\nfunction isNotToThrowOnly(masked: string, totalMatchers: number): boolean {\n if (!NOT_TO_THROW_RE.test(masked)) return false;\n const notToThrowCount = countMatches(masked, /\\.not\\s*\\.\\s*toThrow\\s*\\(/gu);\n return notToThrowCount === totalMatchers;\n}\n\nfunction shouldFlagBareToHaveBeenCalled(\n masked: string,\n threshold: RatchetThreshold,\n): boolean {\n if (!TO_HAVE_BEEN_CALLED_RE.test(masked)) return false;\n if (TO_HAVE_BEEN_CALLED_WITH_RE.test(masked)) return false;\n // Value-comparing matcher anywhere in the file is enough proof.\n const totalMatchers = countMatches(masked, ANY_MATCHER_RE);\n const trivialMatchers = countMatches(masked, TRIVIAL_MATCHER_RE);\n const valueComparing = totalMatchers - trivialMatchers\n - countMatches(masked, /\\.toHaveBeenCalled\\s*\\(\\s*\\)/gu);\n if (threshold === 'permissive') return false;\n if (threshold === 'medium' && valueComparing >= 1) return false;\n return true;\n}\n\nexport const assertionStrengthDetector: Detector = {\n id: RATCHET_ID,\n stub: false,\n async run(input: DetectorRunInput): Promise<DetectorResult> {\n const threshold = input.config.thresholds[RATCHET_ID];\n const files = await walkFiles(input.cwd, (rel) => isTestFile(rel));\n const findings: RatchetFinding[] = [];\n for (const file of files) {\n const content = await readRelative(input.cwd, file);\n for (const hit of inspectFile({ filePath: file, content, threshold })) {\n findings.push({\n ratchet: RATCHET_ID,\n rule: hit.rule,\n file,\n severity: hit.severity,\n message: hit.message,\n });\n }\n }\n return {\n ratchet: RATCHET_ID,\n ran: true,\n findings,\n filesScanned: files.length,\n stub: false,\n };\n },\n};\n\nexport const __test = {\n ALLOW_MARKER_RE,\n ANY_MATCHER_RE,\n TRIVIAL_MATCHER_RE,\n TO_HAVE_BEEN_CALLED_RE,\n TO_HAVE_BEEN_CALLED_WITH_RE,\n MOCK_CALLS_LENGTH_RE,\n NOT_TO_THROW_RE,\n LITERAL_TRUE_RE,\n};\n", "// fix-strength detector \u2014 flags fix-PRs whose tests replace strong literal\n// assertions with comparator-only ones.\n//\n// Ported from scripts/ci/check-fix-strength.mjs +\n// scripts/ci/check-verify-answer-core.mjs (the actual source-of-truth lives\n// in `compareStrengthBetweenVersions`). The honest framing:\n//\n// - A \"fix\" PR claims to fix a real bug.\n// - The trivial way for an automated fixer to make a failing test pass is\n// to WEAKEN the assertion (replace `.toBe(14750)` with `.toBeGreaterThan(0)`).\n// - The static gate (`verify-answer`) catches NEW comparator-only lines,\n// but not the deletion of the strong line that was replaced.\n//\n// This detector compares pre and post versions of every changed test\n// file. If pre had strong assertions and post has fewer strong + more\n// weak ones, the PR is flagged.\n//\n// Important honesty note on the port:\n// The stub's preamble described \"checkout parent commit + run test + assert\n// it fails.\" That is NOT what the Algosuite source-of-truth actually does\n// \u2014 the real check is a comparator-counting check that does NOT execute\n// tests. Running tests at two commits would be much more expensive AND\n// would require the detector to know how to invoke the project's test\n// runner. The pattern-counting check captures the actual signal (strong\n// matchers deleted, weak matchers added) without spawning a test runner.\n//\n// Cost note:\n// Even the comparator-counting check needs the pre-version of each changed\n// file. The default implementation uses `git show <baseRef>:<path>` which\n// spawns one `git` process per changed file. For projects with hundreds of\n// changed test files this is non-trivial. Set `disabled: true` in the\n// config or rely on the threshold = 'permissive' downgrade to keep runs\n// fast in interactive (MCP) contexts.\n//\n// Behavior:\n// The detector is OFF unless the config supplies a `gitContext` via a\n// side-channel \u2014 by default `run` walks no files and returns an empty\n// result with `ran: true` and a sentinel diagnostic. Callers that DO want\n// the git-aware behavior should call `inspectChanges(...)` directly (the\n// cloud control plane wires it in this way).\n//\n// Allowlist markers:\n// File-scope (anywhere before the first import):\n// // vo-ratchets-allow-fix-strength: <reason>\n// In either pre or post source (single line within 3 lines of the change):\n// // fix-strength-allow: <reason>\n\nimport { spawnSync } from 'node:child_process';\nimport type {\n Detector,\n DetectorResult,\n DetectorRunInput,\n RatchetFinding,\n RatchetSeverity,\n RatchetThreshold,\n} from '../types.js';\nimport { isTestFile } from '../util/test-files.js';\n\nconst RATCHET_ID = 'fix-strength' as const;\n\nconst FILE_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-fix-strength\\s*:\\s*\\S/iu;\nconst ALLOW_MARKER_RE = /\\b(?:fix-strength-allow|verify-answer-allow)\\s*:\\s*\\S/iu;\n\n// Strong matchers: pin a specific expected value or run through a\n// `verify*`-family helper that has its own pinning contract.\nconst DEFAULT_STRONG_PATTERNS: ReadonlyArray<RegExp> = [\n /\\.toBe\\s*\\(\\s*(?:-?\\d+(?:\\.\\d+)?|true|false|null|undefined|\"[^\"\\n]*\"|'[^'\\n]*'|`[^`\\n]*`)/u,\n /\\.toEqual\\s*\\(\\s*(?:-?\\d+(?:\\.\\d+)?|true|false|null|undefined|\"[^\"\\n]*\"|'[^'\\n]*'|`[^`\\n]*`|\\[|\\{)/u,\n /\\.toStrictEqual\\s*\\(/u,\n /\\.toBeCloseTo\\s*\\(/u,\n /\\.toMatchObject\\s*\\(/u,\n /\\.toMatchInlineSnapshot\\s*\\(/u,\n];\n\n// Weak matchers: prove the system returned something but don't pin a value.\nconst DEFAULT_WEAK_PATTERNS: ReadonlyArray<RegExp> = [\n /\\.toBeGreaterThan\\s*\\(/u,\n /\\.toBeGreaterThanOrEqual\\s*\\(/u,\n /\\.toBeLessThan\\s*\\(/u,\n /\\.toBeLessThanOrEqual\\s*\\(/u,\n /\\.toBeTruthy\\s*\\(\\s*\\)/u,\n /\\.toBeFalsy\\s*\\(\\s*\\)/u,\n];\n\n/** Counts of strong / weak matchers in a file version. */\nexport interface VersionCounts {\n strong: number;\n weak: number;\n}\n\nexport type Verdict =\n | 'ok'\n | 'no-strong-pre'\n | 'strong-removed'\n | 'strength-decreased';\n\nexport interface FileComparison {\n filePath: string;\n verdict: Verdict;\n pre: VersionCounts;\n post: VersionCounts;\n reason?: string;\n}\n\nexport interface ExtraPatternConfig {\n /** Bare identifier helpers that count as strong (e.g. `verifyAnswer`). */\n extraStrongHelpers?: readonly string[];\n /** Extra regex patterns that count as strong. */\n extraStrongPatterns?: readonly RegExp[];\n /** Extra regex patterns that count as weak. */\n extraWeakPatterns?: readonly RegExp[];\n}\n\nfunction buildHelperPatterns(names: readonly string[]): RegExp[] {\n const result: RegExp[] = [];\n for (const name of names) {\n if (!/^[A-Za-z_$][\\w$]*$/u.test(name)) continue;\n result.push(new RegExp(`\\\\b${name}\\\\s*\\\\(`, 'u'));\n }\n return result;\n}\n\nfunction countMatchesInSource(\n source: string,\n patterns: readonly RegExp[],\n): number {\n if (typeof source !== 'string' || !source) return 0;\n let total = 0;\n const lines = source.split(/\\r?\\n/u);\n for (const line of lines) {\n const stripped = line.trim();\n if (!stripped) continue;\n if (stripped.startsWith('//') || stripped.startsWith('*')) continue;\n for (const re of patterns) {\n const matches = line.match(new RegExp(re.source, `${re.flags}g`));\n if (matches) total += matches.length;\n }\n }\n return total;\n}\n\nexport function countStrong(\n source: string,\n extras: ExtraPatternConfig = {},\n): number {\n const patterns = [\n ...DEFAULT_STRONG_PATTERNS,\n ...buildHelperPatterns(extras.extraStrongHelpers ?? []),\n ...(extras.extraStrongPatterns ?? []),\n ];\n return countMatchesInSource(source, patterns);\n}\n\nexport function countWeak(\n source: string,\n extras: ExtraPatternConfig = {},\n): number {\n const patterns = [\n ...DEFAULT_WEAK_PATTERNS,\n ...(extras.extraWeakPatterns ?? []),\n ];\n return countMatchesInSource(source, patterns);\n}\n\nexport interface CompareInput extends ExtraPatternConfig {\n filePath: string;\n preSource: string;\n postSource: string;\n}\n\n/**\n * Compare strong/weak assertion counts between pre- and post-fix versions\n * of a single file. Pure function \u2014 no I/O.\n */\nexport function compareStrengthBetweenVersions(\n input: CompareInput,\n): FileComparison {\n const { filePath, preSource, postSource } = input;\n const preStrong = countStrong(preSource, input);\n const preWeak = countWeak(preSource, input);\n const postStrong = countStrong(postSource, input);\n const postWeak = countWeak(postSource, input);\n const pre: VersionCounts = { strong: preStrong, weak: preWeak };\n const post: VersionCounts = { strong: postStrong, weak: postWeak };\n\n if (FILE_ALLOW_RE.test(preSource) || FILE_ALLOW_RE.test(postSource)) {\n return { filePath, verdict: 'ok', pre, post, reason: 'file-allow-marker' };\n }\n const sources = `${preSource ?? ''}\\n${postSource ?? ''}`;\n if (ALLOW_MARKER_RE.test(sources)) {\n return { filePath, verdict: 'ok', pre, post, reason: 'line-allow-marker' };\n }\n\n if (preStrong === 0) {\n return { filePath, verdict: 'no-strong-pre', pre, post };\n }\n const strongLost = preStrong > postStrong;\n const weakRose = postWeak > preWeak;\n if (strongLost && weakRose) {\n return { filePath, verdict: 'strength-decreased', pre, post };\n }\n if (strongLost && !weakRose) {\n return { filePath, verdict: 'strong-removed', pre, post };\n }\n return { filePath, verdict: 'ok', pre, post };\n}\n\nexport interface InspectChangesInput extends ExtraPatternConfig {\n /** Changed files (relative paths, forward-slashed). */\n changedFiles: readonly string[];\n /** Async reader returning the source at the base ref. Empty string if missing. */\n readBase(filePath: string): Promise<string>;\n /** Async reader returning the source at the head ref. */\n readHead(filePath: string): Promise<string>;\n /** Threshold \u2014 controls severity of `strong-removed`. */\n threshold: RatchetThreshold;\n}\n\ninterface InspectionFinding {\n rule: string;\n file: string;\n severity: RatchetSeverity;\n message: string;\n}\n\n/**\n * Run the comparator on every changed file and produce findings. The caller\n * supplies the file list and the per-ref readers \u2014 `run` (below) shows the\n * default `git show` wiring.\n */\nexport async function inspectChanges(\n input: InspectChangesInput,\n): Promise<InspectionFinding[]> {\n const findings: InspectionFinding[] = [];\n for (const file of input.changedFiles) {\n if (!isTestFile(file)) continue;\n const [pre, post] = await Promise.all([\n input.readBase(file),\n input.readHead(file),\n ]);\n const verdict = compareStrengthBetweenVersions({\n filePath: file,\n preSource: pre,\n postSource: post,\n // Conditional-spread the optional `extra*` overrides so\n // exactOptionalPropertyTypes is satisfied (undefined isn't assignable\n // to `readonly T[]` when the property is non-optional in the target).\n ...(input.extraStrongHelpers !== undefined ? { extraStrongHelpers: input.extraStrongHelpers } : {}),\n ...(input.extraStrongPatterns !== undefined ? { extraStrongPatterns: input.extraStrongPatterns } : {}),\n ...(input.extraWeakPatterns !== undefined ? { extraWeakPatterns: input.extraWeakPatterns } : {}),\n });\n if (verdict.verdict === 'strength-decreased') {\n findings.push({\n rule: 'strength-decreased',\n file,\n severity: input.threshold === 'permissive' ? 'warning' : 'error',\n message:\n `${file} weakened: pre had ${verdict.pre.strong} strong / `\n + `${verdict.pre.weak} weak; post has ${verdict.post.strong} strong / `\n + `${verdict.post.weak} weak. A fix must not replace strong literal-pinned `\n + 'assertions with comparator-only ones. Restore the strong assertion, '\n + 'pin a new expected value, or add `// fix-strength-allow: <reason>`.',\n });\n continue;\n }\n if (verdict.verdict === 'strong-removed') {\n findings.push({\n rule: 'strong-removed',\n file,\n severity: input.threshold === 'strict' ? 'error' : 'warning',\n message:\n `${file} dropped strong assertion(s) without compensation: pre `\n + `${verdict.pre.strong} -> post ${verdict.post.strong}. If this `\n + 'was intentional (test moved/split, fixture corrected), add '\n + '`// fix-strength-allow: <reason>`.',\n });\n }\n }\n return findings;\n}\n\n// ---------------------------------------------------------------------------\n// Default `run` implementation \u2014 git-aware. Off unless the caller passes\n// `extras['fix-strength']` config or env vars to point at the base/head refs.\n// ---------------------------------------------------------------------------\n\nfunction readGitFile(cwd: string, ref: string, filePath: string): string {\n const result = spawnSync('git', ['show', `${ref}:${filePath}`], {\n cwd,\n encoding: 'utf8',\n maxBuffer: 16 * 1024 * 1024,\n });\n if (result.status !== 0) return '';\n return result.stdout || '';\n}\n\nfunction listChangedFiles(cwd: string, baseRef: string, headRef: string): string[] {\n const result = spawnSync(\n 'git',\n ['diff', '--name-only', '--diff-filter=AMR', `${baseRef}...${headRef}`],\n { cwd, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 },\n );\n if (result.status !== 0) return [];\n return (result.stdout || '')\n .split(/\\r?\\n/u)\n .map((line) => line.trim())\n .filter(Boolean);\n}\n\nexport const fixStrengthDetector: Detector = {\n id: RATCHET_ID,\n stub: false,\n async run(input: DetectorRunInput): Promise<DetectorResult> {\n const threshold = input.config.thresholds[RATCHET_ID];\n // Off-by-default per the cost note: git checkout-equivalent work is\n // expensive enough that we require explicit opt-in via env vars. Other\n // callers can call `inspectChanges` directly.\n const baseRef = process.env['VO_RATCHETS_FIX_STRENGTH_BASE'] || '';\n const headRef = process.env['VO_RATCHETS_FIX_STRENGTH_HEAD'] || '';\n if (!baseRef || !headRef) {\n return {\n ratchet: RATCHET_ID,\n ran: false,\n findings: [],\n filesScanned: 0,\n stub: false,\n };\n }\n const changedFiles = listChangedFiles(input.cwd, baseRef, headRef);\n const findings = await inspectChanges({\n changedFiles,\n readBase: async (file) => readGitFile(input.cwd, baseRef, file),\n readHead: async (file) => readGitFile(input.cwd, headRef, file),\n threshold,\n });\n const ratchetFindings: RatchetFinding[] = findings.map((f) => ({\n ratchet: RATCHET_ID,\n rule: f.rule,\n file: f.file,\n severity: f.severity,\n message: f.message,\n }));\n return {\n ratchet: RATCHET_ID,\n ran: true,\n findings: ratchetFindings,\n filesScanned: changedFiles.filter(isTestFile).length,\n stub: false,\n };\n },\n};\n\nexport const __test = {\n FILE_ALLOW_RE,\n ALLOW_MARKER_RE,\n DEFAULT_STRONG_PATTERNS,\n DEFAULT_WEAK_PATTERNS,\n};\n", "// hollow-tests detector \u2014 flags test files that exist for coverage credit\n// but verify nothing meaningful.\n//\n// Patterns (per handoff \u00A75.2, ported from Algosuite's check-hollow-tests-core.mjs):\n//\n// empty-test-body `it('foo', () => {})` \u2014 body has no assertions\n// and no mock setup.\n// it-count-floor file has fewer than 4 it()/test() blocks\n// (skip/todo excluded unless allow-marked).\n// Strictness controls the floor: strict=4,\n// medium=3, permissive=1.\n// no-interaction `.test.tsx` file without userEvent / fireEvent /\n// `await user.*` \u2014 component test that never\n// clicks anything. Strictness gates this rule:\n// permissive turns it off.\n// swallowing-catch body wrapped in try/catch where the catch\n// returns true or swallows silently.\n//\n// Allowlist marker (file-scope, on a line above the first import):\n// // vo-ratchets-allow-hollow-tests: <reason>\n//\n// Allowlist marker (skip-scope, immediately above an .it.skip / .test.skip /\n// .it.todo / .test.todo line):\n// // vo-ratchets-allow-hollow-tests-skip: <reason>\n\nimport type {\n Detector,\n DetectorResult,\n DetectorRunInput,\n RatchetFinding,\n RatchetSeverity,\n RatchetThreshold,\n} from '../types.js';\nimport {\n countMatches,\n isComponentTestFile,\n isTestFile,\n maskStringsAndComments,\n} from '../util/test-files.js';\nimport { readRelative, walkFiles } from '../util/walk.js';\n\nconst RATCHET_ID = 'hollow-tests' as const;\n\nconst FILE_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-hollow-tests\\s*:\\s*\\S/iu;\nconst SKIP_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-hollow-tests-skip\\s*:\\s*\\S/iu;\n\nconst IT_COUNTING_RE = /(?<![.\\w])(?:it|test)(?:\\.only)?\\s*\\(/gu;\nconst IT_SKIP_RE = /(?<![.\\w])(?:it|test)\\s*\\.\\s*(?:skip|todo)\\s*\\(/u;\nconst USER_EVENT_RE = /\\buserEvent\\s*\\.\\s*\\w+\\s*\\(/u;\nconst FIRE_EVENT_RE = /\\bfireEvent\\s*\\.\\s*\\w+\\s*\\(/u;\nconst AWAIT_USER_RE = /\\bawait\\s+user\\s*\\.\\s*\\w+\\s*\\(/u;\nconst ANY_MATCHER_RE = /\\.(?:not\\s*\\.\\s*)?to[A-Z]\\w*\\s*\\(/gu;\nconst SWALLOWING_CATCH_RE = /catch\\s*(?:\\([^)]*\\))?\\s*\\{\\s*(?:\\/\\/[^\\n]*\\n\\s*)?\\}/u;\nconst CATCH_RETURN_TRUE_RE = /catch\\s*(?:\\([^)]*\\))?\\s*\\{\\s*return\\s+true\\s*;?\\s*\\}/u;\nconst EMPTY_IT_RE =\n /(?<![.\\w])(?:it|test)\\s*\\(\\s*['\"`][^'\"`\\n]+['\"`]\\s*,\\s*(?:async\\s*)?(?:\\(\\s*\\)\\s*=>|function\\s*\\(\\s*\\))\\s*\\{\\s*\\}\\s*\\)/gu;\n\ninterface RuleHit {\n rule: string;\n message: string;\n severity: RatchetSeverity;\n}\n\nconst FLOOR_BY_THRESHOLD: Record<RatchetThreshold, number> = {\n strict: 4,\n medium: 3,\n permissive: 1,\n};\n\nexport function hasFileScopeAllowMarker(content: string): boolean {\n const lines = String(content || '').split(/\\r?\\n/u);\n let firstImport = -1;\n for (let index = 0; index < lines.length; index += 1) {\n if (/^\\s*import\\s/u.test(lines[index] || '')) {\n firstImport = index;\n break;\n }\n }\n const cap = firstImport >= 0 ? firstImport : Math.min(20, lines.length - 1);\n for (let index = 0; index <= cap; index += 1) {\n if (FILE_ALLOW_RE.test(lines[index] || '')) return true;\n }\n return false;\n}\n\nexport function countAllowedSkips(content: string): number {\n const lines = String(content || '').split(/\\r?\\n/u);\n let count = 0;\n for (let index = 0; index < lines.length; index += 1) {\n if (!IT_SKIP_RE.test(lines[index] || '')) continue;\n const previous = lines[index - 1] || '';\n if (SKIP_ALLOW_RE.test(previous)) count += 1;\n }\n return count;\n}\n\nexport function inspectFile({\n filePath,\n content,\n threshold,\n}: {\n filePath: string;\n content: string;\n threshold: RatchetThreshold;\n}): RuleHit[] {\n if (!isTestFile(filePath)) return [];\n if (hasFileScopeAllowMarker(content)) return [];\n\n const masked = maskStringsAndComments(content);\n const hits: RuleHit[] = [];\n\n // empty-test-body \u2014 surface BEFORE the count floor so empty stubs are\n // called out specifically.\n const emptyBodyCount = countMatches(masked, EMPTY_IT_RE);\n if (emptyBodyCount > 0) {\n hits.push({\n rule: 'empty-test-body',\n severity: 'error',\n message:\n `Found ${emptyBodyCount} it()/test() block${emptyBodyCount === 1 ? '' : 's'} `\n + 'with an empty body. An empty test passes by construction and verifies '\n + 'nothing. Either implement the test or remove the placeholder; if you '\n + 'genuinely want a planned-but-not-yet-written marker, use `.todo()`.',\n });\n }\n\n // it-count-floor \u2014 strictness-gated.\n const floor = FLOOR_BY_THRESHOLD[threshold];\n const itCount = countMatches(masked, IT_COUNTING_RE);\n const allowedSkipCount = countAllowedSkips(content);\n const effectiveCount = itCount + allowedSkipCount;\n if (effectiveCount < floor && itCount + allowedSkipCount > 0) {\n const skipNote = allowedSkipCount > 0\n ? ` (+ ${allowedSkipCount} allow-marked skip${allowedSkipCount === 1 ? '' : 's'})`\n : '';\n hits.push({\n rule: 'it-count-floor',\n severity: threshold === 'strict' ? 'error' : 'warning',\n message:\n `Found ${itCount} it()/test() block${itCount === 1 ? '' : 's'}${skipNote}. `\n + `Threshold (${threshold}) requires ${floor}+ tests, including at least `\n + 'one interaction case and one edge/error case. To exempt this file, '\n + 'add `// vo-ratchets-allow-hollow-tests: <why>` above the first import.',\n });\n }\n\n // no-interaction \u2014 only for .test.tsx, and only at strict/medium.\n if (isComponentTestFile(filePath) && threshold !== 'permissive') {\n const hasInteraction =\n USER_EVENT_RE.test(masked)\n || FIRE_EVENT_RE.test(masked)\n || AWAIT_USER_RE.test(masked);\n const hasAnyMatcher = countMatches(masked, ANY_MATCHER_RE) > 0;\n if (!hasInteraction && hasAnyMatcher) {\n hits.push({\n rule: 'no-interaction',\n severity: 'warning',\n message:\n '.test.tsx file has no userEvent / fireEvent / `await user.*` call. '\n + 'Component tests must verify behavior, not just render. Use '\n + '`const user = userEvent.setup()` then `await user.click(...)`, '\n + 'or `fireEvent.X(...)` for events userEvent does not support.',\n });\n }\n }\n\n // swallowing-catch \u2014 wraps body in try/catch and swallows or returns true.\n if (\n SWALLOWING_CATCH_RE.test(masked)\n || CATCH_RETURN_TRUE_RE.test(masked)\n ) {\n hits.push({\n rule: 'swallowing-catch',\n severity: 'error',\n message:\n 'Test body contains an empty `catch {}` or `catch { return true; }`. '\n + 'Swallowing exceptions turns \"the system threw\" into a passing test. '\n + 'Either assert on the expected error (`expect(() => ...).toThrow(...)`)'\n + ' or let the exception bubble.',\n });\n }\n\n return hits;\n}\n\nexport const hollowTestsDetector: Detector = {\n id: RATCHET_ID,\n stub: false,\n async run(input: DetectorRunInput): Promise<DetectorResult> {\n const threshold = input.config.thresholds[RATCHET_ID];\n const files = await walkFiles(input.cwd, (rel) => isTestFile(rel));\n const findings: RatchetFinding[] = [];\n for (const file of files) {\n const content = await readRelative(input.cwd, file);\n for (const hit of inspectFile({ filePath: file, content, threshold })) {\n findings.push({\n ratchet: RATCHET_ID,\n rule: hit.rule,\n file,\n severity: hit.severity,\n message: hit.message,\n });\n }\n }\n return {\n ratchet: RATCHET_ID,\n ran: true,\n findings,\n filesScanned: files.length,\n stub: false,\n };\n },\n};\n\nexport const __test = {\n FILE_ALLOW_RE,\n SKIP_ALLOW_RE,\n IT_COUNTING_RE,\n IT_SKIP_RE,\n USER_EVENT_RE,\n FIRE_EVENT_RE,\n AWAIT_USER_RE,\n ANY_MATCHER_RE,\n SWALLOWING_CATCH_RE,\n CATCH_RETURN_TRUE_RE,\n EMPTY_IT_RE,\n FLOOR_BY_THRESHOLD,\n};\n", "// qa-honesty detector \u2014 flags tests that \"pass on weak evidence.\"\n//\n// Ported from scripts/ci/check-vo-qa-honesty-core.mjs (the catch-all rule\n// pack \u2014 patterns that don't fit cleanly under assertion-strength or\n// hollow-tests but represent real institutional pain). Per handoff \u00A75.5.\n//\n// What the source-of-truth is hand-tuned for:\n// The Algosuite implementation hard-coded a few things that the generic\n// port deliberately strips:\n// a. The file glob limited inspection to `scripts/virtual-office/qa/testers/`.\n// Generic port: file selection is project-agnostic \u2014 any `.test.*` /\n// `.spec.*` file plus any `.mjs` file matching `tester` in its name\n// (the Algosuite tester convention). Strict consumers should override\n// with explicit `paths` config.\n// b. The allow-marker classes were Algosuite-internal symptoms\n// (`vo-qa-allow-permission-denied`, `vo-qa-allow-quota`, etc.).\n// Generic port: the marker family stays \u2014 the words \"PERMISSION_DENIED\"\n// and \"quota\" are generic CS terms, not Algosuite-specific. Consumers\n// without those error codes simply won't trigger the rules.\n// c. The \"verify-named-resource\" rule that whitelisted Algosuite-specific\n// resource names (`classId`, `studentId`, `pollId`, etc.) is dropped.\n// It was a denylist of failure modes specific to Algosuite's data\n// model; reproducing it generically would either be too broad\n// (every variable name) or too narrow (a single project's nouns).\n//\n// Rules ported (each fires once per offending line):\n//\n// reporter-pass-without-evidence test calls `reporter.pass(...)` with no\n// prior assertion / readback in scope.\n// catch-return-true catch block where the error is logged\n// or swallowed and `return true` follows\n// \u2014 INVALID_ARGUMENT errors become\n// passing tests.\n// render-text-only React render() followed only by\n// `toBeInTheDocument` / `toHaveTextContent`\n// and no user interaction. (gated by\n// componentTest file class.)\n// fire-event-without-user-event `fireEvent.X(...)` with no `userEvent`\n// anywhere in the same file.\n// loose-truthy-result `expect(result).toBeTruthy()` where\n// `result` was just assigned from a\n// callable under test \u2014 proves the\n// callable returned something, not the\n// right something.\n// invalid-argument-pass-risk catch path or guard branch where\n// INVALID_ARGUMENT errors are paired\n// with `return true`.\n// snapshot-heavy file whose ONLY assertions are\n// `toMatchSnapshot` / `toMatchInlineSnapshot`.\n//\n// Allowlist marker (file-scope, before the first import):\n// // vo-ratchets-allow-qa-honesty: <reason>\n//\n// Allowlist marker (line-scope, on the line or up to 3 above):\n// // qa-honesty-allow: <reason>\n// // vo-qa-allow-<class>: <reason> (Algosuite-compatible)\n//\n// Edge cases handled:\n// - `*.smoke.test.tsx` files are exempt from render-text-only (intentional\n// smoke tests; matches Algosuite source-of-truth behavior).\n// - Snapshot-heavy is suppressed if any value-comparing matcher exists\n// anywhere in the file.\n\nimport type {\n Detector,\n DetectorResult,\n DetectorRunInput,\n RatchetFinding,\n RatchetSeverity,\n RatchetThreshold,\n} from '../types.js';\nimport {\n isComponentTestFile,\n isTestFile,\n maskStringsAndComments,\n} from '../util/test-files.js';\nimport { readRelative, walkFiles } from '../util/walk.js';\n\nconst RATCHET_ID = 'qa-honesty' as const;\n\n/**\n * Linear-time predicate equivalent to the former /tester[^/]*\\.mjs$/u regex.\n * Used by `inspectFile` (single-file check) and the detector's directory\n * walker (rel-path filter). CodeQL #195 + #196 flagged the regex as ReDoS-\n * prone because `[^/]*` followed by an anchored `\\.mjs$` suffix backtracks\n * quadratically on long non-`.mjs` filenames. Pure string ops are linear\n * and produce exactly the same result for this specific predicate.\n */\nfunction isTesterMjsPath(filePath: string): boolean {\n const lc = filePath.toLowerCase();\n if (!lc.endsWith('.mjs')) return false;\n const slash = lc.lastIndexOf('/');\n const basename = slash >= 0 ? lc.slice(slash + 1) : lc;\n return basename.includes('tester');\n}\n\n// File-scope and line-scope allow markers.\nconst FILE_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-qa-honesty\\s*:\\s*\\S/iu;\nconst LINE_ALLOW_GENERIC_RE = /\\b(?:qa-honesty-allow|vo-qa-allow-[a-z0-9-]+)\\s*:\\s*\\S/iu;\n\n// Rule patterns.\nconst REPORTER_PASS_RE = /\\breporter\\s*\\.\\s*pass\\s*\\(/u;\nconst ASSERTION_RE = /\\b(?:expect|assert(?:\\.ok|Slow|Quality)?)\\s*\\(/u;\n// CodeQL #197: original pattern had nested quantifiers\n// (`(?:[^{}\\n]*\\n?\\s*)*`) \u2014 `\\s*` inside the outer `*` is the classic\n// exponential-backtracking shape. The replacement uses a single lazy\n// `[^{}]*?` (no inner quantifier, no nesting) that still matches across\n// newlines because `[^{}]` does not exclude `\\n`. It stops at the next\n// brace, so a real catch block returning `true` between `{` and `}` is\n// matched; non-matching blocks fail in linear time.\nconst CATCH_RETURN_TRUE_RE =\n /catch\\s*(?:\\([^)]*\\))?\\s*\\{[^{}]*?\\breturn\\s+true\\b/u;\nconst RENDER_RE = /\\brender\\s*\\(/u;\nconst USER_EVENT_RE = /\\buserEvent\\s*\\.\\s*\\w+\\s*\\(/u;\nconst FIRE_EVENT_RE = /\\bfireEvent\\s*\\.\\s*\\w+\\s*\\(/u;\nconst TO_BE_IN_DOCUMENT_RE = /\\.toBeInTheDocument\\s*\\(\\s*\\)/u;\nconst TO_HAVE_TEXT_CONTENT_RE = /\\.toHaveTextContent\\s*\\(/u;\nconst TRUTHY_RE = /\\bexpect\\s*\\(\\s*([A-Za-z_$][\\w$]*)\\s*\\)\\s*\\.\\s*toBeTruthy\\s*\\(\\s*\\)/u;\nconst CALLABLE_RESULT_ASSIGN_RE =\n /\\b(?:const|let|var)\\s+([A-Za-z_$][\\w$]*)\\s*=\\s*await\\s+[A-Za-z_$][\\w$.]*\\s*\\(/u;\nconst INVALID_ARGUMENT_RETURN_TRUE_RE = /\\bINVALID_ARGUMENT\\b[^;\\n]*return\\s+true/iu;\nconst SNAPSHOT_MATCHER_RE = /\\.(?:toMatchSnapshot|toMatchInlineSnapshot)\\s*\\(/u;\nconst VALUE_COMPARING_MATCHER_RE =\n /\\.(?:toBe|toEqual|toStrictEqual|toBeCloseTo|toContain|toHaveLength|toMatch|toMatchObject|toHaveProperty)\\s*\\(/u;\nconst SMOKE_TEST_RE = /\\.smoke\\.test\\.(?:ts|tsx|js|jsx|mjs|cjs)$/u;\n\ninterface RuleHit {\n rule: string;\n message: string;\n severity: RatchetSeverity;\n line?: number;\n}\n\nexport interface InspectFileOptions {\n filePath: string;\n content: string;\n threshold: RatchetThreshold;\n}\n\nexport function hasFileAllowMarker(content: string): boolean {\n return FILE_ALLOW_RE.test(content);\n}\n\nexport function hasLineAllowMarkerNearby(\n lines: readonly string[],\n index: number,\n): boolean {\n const start = Math.max(0, index - 3);\n for (let cursor = start; cursor <= index; cursor += 1) {\n if (LINE_ALLOW_GENERIC_RE.test(lines[cursor] ?? '')) return true;\n }\n return false;\n}\n\nexport function inspectFile({\n filePath,\n content,\n threshold,\n}: InspectFileOptions): RuleHit[] {\n // qa-honesty also applies to Tier 3 sentinel `.mjs` files that aren't\n // *.test.ts. We accept any test file OR any `.mjs` whose name contains\n // `tester` (a common project convention for Tier 3 driver files).\n // CodeQL #195: replaced /tester[^/]*\\.mjs$/ \u2014 the `[^/]*` followed by an\n // anchored suffix can backtrack quadratically on long non-`.mjs` filenames.\n // String ops are linear and equivalent for this exact predicate.\n const isTesterMjs = isTesterMjsPath(filePath);\n if (!isTestFile(filePath) && !isTesterMjs) return [];\n if (hasFileAllowMarker(content)) return [];\n\n const masked = maskStringsAndComments(content);\n const maskedLines = masked.split(/\\r?\\n/u);\n const rawLines = content.split(/\\r?\\n/u);\n const hits: RuleHit[] = [];\n\n // reporter-pass-without-evidence \u2014 flagged once per `reporter.pass(...)` line\n // when no `expect(` or `assert(` appears in the file at all.\n const fileHasAssertion = ASSERTION_RE.test(masked);\n for (let i = 0; i < maskedLines.length; i += 1) {\n const maskedLine = maskedLines[i] ?? '';\n if (!REPORTER_PASS_RE.test(maskedLine)) continue;\n if (fileHasAssertion) continue;\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\n hits.push({\n rule: 'reporter-pass-without-evidence',\n severity: 'error',\n line: i + 1,\n message:\n '`reporter.pass(...)` called but no `expect(...)` / `assert(...)` appears '\n + 'anywhere in this file. Reporting success without proving it is a fake '\n + 'green. Add at least one value-comparing assertion before the pass call.',\n });\n }\n\n // catch-return-true \u2014 entire-file regex (multi-line shape).\n if (CATCH_RETURN_TRUE_RE.test(masked)) {\n // Locate the first line that contains the `return true` to give a useful\n // line number.\n let firstHit: number | undefined;\n for (let i = 0; i < maskedLines.length; i += 1) {\n if (/\\breturn\\s+true\\b/u.test(maskedLines[i] ?? '')) {\n firstHit = i + 1;\n break;\n }\n }\n const hitLine = firstHit ?? 1;\n if (firstHit === undefined || !hasLineAllowMarkerNearby(rawLines, firstHit - 1)) {\n hits.push({\n rule: 'catch-return-true',\n severity: 'error',\n line: hitLine,\n message:\n 'A catch block returns `true`. Swallowing exceptions turns errors '\n + '(including INVALID_ARGUMENT) into passing tests. Either assert on '\n + 'the expected error or let the exception bubble. To exempt this '\n + 'block, add `// qa-honesty-allow: <reason>`.',\n });\n }\n }\n\n // render-text-only \u2014 only for component test files (`.test.tsx`), and only\n // when threshold !== 'permissive'. Also skip `*.smoke.test.tsx`.\n if (\n isComponentTestFile(filePath)\n && !SMOKE_TEST_RE.test(filePath)\n && threshold !== 'permissive'\n ) {\n const hasRender = RENDER_RE.test(masked);\n const hasInteraction = USER_EVENT_RE.test(masked) || FIRE_EVENT_RE.test(masked);\n const onlyTextMatchers =\n (TO_BE_IN_DOCUMENT_RE.test(masked) || TO_HAVE_TEXT_CONTENT_RE.test(masked))\n && !VALUE_COMPARING_MATCHER_RE.test(masked);\n if (hasRender && !hasInteraction && onlyTextMatchers) {\n hits.push({\n rule: 'render-text-only',\n severity: 'warning',\n message:\n 'render() followed by `toBeInTheDocument` / `toHaveTextContent` and '\n + 'nothing else \u2014 Tier-1 smoke parading as Tier-2 verification. Either '\n + 'add user interaction (`await user.click(...)`) and a value-comparing '\n + 'matcher, or rename the file to `*.smoke.test.tsx` to mark it as '\n + 'intentional smoke.',\n });\n }\n }\n\n // fire-event-without-user-event \u2014 fireEvent.X(...) with no userEvent.\n if (FIRE_EVENT_RE.test(masked) && !USER_EVENT_RE.test(masked)) {\n // Locate first fireEvent line.\n let firstLine: number | undefined;\n for (let i = 0; i < maskedLines.length; i += 1) {\n if (FIRE_EVENT_RE.test(maskedLines[i] ?? '')) {\n firstLine = i + 1;\n break;\n }\n }\n if (\n firstLine === undefined\n || !hasLineAllowMarkerNearby(rawLines, firstLine - 1)\n ) {\n hits.push({\n rule: 'fire-event-without-user-event',\n severity: threshold === 'strict' ? 'error' : 'warning',\n ...(firstLine ? { line: firstLine } : {}),\n message:\n '`fireEvent.X(...)` used without any `userEvent.X(...)` in the same '\n + 'file. userEvent models real human interaction (focus, debounce, '\n + 'pointer events); fireEvent short-circuits that. Prefer userEvent '\n + 'unless the event genuinely cannot be modeled (in which case add '\n + 'a `// qa-honesty-allow: <reason>` marker).',\n });\n }\n }\n\n // loose-truthy-result \u2014 `expect(result).toBeTruthy()` where `result` was\n // assigned from an awaited call earlier in the file.\n const callableVars = new Set<string>();\n for (const line of maskedLines) {\n const match = CALLABLE_RESULT_ASSIGN_RE.exec(line);\n if (match) callableVars.add(match[1] ?? '');\n }\n for (let i = 0; i < maskedLines.length; i += 1) {\n const maskedLine = maskedLines[i] ?? '';\n const truthyMatch = TRUTHY_RE.exec(maskedLine);\n if (!truthyMatch) continue;\n const variable = truthyMatch[1];\n if (!variable || !callableVars.has(variable)) continue;\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\n hits.push({\n rule: 'loose-truthy-result',\n severity: 'error',\n line: i + 1,\n message:\n `\\`expect(${variable}).toBeTruthy()\\` where \\`${variable}\\` was assigned `\n + 'from an awaited call. Truthy proves the callable returned something, '\n + 'not the right something. Assert on specific fields or values from '\n + 'the result.',\n });\n }\n\n // invalid-argument-pass-risk \u2014 `INVALID_ARGUMENT ... return true` patterns.\n // Hybrid approach: maskStringsAndComments() blanks out string-literal\n // contents (so `'INVALID_ARGUMENT'` is gone from maskedLines), but we need\n // the literal as the search signal. Read rawLines but STRIP line-comments\n // (everything from `//` onward) before matching, so a comment that\n // mentions both INVALID_ARGUMENT and return true doesn't fire (see test\n // \"does not flag a file that has comments containing INVALID_ARGUMENT\").\n // Block-comment edge cases are not handled here; switch to per-line\n // masked + raw-string side-channel if a real false-positive emerges.\n for (let i = 0; i < rawLines.length; i += 1) {\n const rawLine = rawLines[i] ?? '';\n const slashIdx = rawLine.indexOf('//');\n const codeOnly = slashIdx >= 0 ? rawLine.slice(0, slashIdx) : rawLine;\n if (!INVALID_ARGUMENT_RETURN_TRUE_RE.test(codeOnly)) continue;\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\n hits.push({\n rule: 'invalid-argument-pass-risk',\n severity: 'error',\n line: i + 1,\n message:\n 'INVALID_ARGUMENT error path returns `true`. This turns a validation '\n + 'failure into a passing test \u2014 the system rejected the call but '\n + 'the test claims pass. Assert on the rejection explicitly.',\n });\n }\n\n // snapshot-heavy \u2014 file's only assertions are snapshot matchers.\n const snapshotMatchers = (masked.match(/\\.(?:toMatchSnapshot|toMatchInlineSnapshot)\\s*\\(/gu) || []).length;\n const valueMatchers = (masked.match(/\\.(?:toBe|toEqual|toStrictEqual|toBeCloseTo|toContain|toHaveLength|toMatch|toMatchObject|toHaveProperty)\\s*\\(/gu) || []).length;\n if (\n snapshotMatchers > 0\n && valueMatchers === 0\n && threshold !== 'permissive'\n ) {\n hits.push({\n rule: 'snapshot-heavy',\n severity: 'warning',\n message:\n `Every assertion in this file is a snapshot (${snapshotMatchers} `\n + 'toMatchSnapshot / toMatchInlineSnapshot). Snapshots detect drift '\n + 'but rarely prove the verified product answer. Pair with at least '\n + 'one value-comparing matcher (toEqual / toBe / toMatchObject).',\n });\n }\n\n return hits;\n}\n\nexport const qaHonestyDetector: Detector = {\n id: RATCHET_ID,\n stub: false,\n async run(input: DetectorRunInput): Promise<DetectorResult> {\n const threshold = input.config.thresholds[RATCHET_ID];\n const files = await walkFiles(input.cwd, (rel) => {\n if (isTestFile(rel)) return true;\n // tester *.mjs files (Tier 3 sentinel convention). CodeQL #196: same\n // ReDoS-prone pattern as #195 \u2014 replaced with linear string ops.\n return isTesterMjsPath(rel);\n });\n const findings: RatchetFinding[] = [];\n for (const file of files) {\n const content = await readRelative(input.cwd, file);\n for (const hit of inspectFile({ filePath: file, content, threshold })) {\n findings.push({\n ratchet: RATCHET_ID,\n rule: hit.rule,\n file,\n severity: hit.severity,\n message: hit.message,\n ...(typeof hit.line === 'number' ? { line: hit.line } : {}),\n });\n }\n }\n return {\n ratchet: RATCHET_ID,\n ran: true,\n findings,\n filesScanned: files.length,\n stub: false,\n };\n },\n};\n\nexport const __test = {\n FILE_ALLOW_RE,\n LINE_ALLOW_GENERIC_RE,\n REPORTER_PASS_RE,\n CATCH_RETURN_TRUE_RE,\n RENDER_RE,\n USER_EVENT_RE,\n FIRE_EVENT_RE,\n TO_BE_IN_DOCUMENT_RE,\n TRUTHY_RE,\n CALLABLE_RESULT_ASSIGN_RE,\n INVALID_ARGUMENT_RETURN_TRUE_RE,\n SNAPSHOT_MATCHER_RE,\n VALUE_COMPARING_MATCHER_RE,\n};\n", "// verify-answer detector \u2014 flags Tier 2/3 assertions that don't pin an\n// expected value.\n//\n// Ported from scripts/ci/check-verify-answer-core.mjs (the source-of-truth\n// in the Algosuite tree). The \"honest\" view per handoff \u00A75.3 is: a test\n// claims to verify the product produced the right answer. Comparator-only\n// matchers (`expect(x).toBeGreaterThan(0)`, `expect(x).toBeTruthy()`) prove\n// the system returned SOMETHING but don't carry the right answer, so when\n// they fail the orchestrator/fixer has no expected value to compare against.\n//\n// Rule set (each one fires once per offending line):\n//\n// comparator-only a `.toBeGreaterThan(...)`, `.toBeLessThan(...)`,\n// `.toBeTruthy()`, `.toBeFalsy()` (and the\n// ...OrEqual variants) appears without ANY pinning\n// matcher (`.toBe(<lit>)`, `.toEqual(<lit>)`,\n// `.toMatchObject(...)`, `.toStrictEqual(...)`,\n// `.toBeCloseTo(...)`, `.toMatchInlineSnapshot(...)`)\n// or `verify*()` family helper on the same line.\n// call-succeeded-only a test body contains `await <expr>()` followed\n// by `expect(true).toBe(true)` or no assertion at\n// all \u2014 claims verification but only proves the\n// call didn't throw. (Per handoff \u00A75.3 \u2014 this is\n// the strongest \"honest claim, weak evidence\" signal.)\n//\n// Pinning matchers and verifyAnswer-style helpers are configurable via\n// `extraPinningHelpers` (the Algosuite tree adds `verifyAnswer`, `verifyMatch`,\n// `gradeAIOutput`, etc. \u2014 generic projects ship with the base set only).\n//\n// Allowlist marker (file-scope, anywhere before the first import):\n// // vo-ratchets-allow-verify-answer: <reason>\n//\n// Allowlist marker (single line, on or up to 3 lines above the offending line):\n// // verify-answer-allow: <reason>\n\nimport type {\n Detector,\n DetectorResult,\n DetectorRunInput,\n RatchetFinding,\n RatchetSeverity,\n RatchetThreshold,\n} from '../types.js';\nimport { isTestFile, maskStringsAndComments } from '../util/test-files.js';\nimport { readRelative, walkFiles } from '../util/walk.js';\n\nconst RATCHET_ID = 'verify-answer' as const;\n\nconst FILE_ALLOW_RE = /\\/\\/\\s*vo-ratchets-allow-verify-answer\\s*:\\s*\\S/iu;\nconst LINE_ALLOW_RE = /\\bverify-answer-allow\\s*:\\s*\\S/iu;\n\nconst COMPARATOR_PATTERNS: ReadonlyArray<{ re: RegExp; name: string }> = [\n { re: /\\.toBeGreaterThan\\s*\\(/u, name: 'toBeGreaterThan' },\n { re: /\\.toBeGreaterThanOrEqual\\s*\\(/u, name: 'toBeGreaterThanOrEqual' },\n { re: /\\.toBeLessThan\\s*\\(/u, name: 'toBeLessThan' },\n { re: /\\.toBeLessThanOrEqual\\s*\\(/u, name: 'toBeLessThanOrEqual' },\n { re: /\\.toBeTruthy\\s*\\(\\s*\\)/u, name: 'toBeTruthy' },\n { re: /\\.toBeFalsy\\s*\\(\\s*\\)/u, name: 'toBeFalsy' },\n];\n\n// Pinning matchers \u2014 a comparator-only finding is suppressed if any of these\n// appears on the same line (chained matcher / multi-expectation per line).\nconst DEFAULT_PINNING_PATTERNS: ReadonlyArray<RegExp> = [\n /\\.toBe\\s*\\(\\s*(?:-?\\d+(?:\\.\\d+)?|true|false|null|undefined|\"[^\"\\n]*\"|'[^'\\n]*'|`[^`\\n]*`)/u,\n /\\.toEqual\\s*\\(\\s*(?:-?\\d+(?:\\.\\d+)?|true|false|null|undefined|\"[^\"\\n]*\"|'[^'\\n]*'|`[^`\\n]*`|\\[|\\{)/u,\n /\\.toStrictEqual\\s*\\(/u,\n /\\.toBeCloseTo\\s*\\(/u,\n /\\.toMatchObject\\s*\\(/u,\n /\\.toMatchInlineSnapshot\\s*\\(/u,\n];\n\n// The \"call-succeeded-only\" rule's defining shape: an `expect(true).toBe(true)`\n// or `assert(true)` line appears in a file that ALSO contains at least one\n// `await ...(...)` callsite \u2014 the test executed something but verifies nothing.\nconst LITERAL_TRUE_ASSERT_RE = /\\b(?:expect|assert(?:\\.ok)?)\\s*\\(\\s*true\\s*\\)\\s*(?:\\.\\s*toBe\\s*\\(\\s*true\\s*\\)\\s*)?/u;\nconst AWAIT_CALL_RE = /\\bawait\\s+[A-Za-z_$][\\w$.]*\\s*\\(/u;\n\ninterface RuleHit {\n rule: string;\n message: string;\n severity: RatchetSeverity;\n line?: number;\n}\n\nexport interface InspectFileOptions {\n filePath: string;\n content: string;\n threshold: RatchetThreshold;\n /**\n * Extra helper names that count as pinning the expected value (e.g.\n * Algosuite's `verifyAnswer`, `gradeAIOutput`). Each entry is a bare\n * identifier; the detector compiles `\\b<name>\\s*\\(` for matching.\n */\n extraPinningHelpers?: readonly string[];\n}\n\nexport function hasFileAllowMarker(content: string): boolean {\n return FILE_ALLOW_RE.test(content);\n}\n\nfunction lineHasPinning(\n line: string,\n extraHelpers: readonly string[],\n): boolean {\n for (const pattern of DEFAULT_PINNING_PATTERNS) {\n if (pattern.test(line)) return true;\n }\n for (const helper of extraHelpers) {\n // Defensive: skip malformed identifiers so a user-supplied bad value\n // doesn't blow up regex compilation.\n if (!/^[A-Za-z_$][\\w$]*$/u.test(helper)) continue;\n if (new RegExp(`\\\\b${helper}\\\\s*\\\\(`, 'u').test(line)) return true;\n }\n return false;\n}\n\nfunction hasLineAllowMarkerNearby(lines: readonly string[], index: number): boolean {\n const start = Math.max(0, index - 3);\n for (let cursor = start; cursor <= index; cursor += 1) {\n if (LINE_ALLOW_RE.test(lines[cursor] ?? '')) return true;\n }\n return false;\n}\n\nexport function inspectFile({\n filePath,\n content,\n threshold,\n extraPinningHelpers = [],\n}: InspectFileOptions): RuleHit[] {\n if (!isTestFile(filePath)) return [];\n if (hasFileAllowMarker(content)) return [];\n\n const hits: RuleHit[] = [];\n const masked = maskStringsAndComments(content);\n const maskedLines = masked.split(/\\r?\\n/u);\n const rawLines = content.split(/\\r?\\n/u);\n\n // comparator-only: scan line by line.\n for (let i = 0; i < maskedLines.length; i += 1) {\n const maskedLine = maskedLines[i] ?? '';\n if (!maskedLine.trim()) continue;\n let matchedName: string | null = null;\n for (const { re, name } of COMPARATOR_PATTERNS) {\n if (re.test(maskedLine)) {\n matchedName = name;\n break;\n }\n }\n if (!matchedName) continue;\n if (lineHasPinning(maskedLine, extraPinningHelpers)) continue;\n // Allow-marker is checked against raw lines (markers live inside comments\n // that the mask strips).\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\n\n hits.push({\n rule: 'comparator-only',\n severity: threshold === 'permissive' ? 'warning' : 'error',\n line: i + 1,\n message:\n `Comparator-only matcher \\`.${matchedName}\\` does not pin an expected value. `\n + 'A failure here can\\'t tell the orchestrator the right answer. Use '\n + '`.toBe(<literal>)`, `.toEqual(<literal>)`, `.toBeCloseTo(<value>)`, or '\n + '`.toMatchObject(...)` instead. To exempt this line, add '\n + '`// verify-answer-allow: <reason>` on the line above.',\n });\n }\n\n // call-succeeded-only: any `expect(true).toBe(true)` / `assert(true)` style\n // line in a file that also issues a call. Fires once per offending line.\n const fileHasAwaitCall = AWAIT_CALL_RE.test(masked);\n if (fileHasAwaitCall && threshold !== 'permissive') {\n for (let i = 0; i < maskedLines.length; i += 1) {\n const maskedLine = maskedLines[i] ?? '';\n if (!LITERAL_TRUE_ASSERT_RE.test(maskedLine)) continue;\n if (hasLineAllowMarkerNearby(rawLines, i)) continue;\n hits.push({\n rule: 'call-succeeded-only',\n severity: 'error',\n line: i + 1,\n message:\n 'Found `expect(true).toBe(true)` / `assert(true)` in a file that '\n + 'issues a call. The test proves the callable invocation didn\\'t '\n + 'throw \u2014 not that it produced the right answer. Replace with a '\n + 'value-comparing matcher against the actual output.',\n });\n }\n }\n\n return hits;\n}\n\nexport const verifyAnswerDetector: Detector = {\n id: RATCHET_ID,\n stub: false,\n async run(input: DetectorRunInput): Promise<DetectorResult> {\n const threshold = input.config.thresholds[RATCHET_ID];\n const files = await walkFiles(input.cwd, (rel) => isTestFile(rel));\n const findings: RatchetFinding[] = [];\n for (const file of files) {\n const content = await readRelative(input.cwd, file);\n for (const hit of inspectFile({ filePath: file, content, threshold })) {\n findings.push({\n ratchet: RATCHET_ID,\n rule: hit.rule,\n file,\n severity: hit.severity,\n message: hit.message,\n ...(typeof hit.line === 'number' ? { line: hit.line } : {}),\n });\n }\n }\n return {\n ratchet: RATCHET_ID,\n ran: true,\n findings,\n filesScanned: files.length,\n stub: false,\n };\n },\n};\n\nexport const __test = {\n FILE_ALLOW_RE,\n LINE_ALLOW_RE,\n COMPARATOR_PATTERNS,\n DEFAULT_PINNING_PATTERNS,\n LITERAL_TRUE_ASSERT_RE,\n AWAIT_CALL_RE,\n};\n", "// Detector registry. Order matches the `RatchetId` enum so iteration is stable.\n\nimport type { Detector } from '../types.js';\nimport { assertionStrengthDetector } from './assertion-strength.js';\nimport { fixStrengthDetector } from './fix-strength.js';\nimport { hollowTestsDetector } from './hollow-tests.js';\nimport { qaHonestyDetector } from './qa-honesty.js';\nimport { verifyAnswerDetector } from './verify-answer.js';\n\nexport const ALL_DETECTORS: readonly Detector[] = [\n assertionStrengthDetector,\n hollowTestsDetector,\n verifyAnswerDetector,\n fixStrengthDetector,\n qaHonestyDetector,\n] as const;\n\nexport {\n assertionStrengthDetector,\n fixStrengthDetector,\n hollowTestsDetector,\n qaHonestyDetector,\n verifyAnswerDetector,\n};\n", "// runRatchets \u2014 the programmatic API entry point.\n//\n// Orchestrates: config resolution -> detector dispatch -> allowlist filter ->\n// report assembly. This is the single function the cloud control plane,\n// MCP server, and CLI all call.\n\nimport { applyOnlyFilter, resolveConfig } from './config.js';\nimport { ALL_DETECTORS } from './detectors/index.js';\nimport type {\n DetectorResult,\n RatchetFinding,\n RatchetReport,\n RunRatchetsOptions,\n} from './types.js';\nimport { pathMatchesAny } from './util/walk.js';\n\n/**\n * Run every enabled ratchet against the project at `options.cwd`. Returns a\n * `RatchetReport` whose `failed` flag is true iff at least one\n * non-allowlisted `error`-severity finding remains and `reportOnly` is false.\n *\n * Never throws on detector-internal errors \u2014 those surface as `findings`\n * with severity `error`, not as exceptions. Caller-shape errors (bad\n * config) DO throw \u2014 see `resolveConfig`.\n */\nexport async function runRatchets(\n options: RunRatchetsOptions,\n): Promise<RatchetReport> {\n const startedAtMs = Date.now();\n const startedAt = new Date(startedAtMs).toISOString();\n const resolved = applyOnlyFilter(resolveConfig(options.config), options.only);\n\n const detectorResults: DetectorResult[] = [];\n for (const detector of ALL_DETECTORS) {\n if (!resolved.enabled[detector.id]) {\n detectorResults.push({\n ratchet: detector.id,\n ran: false,\n findings: [],\n filesScanned: 0,\n stub: detector.stub,\n });\n continue;\n }\n const result = await detector.run({ cwd: options.cwd, config: resolved });\n detectorResults.push(result);\n }\n\n const allFindings: RatchetFinding[] = detectorResults.flatMap((r) => r.findings);\n const { kept, suppressed } = partitionByAllowlist(allFindings, resolved.allowlist);\n\n const hasErrorFinding = kept.some((f) => f.severity === 'error');\n const failed = !resolved.reportOnly && hasErrorFinding;\n\n return {\n startedAt,\n durationMs: Date.now() - startedAtMs,\n cwd: options.cwd,\n config: resolved,\n detectors: detectorResults,\n findings: kept,\n suppressedFindings: suppressed,\n failed,\n };\n}\n\nfunction partitionByAllowlist(\n findings: readonly RatchetFinding[],\n allowlist: { paths: readonly string[]; rules: readonly string[] },\n): { kept: RatchetFinding[]; suppressed: RatchetFinding[] } {\n const kept: RatchetFinding[] = [];\n const suppressed: RatchetFinding[] = [];\n for (const finding of findings) {\n if (pathMatchesAny(finding.file, allowlist.paths)) {\n suppressed.push(finding);\n continue;\n }\n if (allowlist.rules.includes(finding.rule)) {\n suppressed.push(finding);\n continue;\n }\n if (allowlist.rules.includes(`${finding.ratchet}:${finding.rule}`)) {\n suppressed.push(finding);\n continue;\n }\n kept.push(finding);\n }\n return { kept, suppressed };\n}\n\nexport const __test = {\n partitionByAllowlist,\n};\n", "/**\n * Tool: `vo_check_ratchets`\n *\n * Runs the `@algosuite/vo-ratchets` library against a project directory and\n * returns the deterministic detector report. Wraps `runRatchets` \u2014 the\n * generic, project-agnostic CI ratchet runner (assertion-strength,\n * hollow-tests, verify-answer, fix-strength, qa-honesty).\n *\n * Plan PR #11 (vo-next-agent-plan-2026-05-23 \u00A711.3) \u2014 Wave 2 prerequisite.\n * First MCP tool that closes the loop operator \u2192 MCP \u2192 vo-ratchets moat\n * library. The three existing static-ratchet tools\n * (`vo_check_assertion_strength`, `vo_check_hollow_test`, `vo_verify_answer`)\n * still call the stub ratchet client + consensus engine respectively; a\n * follow-up PR (#11.5) will migrate them to call into vo-ratchets too.\n *\n * Contract:\n * input: { cwd?: string, only?: RatchetId, report_only?: boolean, paths?: string[] }\n * output: ToolResultEnvelope<CheckRatchetsPayload>\n *\n * Behavior:\n * 1. Validate input shape and resolve cwd default (process.cwd()).\n * 2. Call `deps.ratchetsRunner({ cwd, config, only })`.\n * 3. Project the resulting RatchetReport into the open-shell payload.\n * 4. Write one consensus-call event (gate #7) with gate_type='ratchet'.\n *\n * Cache: deliberately bypassed. RatchetReport is a function of the\n * FILESYSTEM at `cwd` \u2014 input hash alone does not uniquely determine the\n * output. Returning cached results would mask file changes between calls.\n * The event log still records every invocation for the dataset moat.\n */\nimport { runRatchets } from '@algosuite/vo-ratchets';\nimport type {\n CheckRatchetsPayload,\n ToolDeps,\n ToolResultEnvelope,\n} from '../types.js';\nimport {\n buildBaseEvent,\n bytesOf,\n invalidParams,\n jsonContent,\n type ToolInputSchema,\n} from './common.js';\n\nexport const TOOL_NAME = 'vo_check_ratchets';\n\n/** Static-ratchet tool \u2014 not consensus-engine routed. */\nconst GATE_TYPE = 'ratchet' as const;\n\n/** Canonical detector IDs (mirrors vo-ratchets `RatchetId` \u2014 keep in sync). */\nconst ALL_RATCHET_IDS = [\n 'assertion-strength',\n 'hollow-tests',\n 'verify-answer',\n 'fix-strength',\n 'qa-honesty',\n] as const;\ntype RatchetIdLocal = (typeof ALL_RATCHET_IDS)[number];\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n cwd: {\n type: 'string',\n description:\n 'Absolute path the ratchet run resolves files against. Defaults to the MCP server process cwd (the directory the operator launched it from \u2014 typically the project root).',\n },\n only: {\n type: 'string',\n enum: [...ALL_RATCHET_IDS],\n description:\n 'Restrict the run to a single detector. Equivalent to disabling every other detector. Omit to run all enabled detectors.',\n },\n report_only: {\n type: 'boolean',\n description:\n 'When true, the run never reports `failed: true` even on error-severity findings. Useful for diagnostic scans; do NOT set in CI gates.',\n },\n paths: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Optional list of file globs / paths to scan. When omitted, each detector walks the project and applies its own file filters.',\n },\n },\n additionalProperties: false,\n};\n\nexport const description =\n \"Runs the vo-ratchets library against a project directory and returns the deterministic detector report (assertion-strength, hollow-tests, verify-answer, fix-strength, qa-honesty). Use BEFORE accepting generated tests or fixes to catch hollow assertions, weak verification, and other product-truth bypass patterns. Output includes per-detector findings (file, line, severity, message), suppressed findings (allowlist hits), and a pass/fail signal. Use `only` to scope to one detector for speed. Cache is bypassed \u2014 output reflects the live filesystem state at `cwd`.\";\n\ninterface ToolInput {\n readonly cwd?: string;\n readonly only?: RatchetIdLocal;\n readonly report_only?: boolean;\n readonly paths?: readonly string[];\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (o['cwd'] !== undefined && typeof o['cwd'] !== 'string') return false;\n if (o['only'] !== undefined) {\n if (typeof o['only'] !== 'string') return false;\n if (!ALL_RATCHET_IDS.includes(o['only'] as RatchetIdLocal)) return false;\n }\n if (o['report_only'] !== undefined && typeof o['report_only'] !== 'boolean') return false;\n if (o['paths'] !== undefined) {\n if (!Array.isArray(o['paths'])) return false;\n if (!o['paths'].every((p) => typeof p === 'string')) return false;\n }\n return true;\n}\n\nexport async function handleCheckRatchets(\n deps: ToolDeps,\n rawInput: unknown,\n // Accepted for ToolHandler signature parity. vo-ratchets runs are file-IO\n // bound and do not currently honor AbortSignal mid-walk; if a long run\n // needs cancellation, follow-up work would thread `signal` into the\n // runner's detector loop. Marked underscore-unused for eslint.\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n `invalid input. All fields optional. Shape: { cwd?: string, only?: '${ALL_RATCHET_IDS.join(\"' | '\")}', report_only?: boolean, paths?: string[] }.`,\n );\n }\n\n const cwd = rawInput.cwd ?? process.cwd();\n const key = deps.cache.keyFor(TOOL_NAME, rawInput);\n // Event excerpt: a stable single-line summary of the request shape.\n const excerpt = JSON.stringify({\n cwd,\n only: rawInput.only ?? null,\n report_only: rawInput.report_only ?? false,\n paths_count: rawInput.paths?.length ?? 0,\n }).slice(0, 300);\n const inputSizeBytes = bytesOf(JSON.stringify(rawInput));\n\n // Build the UserRatchetConfig from the optional input fields. Omit\n // properties entirely (rather than passing `undefined`) so vo-ratchets'\n // default merging behavior applies cleanly.\n const config: {\n reportOnly?: boolean;\n paths?: readonly string[];\n } = {};\n if (rawInput.report_only !== undefined) config.reportOnly = rawInput.report_only;\n if (rawInput.paths !== undefined) config.paths = rawInput.paths;\n\n const report = await runRatchets({\n cwd,\n ...(Object.keys(config).length > 0 ? { config: config as never } : {}),\n ...(rawInput.only !== undefined ? { only: rawInput.only } : {}),\n });\n\n const payload: CheckRatchetsPayload = {\n failed: report.failed,\n started_at: report.startedAt,\n duration_ms: report.durationMs,\n cwd: report.cwd,\n detectors: report.detectors.map((d) => ({\n ratchet: d.ratchet,\n ran: d.ran,\n files_scanned: d.filesScanned,\n stub: d.stub,\n finding_count: d.findings.length,\n })),\n findings: report.findings.map((f) => ({\n ratchet: f.ratchet,\n rule: f.rule,\n file: f.file,\n severity: f.severity,\n message: f.message,\n ...(f.line !== undefined ? { line: f.line } : {}),\n })),\n suppressed_findings: report.suppressedFindings.map((f) => ({\n ratchet: f.ratchet,\n rule: f.rule,\n file: f.file,\n severity: f.severity,\n message: f.message,\n ...(f.line !== undefined ? { line: f.line } : {}),\n })),\n summary: buildSummary(report),\n };\n\n const envelope: ToolResultEnvelope<CheckRatchetsPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n\n deps.events.append(\n buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n }),\n );\n\n return jsonContent(envelope);\n}\n\nfunction buildSummary(report: {\n failed: boolean;\n detectors: readonly { ratchet: string; ran: boolean; findings: readonly unknown[] }[];\n findings: readonly { severity: string }[];\n suppressedFindings: readonly unknown[];\n}): string {\n const ranCount = report.detectors.filter((d) => d.ran).length;\n const errorCount = report.findings.filter((f) => f.severity === 'error').length;\n const warnCount = report.findings.filter((f) => f.severity === 'warning').length;\n const infoCount = report.findings.filter((f) => f.severity === 'info').length;\n const verdict = report.failed ? 'fail' : 'pass';\n return (\n `verdict=${verdict} detectors_ran=${ranCount}/${report.detectors.length} ` +\n `findings=error:${errorCount} warning:${warnCount} info:${infoCount} ` +\n `suppressed=${report.suppressedFindings.length}`\n );\n}\n", "/**\n * Tool: `vo_decompose_dispatch`\n *\n * Per-tier dispatch decomposition (Plan PR #14, vo-next-agent-plan-2026-05-23 \u00A76\n * Phase 2 / \u00A711.3 Wave 2). Takes a customer goal, returns a structured JSON\n * dispatch plan that breaks the work into bounded tiers (research / build /\n * verify / ship) with token cost estimates + kill switches + artifacts.\n *\n * Implemented as a specialization of the consensus-judgment pattern:\n * 1. Embed a fixed system prompt (the `decompose-by-subagent-budget`\n * doctrine \u2014 Plan PR #7's skill text inlined here so PR #14 stays\n * independent of PR #7 per the plan's parallel-PR matrix).\n * 2. Call the consensus engine with gate_type='plan-review' so each\n * model in the panel produces its own dispatch plan.\n * 3. Parse the synthesized verdict's reasoning text as JSON; if it\n * parses, surface the structured plan in the payload. Surface ALL\n * per-model plan texts too so the operator can audit divergence.\n * 4. Emit one V1 gate #7 event with gate_type='plan-review' (the engine\n * path) \u2014 consistent with consensus-judgment.\n *\n * Contract:\n * input: { customer_goal: string, context?: Record<string, unknown> }\n * output: ToolResultEnvelope<DecomposeDispatchPayload>\n *\n * Cache:\n * Cached on (system_prompt_version, customer_goal, context). Cache HIT\n * short-circuits engine. The system prompt is versioned (constant below)\n * so doctrine updates flip the cache.\n */\nimport type {\n ToolDeps,\n ToolResultEnvelope,\n DecomposeDispatchPayload,\n DispatchPlan,\n ConsensusCallEvent,\n} from '../types.js';\nimport {\n assertWithinByteCap,\n buildBaseEvent,\n bytesOf,\n invalidParams,\n jsonContent,\n toEventPerModelVerdicts,\n toEventSynthesizedVerdict,\n type ToolInputSchema,\n} from './common.js';\n\nexport const TOOL_NAME = 'vo_decompose_dispatch';\n\n/** Forced gate type \u2014 this tool always routes to plan-review. */\nconst GATE_TYPE = 'plan-review' as const;\n\n/** 32 KB customer-goal cap (tighter than consensus-judgment's 64 KB \u2014 goals\n * are short by design; large pastes belong in `context`). */\nconst MAX_GOAL_BYTES = 32 * 1024;\n/** 256 KB context cap (matches consensus-judgment). */\nconst MAX_CONTEXT_BYTES = 256 * 1024;\n\n/**\n * System prompt \u2014 the `decompose-by-subagent-budget` doctrine inlined.\n *\n * Versioned: bumping the suffix flips cache keys so doctrine updates take\n * effect on the next call without manual cache eviction.\n */\nconst SYSTEM_PROMPT_VERSION = 'v1' as const;\n\nconst SYSTEM_PROMPT = `You are a dispatch architect for an AI agent fleet operated by a non-coder founder. Given a customer goal, produce a STRUCTURED JSON dispatch plan that decomposes the work into bounded tiers and surfaces the cost shape BEFORE execution begins.\n\nTiers (use only those that apply; omit irrelevant phases):\n - research: subagents read code/docs and produce a finding report\n - build: subagents implement changes\n - verify: subagents run tests / smoke / consensus checks\n - ship: a single agent commits + PRs + merges + deploys\n\nFor each phase you include, specify:\n - subagent_count how many parallel finite tasks vs single-threaded (1)\n - model_recommendation model id (claude-opus-4, claude-sonnet-4, gpt-4o, etc.)\n - estimated_tokens_in integer best-guess input tokens for the phase\n - estimated_tokens_out integer best-guess output tokens for the phase\n - estimated_cost_usd USD number (or the string \"unknown\" if you cannot estimate)\n - kill_switch one-sentence condition that should HALT this phase\n - artifacts array of file paths / report names the phase produces\n\nOutput STRICT JSON conforming to this schema (no markdown fences, no prose outside the JSON):\n\n{\n \"summary\": \"<one-sentence framing of the overall work>\",\n \"phases\": [\n {\n \"name\": \"research\" | \"build\" | \"verify\" | \"ship\",\n \"subagent_count\": <integer >= 1>,\n \"model_recommendation\": \"<model-id>\",\n \"estimated_tokens_in\": <integer>,\n \"estimated_tokens_out\": <integer>,\n \"estimated_cost_usd\": <number> | \"unknown\",\n \"kill_switch\": \"<condition that should halt this phase>\",\n \"artifacts\": [\"<file or report this phase produces>\"]\n }\n ],\n \"total_estimated_tokens\": <integer> | \"unknown\",\n \"total_estimated_cost_usd\": <number> | \"unknown\",\n \"risks\": [\"<risk + suggested mitigation>\"],\n \"operator_decision_points\": [\"<question the operator should answer before next phase starts>\"]\n}\n\nHonesty rules (NON-NEGOTIABLE):\n - If you cannot estimate a token cost, return the string \"unknown\" \u2014 do NOT bluff a number.\n - If a phase requires consensus verification, list it as an artifact dependency on \\`vo_consensus_judgment\\`, not as work-itself.\n - The plan is a PROPOSAL, not a commitment \u2014 the operator approves each tier separately.\n - Surface at least one operator_decision_point unless the goal is trivially scoped.\n - Surface at least one risk + mitigation. If you genuinely see no risk, say \"no significant risk identified; primary failure mode is cost overrun\" and explain why.\n - Do NOT pad the plan with phases that add no value. A 1-file change may only need a build phase.`;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n customer_goal: {\n type: 'string',\n description:\n \"The customer's goal statement \u2014 what they want accomplished. Keep concise; large supporting material belongs in `context`.\",\n },\n context: {\n type: 'object',\n description:\n 'Optional context: relevant file paths, current state observations, constraints, prior decisions, deadlines. Forwarded to the engine as caller_context.',\n additionalProperties: true,\n },\n },\n required: ['customer_goal'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Decomposes a customer goal into a structured dispatch plan (research / build / verify / ship tiers) with subagent counts, model recommendations, token cost estimates, kill switches, and per-tier artifacts. Runs the consensus engine with gate_type='plan-review' so multiple models each propose a plan, then surfaces the synthesized JSON plan AND each model's raw plan for operator audit. Use BEFORE starting any non-trivial work to make the cost shape visible up front. Operator approves each tier separately; the plan is a proposal, not a commitment.\";\n\ninterface ToolInput {\n readonly customer_goal: string;\n readonly context?: Record<string, unknown>;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['customer_goal'] !== 'string') return false;\n if (o['context'] !== undefined && (typeof o['context'] !== 'object' || o['context'] === null)) {\n return false;\n }\n return true;\n}\n\n/**\n * Try to parse a model's response text as the dispatch-plan JSON. The\n * prompt forbids markdown fences but defensively strip them if present.\n * Returns null on any parse failure \u2014 the caller surfaces the failure as\n * `parse_error` rather than blowing up.\n */\nfunction tryParseDispatchPlan(text: string): DispatchPlan | null {\n const trimmed = text.trim();\n if (trimmed.length === 0) return null;\n\n // Strip ```json \u2026 ``` fences defensively.\n let body = trimmed;\n const fenceMatch = body.match(/^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/u);\n if (fenceMatch && fenceMatch[1]) body = fenceMatch[1].trim();\n\n // Find the first { and last } \u2014 handles models that prepend a sentence.\n const firstBrace = body.indexOf('{');\n const lastBrace = body.lastIndexOf('}');\n if (firstBrace === -1 || lastBrace === -1 || lastBrace < firstBrace) return null;\n const jsonStr = body.slice(firstBrace, lastBrace + 1);\n\n try {\n const parsed = JSON.parse(jsonStr) as unknown;\n if (!isDispatchPlan(parsed)) return null;\n return parsed;\n } catch {\n return null;\n }\n}\n\nfunction isDispatchPlan(v: unknown): v is DispatchPlan {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['summary'] !== 'string') return false;\n if (!Array.isArray(o['phases'])) return false;\n if (!Array.isArray(o['risks'])) return false;\n if (!Array.isArray(o['operator_decision_points'])) return false;\n // total_estimated_tokens / total_estimated_cost_usd may be number OR 'unknown'\n const totalToks = o['total_estimated_tokens'];\n if (typeof totalToks !== 'number' && totalToks !== 'unknown') return false;\n const totalCost = o['total_estimated_cost_usd'];\n if (typeof totalCost !== 'number' && totalCost !== 'unknown') return false;\n // Phase shape \u2014 light validation; missing fields tolerated rather than rejecting the whole plan.\n for (const phase of o['phases']) {\n if (typeof phase !== 'object' || phase === null) return false;\n const p = phase as Record<string, unknown>;\n if (typeof p['name'] !== 'string') return false;\n if (typeof p['subagent_count'] !== 'number') return false;\n }\n return true;\n}\n\nexport async function handleDecomposeDispatch(\n deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Required: { customer_goal: string }. Optional: context (object).',\n );\n }\n assertWithinByteCap(TOOL_NAME, 'customer_goal', rawInput.customer_goal, MAX_GOAL_BYTES);\n\n let contextStrForSize = '';\n if (rawInput.context !== undefined) {\n try {\n contextStrForSize = JSON.stringify(rawInput.context);\n } catch {\n throw invalidParams(TOOL_NAME, \"input field 'context' is not JSON-serializable.\");\n }\n assertWithinByteCap(TOOL_NAME, 'context', contextStrForSize, MAX_CONTEXT_BYTES);\n }\n\n // Cache key includes prompt version so doctrine updates invalidate cache.\n const cacheInput = {\n system_prompt_version: SYSTEM_PROMPT_VERSION,\n customer_goal: rawInput.customer_goal,\n ...(rawInput.context !== undefined ? { context: rawInput.context } : {}),\n };\n const key = deps.cache.keyFor(TOOL_NAME, cacheInput);\n const excerpt = rawInput.customer_goal.slice(0, 300);\n const inputSizeBytes = bytesOf(rawInput.customer_goal) + bytesOf(contextStrForSize);\n\n // Cache replay \u2014 return cached envelope, still emit event (gate #7).\n const cached = deps.cache.get<ToolResultEnvelope<DecomposeDispatchPayload>>(key);\n if (cached !== null) {\n const replayEvent: ConsensusCallEvent = {\n ...buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n }),\n per_model_verdicts: cached.value.payload.per_model_plans.map((p) => ({\n model: p.model_id,\n model_id: p.model_id,\n provider: 'cache-replay',\n verdict: p.parse_ok ? 'pass' : 'uncertain',\n confidence: p.confidence,\n duration_ms: 0,\n raw_response_excerpt: p.plan_text.slice(0, 500),\n raw_response_hash: '',\n reasoning_summary: null,\n cited_sources: [],\n error: null,\n })),\n consensus_engine_version: cached.value.payload.engine_version ?? null,\n cache_hit: true,\n };\n deps.events.append(replayEvent);\n const replayEnvelope: ToolResultEnvelope<DecomposeDispatchPayload> = {\n ...cached.value,\n cache: { hit: true, key },\n };\n return jsonContent(replayEnvelope);\n }\n\n // Build the full prompt: system + customer goal.\n const fullPrompt =\n `${SYSTEM_PROMPT}\\n\\n---\\n\\nCustomer goal:\\n${rawInput.customer_goal}\\n\\nProduce the JSON dispatch plan now.`;\n\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\n tool: TOOL_NAME,\n gateType: GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes,\n session: deps.session,\n now: deps.now(),\n });\n\n let engineResult: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\n let engineThrew = false;\n try {\n engineResult = await deps.consensus.run({\n gate_type: GATE_TYPE,\n prompt: fullPrompt,\n ...(rawInput.context !== undefined ? { caller_context: rawInput.context } : {}),\n ...(signal !== undefined ? { signal } : {}),\n });\n } catch (err) {\n engineThrew = true;\n const message = err instanceof Error ? err.message : String(err);\n engineResult = { ok: false, reason: `engine-threw: ${message}` };\n }\n\n // Cancellation short-circuit (mirrors consensus-judgment.ts pattern).\n if (!engineResult.ok && engineResult.reason === 'cancelled') {\n deps.events.append({\n ...baseEvent,\n downstream_outcome: {\n status: 'cancelled',\n notes: 'mcp notifications/cancelled \u2014 handler aborted via SDK signal',\n },\n });\n const e = new Error('Request cancelled by client (notifications/cancelled)');\n e.name = 'AbortError';\n throw e;\n }\n\n if (!engineResult.ok) {\n // Engine unavailable \u2014 degrade to unimplemented envelope.\n const payload: DecomposeDispatchPayload = {\n verdict: 'unimplemented',\n reason: engineResult.reason,\n dispatch_plan: null,\n per_model_plans: [],\n synthesized_reasoning: '',\n consensus_confidence: 0,\n ...(engineThrew ? { degraded_mode: true } : {}),\n };\n const envelope: ToolResultEnvelope<DecomposeDispatchPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n // Do NOT cache !ok envelopes (same reasoning as consensus-judgment HP2-1 fix).\n deps.events.append(baseEvent);\n return jsonContent(envelope);\n }\n\n // Engine ran \u2014 parse each per-model response as a dispatch plan, parse\n // the synthesized verdict's reasoning_excerpt as THE plan.\n const perModelPlans = engineResult.per_model_verdicts.map((v) => {\n const parsed = tryParseDispatchPlan(v.raw_response_excerpt);\n return {\n model_id: v.model,\n plan_text: v.raw_response_excerpt,\n confidence: v.confidence,\n parse_ok: parsed !== null,\n };\n });\n\n const synthText = engineResult.synthesized_verdict.reasoning_excerpt;\n const synthPlan = tryParseDispatchPlan(synthText);\n\n const payload: DecomposeDispatchPayload = {\n verdict: synthPlan !== null ? 'pass' : 'uncertain',\n reason: synthPlan !== null\n ? 'Synthesized dispatch plan parsed successfully'\n : 'Synthesized verdict did not parse as a valid dispatch plan JSON \u2014 see per_model_plans for raw outputs',\n dispatch_plan: synthPlan,\n ...(synthPlan === null ? { parse_error: 'synthesized verdict text was not valid dispatch-plan JSON' } : {}),\n per_model_plans: perModelPlans,\n synthesized_reasoning: synthText,\n consensus_confidence: engineResult.synthesized_verdict.confidence,\n engine_version: engineResult.engine_version,\n ...(engineResult.per_model_verdicts.length < 3 ? { degraded: true } : {}),\n };\n\n const envelope: ToolResultEnvelope<DecomposeDispatchPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n\n // Cache the successful response (ok-engine envelopes are cacheable).\n deps.cache.set(key, envelope);\n\n const enrichedEvent: ConsensusCallEvent = {\n ...baseEvent,\n consensus_confidence: engineResult.synthesized_verdict.confidence,\n duration_ms: engineResult.duration_ms,\n consensus_engine_version: engineResult.engine_version,\n per_model_verdicts: toEventPerModelVerdicts(engineResult.per_model_verdicts),\n synthesized_verdict: toEventSynthesizedVerdict(engineResult.synthesized_verdict),\n };\n deps.events.append(enrichedEvent);\n\n return jsonContent(envelope);\n}\n\n/** Exported for tests. */\nexport const __test = {\n SYSTEM_PROMPT,\n SYSTEM_PROMPT_VERSION,\n tryParseDispatchPlan,\n isDispatchPlan,\n};\n", "/**\n * HTTP client for vo-control-plane's admin proxy endpoints (PR 1 #5670\n * and PR 2 #5674). Used by the vo-mcp heal/PR tool family when running\n * in cloud-mode.\n *\n * ## Why this exists\n *\n * The 10 stub tools in `src/tools/heal/` and `src/tools/pr/` return\n * `{verdict: 'unimplemented'}` because their Firebase callables (in\n * `functions-core-vo/src/vo/`) gate on Firebase auth UID \u2014 which the\n * MCP server, running as a desktop subprocess, cannot mint server-side.\n *\n * vo-control-plane bridges the gap: PRs 1+2 ship 11 admin HTTP proxy\n * endpoints under `/api/v1/admin/*` that forward to the callables.\n * vo-mcp's cloud-mode tools call these HTTP endpoints with the\n * `VO_CONTROL_PLANE_ADMIN_TOKEN` Bearer.\n *\n * ## End-to-end auth\n *\n * MCP client \u2192 vo-mcp (no auth \u2014 local subprocess)\n * vo-mcp \u2192 vo-control-plane (Bearer VO_CONTROL_PLANE_ADMIN_TOKEN)\n * vo-control-plane \u2192 Firebase callable (Bearer VO_INTERNAL_ADMIN_TOKEN,\n * after PR 1a/1b lands)\n *\n * ## Failure modes (all surface as thrown `AdminCallableError`)\n *\n * - 401 \u2014 invalid/missing token at vo-control-plane gate\n * - 502 \u2014 vo-control-plane couldn't reach the callable (during 1a/1b\n * gap, OR vo-control-plane is down)\n * - 503 \u2014 vo-control-plane has VO_ADMIN_CALLABLES=disabled\n * - 400 \u2014 input failed Zod validation at vo-control-plane\n * - network \u2014 connection refused / DNS / timeout\n *\n * Tool handlers map these to a `verdict: 'fail'` envelope with the\n * specific reason. Stub-mode (no client wired) remains the default.\n */\n\nimport {\n type AuthTokenSource,\n createStaticTokenSource,\n createAuthTokenSourceFromEnv,\n} from './auth-token-source.js';\nimport { readStoredCredential } from './credential-store.js';\n\nexport interface AdminInvokeOptions {\n /**\n * When true, return the full success envelope (with `ok` stripped)\n * instead of mandating a `.result` field. For control-plane endpoints\n * that do NOT use the callable-proxy `{ok, callable, result}` shape \u2014\n * e.g. concierge dispatch returns `{ok, mode, routed_from, pack}`.\n */\n readonly rawEnvelope?: boolean;\n}\n\nexport interface AdminCallableClient {\n /**\n * Read-only mode flag. When true, `buildCloudOrStubResponse` gates any\n * caller that is NOT itself read-only (the heal/PR *write* tools) back to\n * the stub envelope, while read-only tools (concierge dispatch + the\n * list/get diagnostics) still forward to cloud. Set via the\n * `VO_ADMIN_CALLABLES_READONLY` env var. Optional so existing test mocks\n * (plain object literals) remain valid.\n */\n readonly readOnly?: boolean;\n\n /**\n * Invoke an admin proxy endpoint. Returns the wrapped `result` field\n * from vo-control-plane's `{ok: true, callable, result}` envelope on\n * success. Throws `AdminCallableError` on any failure.\n *\n * @param path Relative path, e.g. '/api/v1/admin/pr/list'. Leading\n * slash required; gets joined with the control-plane base URL.\n * @param body JSON-serializable body. Pass `{}` for callables with no\n * parameters.\n * @param opts Pass `{ rawEnvelope: true }` for endpoints that return\n * a non-callable-proxy envelope (no `.result`) \u2014 the full object is\n * returned with `ok` stripped.\n */\n invoke<T = unknown>(\n path: string,\n body: Record<string, unknown>,\n opts?: AdminInvokeOptions,\n ): Promise<T>;\n}\n\n/**\n * Minimal fetch-like surface for testing. Production uses\n * `globalThis.fetch`.\n */\nexport type FetchLike = (\n url: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n },\n) => Promise<{\n status: number;\n text: () => Promise<string>;\n}>;\n\nexport interface HttpAdminCallableClientConfig {\n /**\n * Base URL of vo-control-plane (no trailing slash), e.g.\n * `https://vo-control-plane-bzjphrajaq-uc.a.run.app`.\n */\n readonly controlPlaneUrl: string;\n /**\n * Legacy static god-token (`VO_CONTROL_PLANE_ADMIN_TOKEN`). Provide this OR\n * `tokenSource`. Retained for back-compat \u2014 wrapped in a static source.\n */\n readonly adminToken?: string;\n /**\n * Per-user / pluggable auth (Increment 3). Preferred over `adminToken`; lets\n * the client send the USER's Firebase token so the god-token leaves the client\n * config. See {@link AuthTokenSource}.\n */\n readonly tokenSource?: AuthTokenSource;\n /** Read-only mode \u2014 gates non-read-only (write) tools back to stub. Default false. */\n readonly readOnly?: boolean;\n /** Optional fetch override for tests. Defaults to `globalThis.fetch`. */\n readonly fetchFn?: FetchLike;\n}\n\nexport class AdminCallableError extends Error {\n constructor(\n public readonly status: number,\n public readonly path: string,\n message: string,\n ) {\n super(message);\n this.name = 'AdminCallableError';\n }\n}\n\nexport class HttpAdminCallableClient implements AdminCallableClient {\n private readonly baseUrl: string;\n private readonly tokenSource: AuthTokenSource;\n private readonly fetchFn: FetchLike;\n /** See {@link AdminCallableClient.readOnly}. */\n public readonly readOnly: boolean;\n\n constructor(config: HttpAdminCallableClientConfig) {\n if (!config.controlPlaneUrl || config.controlPlaneUrl.length === 0) {\n throw new Error('HttpAdminCallableClient: controlPlaneUrl is required');\n }\n if (config.tokenSource) {\n this.tokenSource = config.tokenSource;\n } else if (config.adminToken && config.adminToken.length > 0) {\n this.tokenSource = createStaticTokenSource(config.adminToken, 'admin-token');\n } else {\n throw new Error('HttpAdminCallableClient: a tokenSource or adminToken is required');\n }\n this.baseUrl = config.controlPlaneUrl.replace(/\\/+$/, '');\n this.fetchFn = config.fetchFn ?? (globalThis.fetch as unknown as FetchLike);\n this.readOnly = config.readOnly ?? false;\n }\n\n async invoke<T = unknown>(\n path: string,\n body: Record<string, unknown>,\n opts?: AdminInvokeOptions,\n ): Promise<T> {\n if (!path.startsWith('/')) {\n throw new Error(\n `HttpAdminCallableClient.invoke: path must start with '/', got '${path}'`,\n );\n }\n const token = await this.tokenSource.getToken();\n if (!token) {\n // Fail-closed: never send an empty Bearer. Surfaces as a tool 'fail' envelope.\n throw new AdminCallableError(\n 401,\n path,\n `admin proxy ${path}: no auth token available (check VO_USER_REFRESH_TOKEN / VO_USER_ID_TOKEN / VO_CONTROL_PLANE_ADMIN_TOKEN)`,\n );\n }\n const url = `${this.baseUrl}${path}`;\n const response = await this.fetchFn(url, {\n method: 'POST',\n headers: {\n 'authorization': `Bearer ${token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n const text = await response.text();\n if (response.status < 200 || response.status >= 300) {\n throw new AdminCallableError(\n response.status,\n path,\n `admin proxy ${path} returned HTTP ${response.status}: ${text.slice(0, 200)}`,\n );\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n throw new AdminCallableError(\n response.status,\n path,\n `admin proxy ${path} returned non-JSON body`,\n );\n }\n if (typeof parsed !== 'object' || parsed === null) {\n throw new AdminCallableError(\n response.status,\n path,\n `admin proxy ${path} response not an object`,\n );\n }\n const obj = parsed as Record<string, unknown>;\n if (obj['ok'] !== true) {\n throw new AdminCallableError(\n response.status,\n path,\n `admin proxy ${path} returned ok=false: ${JSON.stringify(obj).slice(0, 200)}`,\n );\n }\n // Endpoints that don't use the callable-proxy `{ok, callable, result}`\n // envelope (e.g. concierge dispatch \u2192 `{ok, mode, routed_from, pack}`)\n // opt into receiving the full envelope, with `ok` stripped.\n if (opts?.rawEnvelope) {\n const rest: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (k !== 'ok') rest[k] = v;\n }\n return rest as T;\n }\n if (!('result' in obj)) {\n throw new AdminCallableError(\n response.status,\n path,\n `admin proxy ${path} response missing .result field`,\n );\n }\n return obj['result'] as T;\n }\n}\n\n/**\n * Build an admin callable client from env vars. Called by `cli.ts` \u2014\n * tests construct directly.\n *\n * Env vars:\n * - `VO_CONTROL_PLANE_URL` \u2014 required for cloud mode\n * - `VO_CONTROL_PLANE_ADMIN_TOKEN` \u2014 required for cloud mode\n * - `VO_ADMIN_CALLABLES_READONLY` \u2014 optional; when truthy (`1`/`true`)\n * the client is read-only: write tools (heal/PR) stay stubbed while\n * read-only tools (concierge dispatch + list/get diagnostics) forward.\n *\n * Returns `null` when both URL + token are unset \u2014 cloud mode is off, tools\n * fall back to stub envelopes. Both must be set together; presence of\n * only one is a configuration error (throws).\n */\nexport function buildAdminCallableClientFromEnv(\n env: NodeJS.ProcessEnv = process.env,\n): AdminCallableClient | null {\n const url = env['VO_CONTROL_PLANE_URL'];\n // Increment 3: prefer a per-user token over the legacy god-token. Priority:\n // explicit env user-token \u2192 stored `vo-mcp login` credential (3b) \u2192 god-token.\n // The god-token is no longer required in client config when a user token is\n // present. Throws on a half-configured env refresh source (token without key).\n const tokenSource = createAuthTokenSourceFromEnv(env, undefined, () => readStoredCredential(env));\n if (!url && !tokenSource) return null;\n if (!url) {\n throw new Error('Cloud mode requires VO_CONTROL_PLANE_URL');\n }\n if (!tokenSource) {\n throw new Error(\n 'Cloud mode requires auth: set VO_USER_REFRESH_TOKEN+VO_FIREBASE_API_KEY (per-user), VO_USER_ID_TOKEN, or VO_CONTROL_PLANE_ADMIN_TOKEN',\n );\n }\n const readOnlyRaw = env['VO_ADMIN_CALLABLES_READONLY'];\n const readOnly = readOnlyRaw === '1' || readOnlyRaw?.toLowerCase() === 'true';\n return new HttpAdminCallableClient({\n controlPlaneUrl: url,\n tokenSource,\n readOnly,\n });\n}\n", "/**\n * Shared cloud-or-stub response builder for the heal/PR tool family.\n *\n * Replaces the buildPrStubResponse / buildHealStubResponse calls with a\n * single path that branches on `deps.adminCallables`:\n *\n * - null/undefined \u2192 stub envelope (`verdict: 'unimplemented'`)\n * - set + success \u2192 cloud envelope (`verdict: 'pass'` + response_data)\n * - set + failure \u2192 cloud envelope (`verdict: 'fail'` + reason)\n *\n * In all 3 cases a gate #7 event is appended. The stub-mode `!ok`\n * envelope is NOT cached (HP2-1 hardening); the cloud-mode `pass`\n * envelope is cacheable but we leave caching off in PR 3 to keep the\n * surface tight \u2014 PR 4 can revisit.\n *\n * PR 3 wires this helper into `list-pending-prs.ts` (PR family) and\n * `get-workflow-runs.ts` (heal family) as proof of pattern. PR 4\n * migrates the remaining 8 stub tools.\n */\nimport type {\n AdminCallablePayload,\n ConsensusCallEvent,\n ToolDeps,\n ToolResultEnvelope,\n} from '../types.js';\nimport { AdminCallableError } from '../cloud/admin-callable-client.js';\nimport { buildBaseEvent, bytesOf, jsonContent } from './common.js';\n\nexport interface BuildCloudOrStubArgs {\n readonly toolName: string;\n /** Firebase callable name, e.g. `voListPendingPRs`. */\n readonly callableName: string;\n /** vo-control-plane admin proxy path, e.g. `/api/v1/admin/pr/list`. */\n readonly adminPath: string;\n /**\n * Snake-case input as received from the MCP client. Used verbatim in\n * the response envelope's `normalized_input` field, AND as the\n * cloud-mode wire body when `cloudBody` is not provided.\n */\n readonly normalizedInput: Record<string, unknown>;\n /**\n * Optional camelCase body for the cloud HTTP call. Tools whose MCP\n * input contract uses snake_case (e.g. `pr_number`) but whose admin\n * endpoint validates camelCase (e.g. `prNumber`) pass an explicit\n * remapped body here. Defaults to `normalizedInput` for tools where\n * the two shapes coincide (empty body, or already camelCase).\n */\n readonly cloudBody?: Record<string, unknown>;\n /**\n * When true, the cloud HTTP response is accepted as a raw envelope (no\n * mandated `.result` field) and used as `response_data` directly. For\n * endpoints like concierge dispatch that return `{ok, mode, pack}`\n * rather than the callable-proxy `{ok, callable, result}` shape.\n */\n readonly rawEnvelope?: boolean;\n /** Either 'admin-action' (matches existing heal/PR stub families). */\n readonly gateType: string;\n /** Stub-mode reason text \u2014 see common-pr.ts PR_STUB_REASON. */\n readonly stubReason: string;\n /**\n * Marks this tool as read-only. When the wired admin client is in\n * read-only mode (`VO_ADMIN_CALLABLES_READONLY`), only read-only tools\n * forward to cloud; non-read-only (write) tools are gated back to the\n * stub envelope. Default false (write).\n */\n readonly readOnly?: boolean;\n readonly deps: ToolDeps;\n}\n\n/**\n * Reason emitted when cloud mode is active but read-only, and a write tool\n * is gated back to its stub. Exported for tests.\n */\nexport const ADMIN_READONLY_GATE_REASON =\n '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.';\n\nexport async function buildCloudOrStubResponse(\n args: BuildCloudOrStubArgs,\n): Promise<ReturnType<typeof jsonContent>> {\n const inputJson = JSON.stringify(args.normalizedInput);\n const excerpt = inputJson.slice(0, 300);\n const key = args.deps.cache.keyFor(args.toolName, args.normalizedInput);\n\n const event: ConsensusCallEvent = buildBaseEvent({\n tool: args.toolName,\n gateType: args.gateType,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes: bytesOf(inputJson),\n session: args.deps.session,\n now: args.deps.now(),\n });\n\n // Read-only gate: when the wired client is read-only and THIS tool is a\n // write tool, fall back to the stub envelope instead of forwarding.\n const gatedByReadonly =\n Boolean(args.deps.adminCallables?.readOnly) && !args.readOnly;\n\n if (!args.deps.adminCallables || gatedByReadonly) {\n const payload: AdminCallablePayload = {\n verdict: 'unimplemented',\n reason: gatedByReadonly ? ADMIN_READONLY_GATE_REASON : args.stubReason,\n callable: args.callableName,\n normalized_input: args.normalizedInput,\n };\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\n tool: args.toolName,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n args.deps.events.append(event);\n return jsonContent(envelope);\n }\n\n try {\n const result = await args.deps.adminCallables.invoke<Record<string, unknown>>(\n args.adminPath,\n args.cloudBody ?? args.normalizedInput,\n args.rawEnvelope ? { rawEnvelope: true } : undefined,\n );\n const payload: AdminCallablePayload = {\n verdict: 'pass',\n reason: `forwarded to ${args.callableName} via vo-control-plane ${args.adminPath}`,\n callable: args.callableName,\n normalized_input: args.normalizedInput,\n response_data:\n result && typeof result === 'object' && !Array.isArray(result)\n ? (result as Record<string, unknown>)\n : { value: result },\n };\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\n tool: args.toolName,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n args.deps.events.append(event);\n return jsonContent(envelope);\n } catch (err) {\n const status =\n err instanceof AdminCallableError ? err.status : undefined;\n const message = err instanceof Error ? err.message : String(err);\n const payload: AdminCallablePayload = {\n verdict: 'fail',\n reason:\n status !== undefined\n ? `vo-control-plane returned HTTP ${status}: ${message.slice(0, 300)}`\n : `cloud invocation failed: ${message.slice(0, 300)}`,\n callable: args.callableName,\n normalized_input: args.normalizedInput,\n };\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\n tool: args.toolName,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n args.deps.events.append(event);\n return jsonContent(envelope);\n }\n}\n", "/**\n * Shared helpers for the heal/* MCP tool family (Plan PR #12a).\n *\n * All 5 heal tools (vo_trigger_heal, vo_fix_retry, vo_fix_clear,\n * vo_stop_workflow, vo_get_workflow_runs) wrap admin onCall callables in\n * `functions-core-vo/src/vo/vo-heal-*.ts`. For V1 those callables are\n * NOT reachable from the local MCP server because cloud-mode is still\n * unwired (`packages/vo-mcp/src/modes/cloud.ts` throws on construction,\n * pending `vo-cloud-tenant-model-2026-05-21.md` dispatch).\n *\n * So this PR ships the tool SURFACE \u2014 `tools/list` reveals the 5 tools,\n * input schemas are pinned to the cloud callables' contracts, and every\n * call returns a structured \"unimplemented\" envelope with a precise\n * reason. The contract is locked; a follow-up PR adds the real cloud\n * wiring without touching the contract or any consumer.\n *\n * This mirrors how `vo_consensus_judgment` originally shipped: stub \u2192\n * engine wired later \u2192 consumers unchanged. Documented in\n * `EXTRACTION_AUDIT.md` under \"Stub remaining (2026-05-24)\".\n */\nimport type {\n AdminCallablePayload,\n ConsensusCallEvent,\n SessionContext,\n ToolDeps,\n ToolResultEnvelope,\n} from '../../types.js';\nimport {\n buildBaseEvent,\n bytesOf,\n jsonContent,\n} from '../common.js';\n\n/** Reason text shared by every heal-family stub. */\nexport const HEAL_STUB_REASON =\n '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\")';\n\n/** gate_type for admin-action telemetry \u2014 distinct from ratchet/consensus paths. */\nexport const HEAL_GATE_TYPE = 'admin-action' as const;\n\nexport interface BuildHealStubArgs {\n readonly toolName: string;\n readonly callableName: string;\n readonly normalizedInput: Record<string, unknown>;\n readonly deps: ToolDeps;\n}\n\n/**\n * Build the standard heal-stub envelope + emit the gate #7 event line.\n *\n * Every tool in the heal family calls this after validating its input.\n * The shape is intentionally identical across tools so the operator's\n * consumers can branch on a single AdminCallablePayload type.\n */\nexport function buildHealStubResponse(\n args: BuildHealStubArgs,\n): ReturnType<typeof jsonContent> {\n const inputJson = JSON.stringify(args.normalizedInput);\n const excerpt = inputJson.slice(0, 300);\n const key = args.deps.cache.keyFor(args.toolName, args.normalizedInput);\n\n const payload: AdminCallablePayload = {\n verdict: 'unimplemented',\n reason: HEAL_STUB_REASON,\n callable: args.callableName,\n normalized_input: args.normalizedInput,\n };\n\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\n tool: args.toolName,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n\n // Do NOT cache the unimplemented envelope (HP2-1 hardening \u2014 see\n // consensus-judgment.ts). When cloud-mode lands, the env transition\n // unwired\u2192wired must be visible without manual cache eviction.\n const event: ConsensusCallEvent = buildBaseEvent({\n tool: args.toolName,\n gateType: HEAL_GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes: bytesOf(inputJson),\n session: args.deps.session,\n now: args.deps.now(),\n });\n args.deps.events.append(event);\n\n return jsonContent(envelope);\n}\n\n/** Re-exported for tools that need to construct events directly. */\nexport type { SessionContext };\n", "/**\n * Tool: `vo_trigger_heal`\n *\n * Wraps the `voTriggerHeal` admin callable\n * (`functions-core-vo/src/vo/vo-heal-trigger.ts`). Triggers a self-heal\n * pass \u2014 either for a specific focus page/tester (priority queue) or\n * for the auto-process queue.\n *\n * V1 ships as a stub returning `verdict: 'unimplemented'` because\n * cloud-mode is not yet wired (see common-heal.ts header). The input\n * schema mirrors the cloud callable's contract exactly so a future\n * cloud-mode wiring PR can drop in the real implementation without\n * touching any consumer.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\n\nexport const TOOL_NAME = 'vo_trigger_heal';\nconst CALLABLE_NAME = 'voTriggerHeal' as const;\nconst ADMIN_PATH = '/api/v1/admin/heal/trigger' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n focus_page: {\n type: 'string',\n description:\n 'Optional focus page identifier (maps to a known tester via vo-heal-focus state). When set, the heal pass becomes priority-queued for that tester. Omit to trigger the auto-process queue.',\n },\n force: {\n type: 'boolean',\n description:\n 'When true, request the heal pass even if a queue manager is already active. Default false.',\n },\n },\n additionalProperties: false,\n};\n\nexport const description =\n \"Triggers a self-heal pass against open PRs. Optionally scope to a `focus_page` (priority queue for one tester) or omit to fire the auto-process queue. Wraps the `voTriggerHeal` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` with structured normalized_input \u2014 cloud-mode wiring is pending. The contract is locked; consumers can call this tool today and get the correct surface without working execution.\";\n\ninterface ToolInput {\n readonly focus_page?: string;\n readonly force?: boolean;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (o['focus_page'] !== undefined && typeof o['focus_page'] !== 'string') return false;\n if (o['force'] !== undefined && typeof o['force'] !== 'boolean') return false;\n return true;\n}\n\nexport async function handleTriggerHeal(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. All fields optional. Shape: { focus_page?: string, force?: boolean }.',\n );\n }\n const normalizedInput: Record<string, unknown> = {};\n const cloudBody: Record<string, unknown> = {};\n if (rawInput.focus_page !== undefined) {\n normalizedInput['focus_page'] = rawInput.focus_page;\n cloudBody['focusPage'] = rawInput.focus_page;\n }\n if (rawInput.force !== undefined) {\n normalizedInput['force'] = rawInput.force;\n cloudBody['force'] = rawInput.force;\n }\n\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput,\n cloudBody,\n gateType: HEAL_GATE_TYPE,\n stubReason: HEAL_STUB_REASON,\n deps,\n });\n}\n", "/**\n * Tool: `vo_fix_retry`\n *\n * Consolidates two admin callables:\n * - `voRetryFixAttempt({ attemptId })` \u2014 retry one failed fix attempt\n * - `voRetryFixAttempts({ attemptIds[] })` \u2014 retry up to 50 at once\n *\n * The MCP tool exposes BOTH via a single input shape: pass `attempt_id`\n * for the single case, or `attempt_ids` for the batch case. Mutually\n * exclusive \u2014 passing both is rejected.\n *\n * V1 stub \u2014 see common-heal.ts header.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\n\nexport const TOOL_NAME = 'vo_fix_retry';\n\n/** Batch cap matches the cloud callable's slice(0, 50) limit. */\nconst MAX_BATCH = 50;\n\n/** Admin proxy paths for the two callables this MCP tool consolidates. */\nconst ADMIN_PATH_SINGLE = '/api/v1/admin/heal/retry-attempt' as const;\nconst ADMIN_PATH_BATCH = '/api/v1/admin/heal/retry-attempts' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n attempt_id: {\n type: 'string',\n description:\n 'Retry a single fix attempt by id (wraps voRetryFixAttempt). Mutually exclusive with attempt_ids.',\n },\n attempt_ids: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Retry up to 50 fix attempts by id (wraps voRetryFixAttempts). Mutually exclusive with attempt_id.',\n },\n },\n additionalProperties: false,\n};\n\nexport const description =\n \"Retries one or more failed fix attempts by id. Pass `attempt_id` for the single case or `attempt_ids` (up to 50) for the batch case. Wraps `voRetryFixAttempt` / `voRetryFixAttempts` admin Cloud Functions. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\n\ninterface ToolInput {\n readonly attempt_id?: string;\n readonly attempt_ids?: readonly string[];\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (o['attempt_id'] !== undefined && typeof o['attempt_id'] !== 'string') return false;\n if (o['attempt_ids'] !== undefined) {\n if (!Array.isArray(o['attempt_ids'])) return false;\n if (!o['attempt_ids'].every((id) => typeof id === 'string')) return false;\n }\n return true;\n}\n\nexport async function handleFixRetry(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Shape: { attempt_id: string } OR { attempt_ids: string[] } (mutually exclusive).',\n );\n }\n\n // Validate one-of contract. Destructure so the truthy-check inside each\n // `if` block also narrows the type \u2014 avoids the non-null-assertion path\n // the older code used (`rawInput.attempt_id!` etc.).\n const { attempt_id, attempt_ids } = rawInput;\n const hasSingle = typeof attempt_id === 'string' && attempt_id.trim().length > 0;\n const hasBatch = Array.isArray(attempt_ids) && attempt_ids.length > 0;\n\n if (!hasSingle && !hasBatch) {\n throw invalidParams(\n TOOL_NAME,\n 'either attempt_id (string) or attempt_ids (non-empty array) is required.',\n );\n }\n if (hasSingle && hasBatch) {\n throw invalidParams(\n TOOL_NAME,\n 'attempt_id and attempt_ids are mutually exclusive \u2014 pass exactly one.',\n );\n }\n if (Array.isArray(attempt_ids) && attempt_ids.length > MAX_BATCH) {\n throw invalidParams(\n TOOL_NAME,\n `attempt_ids exceeds batch cap of ${MAX_BATCH} (got ${attempt_ids.length}).`,\n );\n }\n\n const normalizedInput: Record<string, unknown> = {};\n const cloudBody: Record<string, unknown> = {};\n let resolvedCallable: string;\n let resolvedAdminPath: string;\n if (typeof attempt_id === 'string' && attempt_id.trim().length > 0) {\n const trimmed = attempt_id.trim();\n normalizedInput['attempt_id'] = trimmed;\n cloudBody['attemptId'] = trimmed;\n resolvedCallable = 'voRetryFixAttempt';\n resolvedAdminPath = ADMIN_PATH_SINGLE;\n } else if (Array.isArray(attempt_ids)) {\n // De-dupe + trim, mirroring the cloud callable's normalization.\n const cleaned = [\n ...new Set(attempt_ids.map((id) => id.trim()).filter(Boolean)),\n ].slice(0, MAX_BATCH);\n normalizedInput['attempt_ids'] = cleaned;\n cloudBody['attemptIds'] = cleaned;\n resolvedCallable = 'voRetryFixAttempts';\n resolvedAdminPath = ADMIN_PATH_BATCH;\n } else {\n // Defensive: the validation above guarantees one of the two branches\n // fired, but TypeScript can't propagate the hasSingle/hasBatch booleans.\n throw invalidParams(TOOL_NAME, 'internal validation skipped \u2014 please report.');\n }\n\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: resolvedCallable,\n adminPath: resolvedAdminPath,\n normalizedInput,\n cloudBody,\n gateType: HEAL_GATE_TYPE,\n stubReason: HEAL_STUB_REASON,\n deps,\n });\n}\n", "/**\n * Tool: `vo_fix_clear`\n *\n * Wraps `voClearFixAttempt({ attemptId })` admin callable. Clears (cancels)\n * a fix attempt in the virtual_office_attempts collection.\n *\n * V1 stub \u2014 see common-heal.ts header.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\n\nexport const TOOL_NAME = 'vo_fix_clear';\nconst CALLABLE_NAME = 'voClearFixAttempt' as const;\nconst ADMIN_PATH = '/api/v1/admin/heal/clear-attempt' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n attempt_id: {\n type: 'string',\n description: 'Required. Fix-attempt id in the virtual_office_attempts collection.',\n },\n },\n required: ['attempt_id'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Clears (cancels) a single fix attempt by id. Wraps `voClearFixAttempt` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\n\ninterface ToolInput {\n readonly attempt_id: string;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['attempt_id'] !== 'string') return false;\n if (o['attempt_id'].trim().length === 0) return false;\n return true;\n}\n\nexport async function handleFixClear(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Shape: { attempt_id: non-empty string }.',\n );\n }\n\n const trimmed = rawInput.attempt_id.trim();\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput: { attempt_id: trimmed },\n cloudBody: { attemptId: trimmed },\n gateType: HEAL_GATE_TYPE,\n stubReason: HEAL_STUB_REASON,\n deps,\n });\n}\n", "/**\n * Tool: `vo_stop_workflow`\n *\n * Wraps `voStopWorkflow({ runId, force? })` admin callable. Cancels a\n * running GitHub Actions workflow run via the gh API.\n *\n * V1 stub \u2014 see common-heal.ts header.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\n\nexport const TOOL_NAME = 'vo_stop_workflow';\nconst CALLABLE_NAME = 'voStopWorkflow' as const;\nconst ADMIN_PATH = '/api/v1/admin/workflow/stop' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n run_id: {\n type: 'number',\n description: 'Required. GitHub Actions workflow run id (positive integer).',\n },\n force: {\n type: 'boolean',\n description: 'Optional. When true, force-cancel even if the run looks healthy. Default false.',\n },\n },\n required: ['run_id'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Cancels a running GitHub Actions workflow by run id. Wraps `voStopWorkflow` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\n\ninterface ToolInput {\n readonly run_id: number;\n readonly force?: boolean;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['run_id'] !== 'number') return false;\n if (!Number.isFinite(o['run_id']) || o['run_id'] <= 0 || !Number.isInteger(o['run_id'])) {\n return false;\n }\n if (o['force'] !== undefined && typeof o['force'] !== 'boolean') return false;\n return true;\n}\n\nexport async function handleStopWorkflow(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Shape: { run_id: positive integer, force?: boolean }.',\n );\n }\n\n const normalizedInput: Record<string, unknown> = { run_id: rawInput.run_id };\n const cloudBody: Record<string, unknown> = { runId: rawInput.run_id };\n if (rawInput.force !== undefined) {\n normalizedInput['force'] = rawInput.force;\n cloudBody['force'] = rawInput.force;\n }\n\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput,\n cloudBody,\n gateType: HEAL_GATE_TYPE,\n stubReason: HEAL_STUB_REASON,\n deps,\n });\n}\n", "/**\n * Tool: `vo_get_workflow_runs`\n *\n * Wraps `voGetWorkflowRuns()` admin callable (no parameters). Returns\n * the Command Center workflow-runs snapshot via the gh API.\n *\n * V1 stub \u2014 see common-heal.ts header. Stub envelope's\n * `normalized_input` is the empty object since the callable takes no\n * params; the structured contract is still cache-keyed for consistency\n * with the rest of the heal family.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { HEAL_GATE_TYPE, HEAL_STUB_REASON } from './common-heal.js';\n\nexport const TOOL_NAME = 'vo_get_workflow_runs';\nconst CALLABLE_NAME = 'voGetWorkflowRuns' as const;\nconst ADMIN_PATH = '/api/v1/admin/workflow/runs' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {},\n additionalProperties: false,\n};\n\nexport const description =\n \"Returns the current Command Center workflow-runs snapshot (Heal, Manager, Auto-Merge, Deploy on Merge, etc.). Wraps `voGetWorkflowRuns` admin Cloud Function. Read-only diagnostic. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\n\nfunction isToolInput(v: unknown): v is Record<string, never> {\n if (typeof v !== 'object' || v === null) return false;\n // Tolerate extra fields rather than rejecting \u2014 the schema declares\n // additionalProperties:false, so the MCP SDK will reject unknown fields\n // before we get here on spec-compliant clients. Loose check for\n // non-compliant clients.\n return true;\n}\n\nexport async function handleGetWorkflowRuns(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(TOOL_NAME, 'invalid input. This tool takes no parameters.');\n }\n\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput: {},\n gateType: HEAL_GATE_TYPE,\n stubReason: HEAL_STUB_REASON,\n // Read-only diagnostic \u2014 stays live under VO_ADMIN_CALLABLES_READONLY.\n readOnly: true,\n deps,\n });\n}\n", "/**\n * Shared helpers for the pr/* MCP tool family (Plan PR #12b).\n *\n * Mirrors `packages/vo-mcp/src/tools/heal/common-heal.ts` exactly \u2014 same\n * stub-mode pattern, same envelope shape (AdminCallablePayload), same\n * gate_type ('admin-action'). The two helpers diverge only in their\n * STUB_REASON text so logs distinguish which family was called.\n *\n * Wraps the pr-manager admin callables in\n * `functions-core-vo/src/vo/vo-pr-manager.ts`:\n * - voListPendingPRs \u2192 vo_list_pending_prs\n * - voMergePR \u2192 vo_merge_pr\n * - voRejectPR \u2192 vo_reject_pr\n * - voApproveAllFixes \u2192 vo_approve_all_fixes\n * - voRejectAndRetry \u2192 vo_reject_and_retry\n *\n * V1 ships as stubs because vo-mcp's cloud-mode adapter\n * (packages/vo-mcp/src/modes/cloud.ts) still throws on construction.\n * See common-heal.ts header for the longer rationale.\n */\nimport type {\n AdminCallablePayload,\n ConsensusCallEvent,\n ToolDeps,\n ToolResultEnvelope,\n} from '../../types.js';\nimport {\n buildBaseEvent,\n bytesOf,\n jsonContent,\n} from '../common.js';\n\n/** Reason text shared by every pr-family stub. */\nexport const PR_STUB_REASON =\n '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\")';\n\n/** Same gate_type as the heal family \u2014 both are admin-action telemetry. */\nexport const PR_GATE_TYPE = 'admin-action' as const;\n\nexport interface BuildPrStubArgs {\n readonly toolName: string;\n readonly callableName: string;\n readonly normalizedInput: Record<string, unknown>;\n readonly deps: ToolDeps;\n}\n\n/**\n * Build the standard pr-stub envelope + emit the gate #7 event line.\n * Parallel to `buildHealStubResponse` \u2014 see common-heal.ts.\n */\nexport function buildPrStubResponse(\n args: BuildPrStubArgs,\n): ReturnType<typeof jsonContent> {\n const inputJson = JSON.stringify(args.normalizedInput);\n const excerpt = inputJson.slice(0, 300);\n const key = args.deps.cache.keyFor(args.toolName, args.normalizedInput);\n\n const payload: AdminCallablePayload = {\n verdict: 'unimplemented',\n reason: PR_STUB_REASON,\n callable: args.callableName,\n normalized_input: args.normalizedInput,\n };\n\n const envelope: ToolResultEnvelope<AdminCallablePayload> = {\n tool: args.toolName,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n\n // Do NOT cache unimplemented envelopes (HP2-1 hardening \u2014 same as\n // common-heal.ts).\n const event: ConsensusCallEvent = buildBaseEvent({\n tool: args.toolName,\n gateType: PR_GATE_TYPE,\n inputHash: key,\n inputExcerpt: excerpt,\n inputSizeBytes: bytesOf(inputJson),\n session: args.deps.session,\n now: args.deps.now(),\n });\n args.deps.events.append(event);\n\n return jsonContent(envelope);\n}\n", "/**\n * Tool: `vo_list_pending_prs`\n *\n * Wraps `voListPendingPRs()` admin callable (no parameters). Returns\n * the list of open VO-source pull requests with blocker / source /\n * tester / specialist-context metadata.\n *\n * V1 stub \u2014 see common-pr.ts header.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\n\nexport const TOOL_NAME = 'vo_list_pending_prs';\nconst CALLABLE_NAME = 'voListPendingPRs' as const;\nconst ADMIN_PATH = '/api/v1/admin/pr/list' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {},\n additionalProperties: false,\n};\n\nexport const description =\n \"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.\";\n\nfunction isToolInput(v: unknown): v is Record<string, never> {\n return typeof v === 'object' && v !== null;\n}\n\nexport async function handleListPendingPRs(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(TOOL_NAME, 'invalid input. This tool takes no parameters.');\n }\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput: {},\n gateType: PR_GATE_TYPE,\n stubReason: PR_STUB_REASON,\n // Read-only diagnostic \u2014 stays live under VO_ADMIN_CALLABLES_READONLY.\n readOnly: true,\n deps,\n });\n}\n", "/**\n * Tool: `vo_merge_pr`\n *\n * Wraps `voMergePR({ prNumber })` admin callable. Approves + merges a\n * single VO-source PR by number (refuses non-VO PRs server-side).\n *\n * V1 stub \u2014 see common-pr.ts header.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\n\nexport const TOOL_NAME = 'vo_merge_pr';\nconst CALLABLE_NAME = 'voMergePR' as const;\nconst ADMIN_PATH = '/api/v1/admin/pr/merge' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n pr_number: {\n type: 'number',\n description: 'Required. GitHub pull request number (positive integer).',\n },\n },\n required: ['pr_number'],\n additionalProperties: false,\n};\n\nexport const description =\n \"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.\";\n\ninterface ToolInput {\n readonly pr_number: number;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['pr_number'] !== 'number') return false;\n if (!Number.isFinite(o['pr_number']) || o['pr_number'] < 1 || !Number.isInteger(o['pr_number'])) {\n return false;\n }\n return true;\n}\n\nexport async function handleMergePR(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Shape: { pr_number: positive integer }.',\n );\n }\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput: { pr_number: rawInput.pr_number },\n cloudBody: { prNumber: rawInput.pr_number },\n gateType: PR_GATE_TYPE,\n stubReason: PR_STUB_REASON,\n deps,\n });\n}\n", "/**\n * Tool: `vo_reject_pr`\n *\n * Wraps `voRejectPR({ prNumber })` admin callable. Closes a PR without\n * merging (no retry dispatched \u2014 use vo_reject_and_retry for that).\n *\n * V1 stub \u2014 see common-pr.ts header.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\n\nexport const TOOL_NAME = 'vo_reject_pr';\nconst CALLABLE_NAME = 'voRejectPR' as const;\nconst ADMIN_PATH = '/api/v1/admin/pr/reject' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n pr_number: {\n type: 'number',\n description: 'Required. GitHub pull request number (positive integer).',\n },\n },\n required: ['pr_number'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Closes a pull request without merging. No retry dispatched \u2014 use `vo_reject_and_retry` for close+retry. Wraps `voRejectPR` admin Cloud Function. V1 stub: returns `verdict: 'unimplemented'` \u2014 cloud-mode wiring pending.\";\n\ninterface ToolInput {\n readonly pr_number: number;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['pr_number'] !== 'number') return false;\n if (!Number.isFinite(o['pr_number']) || o['pr_number'] < 1 || !Number.isInteger(o['pr_number'])) {\n return false;\n }\n return true;\n}\n\nexport async function handleRejectPR(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Shape: { pr_number: positive integer }.',\n );\n }\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput: { pr_number: rawInput.pr_number },\n cloudBody: { prNumber: rawInput.pr_number },\n gateType: PR_GATE_TYPE,\n stubReason: PR_STUB_REASON,\n deps,\n });\n}\n", "/**\n * Tool: `vo_approve_all_fixes`\n *\n * Wraps `voApproveAllFixes()` admin callable (no parameters). Iterates\n * all open VO-source PRs and merges/auto-merges each. Returns a summary\n * of merged / accepted / total counts plus per-PR results.\n *\n * V1 stub \u2014 see common-pr.ts header.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\n\nexport const TOOL_NAME = 'vo_approve_all_fixes';\nconst CALLABLE_NAME = 'voApproveAllFixes' as const;\nconst ADMIN_PATH = '/api/v1/admin/pr/approve-all' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {},\n additionalProperties: false,\n};\n\nexport const description =\n \"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.\";\n\nfunction isToolInput(v: unknown): v is Record<string, never> {\n return typeof v === 'object' && v !== null;\n}\n\nexport async function handleApproveAllFixes(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(TOOL_NAME, 'invalid input. This tool takes no parameters.');\n }\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput: {},\n gateType: PR_GATE_TYPE,\n stubReason: PR_STUB_REASON,\n deps,\n });\n}\n", "/**\n * Tool: `vo_reject_and_retry`\n *\n * Wraps `voRejectAndRetry({ prNumber })` admin callable. Closes a VO PR\n * and dispatches a self-heal pass to retry the same focus page. The\n * cloud callable refuses non-VO PRs and respects the self-heal kill\n * switch + per-PR retry block.\n *\n * V1 stub \u2014 see common-pr.ts header.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type {\n jsonContent} from '../common.js';\nimport {\n invalidParams,\n type ToolInputSchema,\n} from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport { PR_GATE_TYPE, PR_STUB_REASON } from './common-pr.js';\n\nexport const TOOL_NAME = 'vo_reject_and_retry';\nconst CALLABLE_NAME = 'voRejectAndRetry' as const;\nconst ADMIN_PATH = '/api/v1/admin/pr/reject-retry' as const;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n pr_number: {\n type: 'number',\n description: 'Required. GitHub pull request number (positive integer).',\n },\n },\n required: ['pr_number'],\n additionalProperties: false,\n};\n\nexport const description =\n \"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.\";\n\ninterface ToolInput {\n readonly pr_number: number;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['pr_number'] !== 'number') return false;\n if (!Number.isFinite(o['pr_number']) || o['pr_number'] < 1 || !Number.isInteger(o['pr_number'])) {\n return false;\n }\n return true;\n}\n\nexport async function handleRejectAndRetry(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Shape: { pr_number: positive integer }.',\n );\n }\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput: { pr_number: rawInput.pr_number },\n cloudBody: { prNumber: rawInput.pr_number },\n gateType: PR_GATE_TYPE,\n stubReason: PR_STUB_REASON,\n deps,\n });\n}\n", "/**\n * Tool: `vo_review_merge` \u2014 READ-ONLY consensus pre-merge review.\n *\n * The safe half of \"verify-before-act\" for the Command Center. Given a PR\n * number it:\n * 1. Pulls the PR's metadata from the live admin proxy (`/api/v1/admin/pr/list`\n * \u2014 CI/blocker status, source, linked bug ids, title).\n * 2. Runs the consensus engine (\"should this merge? assess risk\") via the\n * `final-deep-verify` gate.\n * 3. Returns a `recommendation` (merge / hold / reject) with per-model\n * verdicts + reasoning.\n *\n * It NEVER merges \u2014 `merged` is always `false`. Acting on the recommendation is\n * a separate, explicit step (`vo_merge_pr`). A deterministic SAFETY OVERLAY sits\n * on top of the consensus verdict: it will not recommend `merge` when the PR has\n * any open blocker (failed checks, conflict, draft), and defaults to `hold` when\n * consensus is unavailable \u2014 so a single model hallucinating \"merge\" can never\n * upgrade a blocked or unverified PR.\n *\n * Increment 1 (this tool) reviews on METADATA (`basis: 'metadata'`). A later\n * increment deepens it with the actual diff + full check-runs.\n *\n * Requires cloud mode (admin proxy) for PR context + \u22652 model keys for a real\n * consensus verdict. Without admin callables it returns `recommendation:\n * 'unavailable'`. Every call is logged (gate #7).\n */\nimport type {\n ToolDeps,\n ToolResultEnvelope,\n ConsensusCallEvent,\n PerModelVerdict,\n SynthesizedVerdict,\n} from '../../types.js';\nimport {\n buildBaseEvent,\n bytesOf,\n invalidParams,\n jsonContent,\n toEventPerModelVerdicts,\n toEventSynthesizedVerdict,\n type ToolInputSchema,\n} from '../common.js';\nimport { AdminCallableError } from '../../cloud/admin-callable-client.js';\n\nexport const TOOL_NAME = 'vo_review_merge';\nconst LIST_PATH = '/api/v1/admin/pr/list' as const;\n/** Recognized engine gate \u2014 thorough deliberation, fitting a merge decision. */\nconst ENGINE_GATE = 'final-deep-verify' as const;\n/** Telemetry gate_type for the gate-#7 event (slices merge reviews separately). */\nconst EVENT_GATE = 'merge-review' as const;\n/** Reason when cloud mode (admin proxy) is not wired, so PR context can't be fetched. */\nconst UNAVAILABLE_REASON =\n 'vo_review_merge needs cloud mode to fetch PR context \u2014 set VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN in the MCP env. (It is read-only; it never merges.)';\n\nexport type MergeRecommendation = 'merge' | 'hold' | 'reject' | 'unavailable';\n\ninterface ReviewedPr {\n readonly number: number;\n readonly title: string;\n readonly source: string | null;\n readonly blocker: string | null;\n readonly bug_ids: readonly string[];\n}\n\ninterface MergeReviewPayload {\n readonly recommendation: MergeRecommendation;\n /** Raw consensus verdict (null when consensus didn't run). */\n readonly verdict: 'pass' | 'fail' | 'uncertain' | null;\n readonly confidence: number | null;\n readonly reason: string;\n /** What the review is grounded on. Increment 1 = metadata (no diff yet). */\n readonly basis: 'metadata';\n readonly pr: ReviewedPr | null;\n readonly per_model_verdicts: readonly PerModelVerdict[];\n readonly synthesized_verdict: SynthesizedVerdict | null;\n readonly engine_version: string | null;\n readonly degraded: boolean;\n /** This tool NEVER merges. Always false \u2014 present so callers can assert it. */\n readonly merged: false;\n}\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n pr_number: {\n type: 'number',\n description: 'GitHub pull request number (positive integer) to review for merge.',\n },\n notes: {\n type: 'string',\n description: 'Optional reviewer notes / context to factor into the verdict.',\n },\n },\n required: ['pr_number'],\n additionalProperties: false,\n};\n\nexport const description =\n \"READ-ONLY pre-merge review: given a PR number, fetches its CI/blocker status + source + linked bugs, runs a multi-model consensus 'should this merge?' check, and returns a recommendation (merge | hold | reject) with per-model reasoning. NEVER merges \u2014 acting on it is a separate vo_merge_pr call. A deterministic safety overlay refuses to recommend merge when the PR has an open blocker and defaults to hold when consensus is unavailable. Use BEFORE vo_merge_pr to put a verification gate in front of the Command Center. Increment 1 reviews on metadata; needs cloud mode for PR context + model keys for a real verdict.\";\n\ninterface ToolInput {\n readonly pr_number: number;\n readonly notes?: string;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['pr_number'] !== 'number') return false;\n if (o['notes'] !== undefined && typeof o['notes'] !== 'string') return false;\n return true;\n}\n\nfunction str(v: unknown): string | null {\n return typeof v === 'string' && v.length > 0 ? v : null;\n}\n\n/** Extract the fields we depend on from a list-proxy PR entry (defensive on casing). */\nfunction readPr(raw: Record<string, unknown>): ReviewedPr {\n const bugsRaw = raw['bugIds'] ?? raw['bug_ids'];\n const bug_ids = Array.isArray(bugsRaw) ? bugsRaw.filter((b): b is string => typeof b === 'string') : [];\n return {\n number: Number(raw['number']),\n title: str(raw['title']) ?? '(untitled)',\n source: str(raw['source']) ?? str(raw['prSource']),\n blocker: str(raw['blockerKind']) ?? str(raw['blocker_kind']) ?? str(raw['blockerLabel']),\n bug_ids,\n };\n}\n\nfunction buildPrompt(pr: ReviewedPr, notes: string | undefined): string {\n const lines = [\n 'You are a release gatekeeper deciding whether a pull request is safe to MERGE.',\n 'Recommend exactly one of: merge / hold / reject. Be conservative \u2014 this is a high-stakes irreversible action.',\n '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.',\n '',\n `PR #${pr.number}: ${pr.title}`,\n `Source: ${pr.source ?? 'unknown'}`,\n `Linked bug ids: ${pr.bug_ids.length > 0 ? pr.bug_ids.join(', ') : '(none)'}`,\n `Open blocker: ${pr.blocker ?? 'none reported'}`,\n ];\n if (notes !== undefined && notes.length > 0) lines.push('', `Reviewer notes: ${notes}`);\n lines.push('', 'Return your verdict (pass = recommend merge, fail = reject, uncertain = hold) with concise reasoning.');\n return lines.join('\\n');\n}\n\nexport async function handleReviewMerge(\n deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput) || !Number.isInteger(rawInput.pr_number) || rawInput.pr_number < 1) {\n throw invalidParams(TOOL_NAME, 'invalid input. Required: { pr_number: positive integer }. Optional: notes.');\n }\n const prNumber = rawInput.pr_number;\n const normalizedInput: Record<string, unknown> = { pr_number: prNumber };\n if (rawInput.notes !== undefined) normalizedInput.notes = rawInput.notes;\n const inputJson = JSON.stringify(normalizedInput);\n const key = deps.cache.keyFor(TOOL_NAME, normalizedInput);\n\n const baseEvent: ConsensusCallEvent = buildBaseEvent({\n tool: TOOL_NAME,\n gateType: EVENT_GATE,\n inputHash: key,\n inputExcerpt: inputJson.slice(0, 300),\n inputSizeBytes: bytesOf(inputJson),\n session: deps.session,\n now: deps.now(),\n });\n\n const emit = (\n payload: MergeReviewPayload,\n eventExtra?: Partial<ConsensusCallEvent>,\n ): ReturnType<typeof jsonContent> => {\n deps.events.append(eventExtra ? { ...baseEvent, ...eventExtra } : baseEvent);\n const envelope: ToolResultEnvelope<MergeReviewPayload> = {\n tool: TOOL_NAME,\n schema_version: 1,\n cache: { hit: false, key },\n payload,\n };\n return jsonContent(envelope);\n };\n\n const emptyPayload = (\n recommendation: MergeRecommendation,\n reason: string,\n pr: ReviewedPr | null,\n ): MergeReviewPayload => ({\n recommendation,\n verdict: null,\n confidence: null,\n reason,\n basis: 'metadata',\n pr,\n per_model_verdicts: [],\n synthesized_verdict: null,\n engine_version: null,\n degraded: false,\n merged: false,\n });\n\n // No cloud mode \u2192 can't fetch PR context. Read-only, so report unavailable.\n if (!deps.adminCallables) {\n return emit(emptyPayload('unavailable', UNAVAILABLE_REASON, null));\n }\n\n // Fetch the PR's metadata via the live admin proxy.\n let pr: ReviewedPr | null = null;\n try {\n const list = await deps.adminCallables.invoke<unknown>(LIST_PATH, {});\n const entries = Array.isArray(list) ? (list as Array<Record<string, unknown>>) : [];\n const found = entries.find((p) => Number(p['number']) === prNumber);\n if (found) pr = readPr(found);\n } catch (err) {\n const status = err instanceof AdminCallableError ? ` (HTTP ${err.status})` : '';\n const message = err instanceof Error ? err.message : String(err);\n return emit(emptyPayload('hold', `could not fetch PR context${status}: ${message.slice(0, 200)}`, null));\n }\n\n if (pr === null) {\n return emit(\n emptyPayload('hold', `PR #${prNumber} is not among open VO PRs (already merged/closed, or not a VO-source PR).`, null),\n );\n }\n\n const hasBlocker = pr.blocker !== null && pr.blocker !== 'none';\n\n // Run consensus. Engine-client never throws by contract, but wrap defensively.\n let result: Awaited<ReturnType<ToolDeps['consensus']['run']>>;\n let threw = false;\n try {\n result = await deps.consensus.run({\n gate_type: ENGINE_GATE,\n prompt: buildPrompt(pr, rawInput.notes),\n caller_context: { pr_number: prNumber, pr_metadata: pr, basis: 'metadata' },\n ...(signal !== undefined ? { signal } : {}),\n });\n } catch (err) {\n threw = true;\n result = { ok: false, reason: `engine-threw: ${err instanceof Error ? err.message : String(err)}` };\n }\n\n if (!result.ok && result.reason === 'cancelled') {\n deps.events.append({\n ...baseEvent,\n downstream_outcome: { status: 'cancelled', notes: 'mcp notifications/cancelled' },\n });\n const e = new Error('Request cancelled by client (notifications/cancelled)');\n e.name = 'AbortError';\n throw e;\n }\n\n // Consensus unavailable \u2192 conservative HOLD (never auto-recommend merge unverified).\n if (!result.ok) {\n const reason =\n `consensus unavailable (${result.reason}) \u2014 holding for manual review.` +\n (hasBlocker ? ` PR also has an open blocker: ${pr.blocker}.` : '');\n return emit(\n { ...emptyPayload('hold', reason, pr), degraded: threw },\n threw ? {} : undefined,\n );\n }\n\n // Map consensus verdict \u2192 recommendation, then apply the safety overlay.\n const verdict = result.synthesized_verdict.verdict;\n let recommendation: MergeRecommendation = verdict === 'pass' ? 'merge' : verdict === 'fail' ? 'reject' : 'hold';\n let reason =\n result.synthesized_verdict.dissent_summary ??\n result.synthesized_verdict.reasoning_excerpt ??\n `panel agreed (${result.per_model_verdicts.length} models, engine=${result.engine_version})`;\n\n if (hasBlocker && recommendation === 'merge') {\n recommendation = 'hold';\n reason = `Consensus leaned merge, but PR has an open blocker (${pr.blocker}) \u2014 holding (safety overlay). ${reason}`;\n }\n\n const perModel = toEventPerModelVerdicts(result.per_model_verdicts);\n const synth = toEventSynthesizedVerdict(result.synthesized_verdict);\n\n const payload: MergeReviewPayload = {\n recommendation,\n verdict,\n confidence: result.synthesized_verdict.confidence,\n reason,\n basis: 'metadata',\n pr,\n per_model_verdicts: perModel,\n synthesized_verdict: synth,\n engine_version: result.engine_version,\n degraded: result.degraded,\n merged: false,\n };\n\n return emit(payload, {\n consensus_confidence: result.synthesized_verdict.confidence,\n duration_ms: result.duration_ms,\n consensus_engine_version: result.engine_version,\n per_model_verdicts: perModel,\n synthesized_verdict: synth,\n });\n}\n", "/**\n * Directive computation for `vo_report_session_state`.\n *\n * Pure function of `context_used_pct`. V1 ships VO-fixed thresholds;\n * per-tenant configurable thresholds land in V2 per\n * `docs/vo/vo-roadmap-2026-05-26.md` \u00A73.4 (open operator question).\n *\n * Thresholds chosen 2026-05-26 per operator directive (\"at 70% time to\n * wrap up\"). 85% is the hard-handoff threshold \u2014 the agent must\n * terminate now or risk hitting context exhaustion mid-write.\n *\n * **Duplication notice.** These thresholds are also defined at\n * `cloud-run/vo-control-plane/src/directive.ts` (the server-side\n * implementation that this MCP tool will eventually call once\n * vo-control-plane is deployed \u2014 Phase 3). They MUST stay in lock-step.\n * When the cloud-control-plane is wired (Phase 3), the local stub\n * computation here gets removed and this file becomes a thin caller of\n * the HTTP endpoint. Until then the duplication is the cost of running\n * V1 launch gate #9 before the cloud control plane is live.\n *\n * Rationale for two thresholds (not one):\n * - 70% gives the agent slack to finish the current sub-task before\n * writing a handoff. Premature handoff fragments work.\n * - 85% leaves ~15% of context budget for the handoff doc itself + any\n * final commits/PRs. Tighter than that risks the handoff being\n * incomplete.\n */\n\n/** Directive the agent should follow based on its context utilization. */\nexport type SessionDirective = 'continue' | 'prepare_handoff' | 'execute_handoff_now';\n\n/**\n * Thresholds for context-usage \u2192 directive transitions.\n *\n * V1: VO-fixed. V2 plan: per-tenant override via\n * `Tenant.context_directive_thresholds`.\n *\n * **Schema-v1 lock invariant:** changes here must match the\n * vo-control-plane sibling exactly. CI doesn't yet enforce this; cover\n * via the cross-package directive-threshold-parity test in PR #B3.\n */\nexport const SESSION_DIRECTIVE_THRESHOLDS = {\n prepare_handoff_pct: 70,\n execute_handoff_now_pct: 85,\n} as const;\n\n/**\n * Compute the directive given current context usage.\n *\n * Inputs outside [0, 100] are clamped to the nearest valid value\n * (defensive \u2014 schema validation rejects them upstream but routes that\n * skip validation should not crash).\n */\nexport function computeDirective(context_used_pct: number): SessionDirective {\n const clamped = Math.max(0, Math.min(100, context_used_pct));\n if (clamped >= SESSION_DIRECTIVE_THRESHOLDS.execute_handoff_now_pct) {\n return 'execute_handoff_now';\n }\n if (clamped >= SESSION_DIRECTIVE_THRESHOLDS.prepare_handoff_pct) {\n return 'prepare_handoff';\n }\n return 'continue';\n}\n\n/** Human-readable message paired with each directive. */\nexport function directiveMessage(directive: SessionDirective): string {\n switch (directive) {\n case 'continue':\n return 'Context usage healthy. Continue work.';\n case 'prepare_handoff':\n return `Context usage at or above ${SESSION_DIRECTIVE_THRESHOLDS.prepare_handoff_pct}%. Begin wrapping up the current sub-task and prepare a handoff doc covering current state, next steps, blockers, and verification needed.`;\n case 'execute_handoff_now':\n return `Context usage at or above ${SESSION_DIRECTIVE_THRESHOLDS.execute_handoff_now_pct}%. Stop work, write the handoff doc using the template at ~/.vo/templates/handoff.md to ~/.vo/handoffs/<session_id>-<ts>.md, then call endSession with terminal_status='handed_off' and the handoff path.`;\n }\n}\n\n/**\n * Compute the suggested handoff destination path for a session. Only\n * meaningful when directive === 'execute_handoff_now'; returned by the\n * MCP tool as a convenience so the caller doesn't have to build the\n * path themselves.\n *\n * The MCP tool does NOT create the file \u2014 that's the caller's job. It\n * just suggests the canonical location.\n */\nexport function suggestedHandoffPath(session_id: string, isoTimestamp: string): string {\n // ISO timestamps include `:` which is invalid in Windows filenames. Replace\n // with `-` so the same path is portable.\n const safeTs = isoTimestamp.replace(/[:.]/g, '-');\n return `~/.vo/handoffs/${session_id}-${safeTs}.md`;\n}\n", "/**\n * Tool: `vo_report_session_state`\n *\n * Implements V1 launch gate #9 (fleet context lifecycle management) per\n * `docs/vo/vo-roadmap-2026-05-26.md` \u00A73.4. Accepts a per-session context\n * report from a host agent (Claude Code, Cursor, Continue, Codex) and\n * returns a directive: continue | prepare_handoff | execute_handoff_now.\n *\n * Cloud-control-plane mode: when VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN\n * + VO_TENANT_ID are set, the tool forwards reports to the deployed vo-control-plane\n * HTTP API. Auto-allocates the session on first report (404 from report-state \u2192 allocate\n * \u2192 retry). Interactive agents (Claude Code, Cursor, Codex, Continue) now appear on\n * the live fleet whiteboard.\n *\n * Stub-local fallback: when cloud config is absent OR cloud calls fail, computes the\n * directive purely from `context_used_pct` against the documented thresholds (70% / 85%)\n * without a network call. The response shape stays stable across modes (`backend_mode`\n * field in the payload tells the caller which mode produced the verdict).\n *\n * Sibling SessionState entity at `cloud-run/vo-control-plane/src/schema/session-v1.ts`\n * was shipped in PR #5409. The cloud-side `computeDirective` at\n * `cloud-run/vo-control-plane/src/directive.ts` is mirrored here in\n * `./directive.ts` \u2014 both files MUST share the same thresholds.\n */\nimport type { ToolDeps } from '../../types.js';\nimport {\n invalidParams,\n jsonContent,\n type ToolInputSchema,\n} from '../common.js';\nimport {\n computeDirective,\n directiveMessage,\n suggestedHandoffPath,\n type SessionDirective,\n} from './directive.js';\n\nexport const TOOL_NAME = 'vo_report_session_state';\n\nconst VALID_AGENT_TYPES = ['claude-code', 'codex', 'cursor', 'continue'] as const;\ntype AgentType = (typeof VALID_AGENT_TYPES)[number];\n\n/** Max length of `current_goal` accepted \u2014 anything longer is truncated, not rejected. */\nconst MAX_GOAL_CHARS = 500;\n/** Max items in `recent_files_touched`. Inputs over this are rejected. */\nconst MAX_RECENT_FILES = 20;\n/** Max items in `recent_tool_uses`. Inputs over this are rejected. */\nconst MAX_RECENT_TOOLS = 50;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n operator_id: {\n type: 'string',\n minLength: 1,\n description: 'Stable identifier (UUID expected) of the operator owning this session.',\n },\n session_id: {\n type: 'string',\n minLength: 1,\n description: 'Stable identifier (UUID expected) of the session reporting state.',\n },\n agent_type: {\n type: 'string',\n enum: [...VALID_AGENT_TYPES],\n description: 'Which host agent runtime is reporting (claude-code | codex | cursor | continue).',\n },\n context_used_pct: {\n type: 'number',\n minimum: 0,\n maximum: 100,\n description: 'Current context-window utilization, 0-100. Used to compute the directive.',\n },\n current_goal: {\n type: 'string',\n maxLength: MAX_GOAL_CHARS,\n description: 'Optional one-line goal statement. Truncated server-side at 500 chars.',\n },\n recent_files_touched: {\n type: 'array',\n items: { type: 'string' },\n maxItems: MAX_RECENT_FILES,\n description: `Optional list of recent file paths touched in the session. Capped at ${MAX_RECENT_FILES} items.`,\n },\n recent_tool_uses: {\n type: 'array',\n items: { type: 'string' },\n maxItems: MAX_RECENT_TOOLS,\n description: `Optional list of recent tool names invoked in the session. Capped at ${MAX_RECENT_TOOLS} items.`,\n },\n },\n required: ['operator_id', 'session_id', 'agent_type', 'context_used_pct'],\n additionalProperties: false,\n};\n\nexport const description =\n \"Reports per-session context-window utilization to VO and returns a directive: 'continue' (under 70%), 'prepare_handoff' (70-84%), or 'execute_handoff_now' (\u226585%). Implements V1 launch gate #9 (fleet context lifecycle management) per the official VO roadmap. Cloud-control-plane mode when VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN + VO_TENANT_ID are set; auto-allocates the session on first report so interactive agents (Claude Code, Cursor, Codex, Continue) appear on the live fleet whiteboard. Stub-local fallback when cloud config is absent or fails. The response shape stays stable across modes (`backend_mode` field in the payload tells the caller which mode produced the verdict).\";\n\ninterface ToolInput {\n readonly operator_id: string;\n readonly session_id: string;\n readonly agent_type: AgentType;\n readonly context_used_pct: number;\n readonly current_goal?: string;\n readonly recent_files_touched?: readonly string[];\n readonly recent_tool_uses?: readonly string[];\n}\n\nfunction isStringArray(v: unknown, maxItems: number): v is readonly string[] {\n if (!Array.isArray(v)) return false;\n if (v.length > maxItems) return false;\n return v.every((item) => typeof item === 'string');\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (typeof o['operator_id'] !== 'string' || o['operator_id'].length === 0) return false;\n if (typeof o['session_id'] !== 'string' || o['session_id'].length === 0) return false;\n if (typeof o['agent_type'] !== 'string') return false;\n if (!(VALID_AGENT_TYPES as readonly string[]).includes(o['agent_type'])) return false;\n if (typeof o['context_used_pct'] !== 'number') return false;\n if (!Number.isFinite(o['context_used_pct'])) return false;\n if (o['context_used_pct'] < 0 || o['context_used_pct'] > 100) return false;\n if (o['current_goal'] !== undefined && typeof o['current_goal'] !== 'string') return false;\n if (o['recent_files_touched'] !== undefined && !isStringArray(o['recent_files_touched'], MAX_RECENT_FILES)) {\n return false;\n }\n if (o['recent_tool_uses'] !== undefined && !isStringArray(o['recent_tool_uses'], MAX_RECENT_TOOLS)) {\n return false;\n }\n return true;\n}\n\n/**\n * Output payload shape. Schema-v1 lock \u2014 new fields MUST be additive +\n * optional, never required-shape changes. Mirrors V1 gate #9 contract.\n */\nexport interface ReportSessionStatePayload {\n /** Directive the agent should follow this turn. */\n readonly directive: SessionDirective;\n /** Human-readable message explaining the directive. */\n readonly message: string;\n /** When directive === 'execute_handoff_now', the canonical handoff doc path. Otherwise absent. */\n readonly handoff_path?: string;\n /** Echoed for caller correlation. */\n readonly session_id: string;\n /** Echoed for caller correlation. */\n readonly operator_id: string;\n /** Echoed for caller correlation. */\n readonly agent_type: AgentType;\n /** Echoed back so the caller can confirm the value was received. */\n readonly context_used_pct: number;\n /**\n * Which backend produced this verdict. V1: 'stub-local' always.\n * Phase 3: 'cloud-control-plane' when wired to the deployed HTTP API.\n */\n readonly backend_mode: 'stub-local' | 'cloud-control-plane';\n /** Server-side ISO timestamp. */\n readonly ts: string;\n /** Hard-locked at 1. */\n readonly schema_version: 1;\n}\n\n/**\n * Cloud mode detection \u2014 checks if VO_CONTROL_PLANE_URL, VO_CONTROL_PLANE_ADMIN_TOKEN,\n * and VO_TENANT_ID are set. When all three are present, cloud mode is available;\n * otherwise fall back to stub-local.\n */\ninterface CloudConfig {\n readonly url: string;\n readonly token: string;\n readonly tenant_id: string;\n}\n\nfunction getCloudConfig(): CloudConfig | null {\n const url = process.env['VO_CONTROL_PLANE_URL'];\n const token = process.env['VO_CONTROL_PLANE_ADMIN_TOKEN'];\n const tenant_id = process.env['VO_TENANT_ID'];\n if (!url || !token || !tenant_id) return null;\n return { url, token, tenant_id };\n}\n\n/**\n * Cloud-mode response shape from `POST /api/v1/session/:id/report-state`.\n * Matches `cloud-run/vo-control-plane/src/routes/session.ts` handleReportSessionState.\n */\ninterface CloudReportStateResponse {\n readonly ok: boolean;\n readonly session?: {\n readonly session_id: string;\n readonly operator_id: string;\n readonly agent_type: string;\n readonly context_used_pct: number;\n readonly current_goal: string;\n readonly last_directive: SessionDirective;\n readonly last_seen_at: string;\n };\n readonly directive?: {\n readonly action: SessionDirective;\n readonly message: string;\n };\n readonly error?: string;\n}\n\nasync function tryCloudReportState(\n cloud: CloudConfig,\n input: ToolInput,\n): Promise<ReportSessionStatePayload | null> {\n try {\n const reportBody: Record<string, unknown> = {\n context_used_pct: input.context_used_pct,\n };\n if (input.current_goal !== undefined) reportBody['current_goal'] = input.current_goal;\n if (input.recent_files_touched !== undefined) {\n reportBody['recent_files_touched'] = input.recent_files_touched;\n }\n if (input.recent_tool_uses !== undefined) {\n reportBody['recent_tool_uses'] = input.recent_tool_uses;\n }\n\n const reportUrl = `${cloud.url}/api/v1/session/${input.session_id}/report-state`;\n let response = await fetch(reportUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${cloud.token}`,\n },\n body: JSON.stringify(reportBody),\n });\n\n // Auto-allocate on 404 (session not found), then retry once\n if (response.status === 404) {\n const allocateBody: Record<string, unknown> = {\n operator_id: input.operator_id,\n tenant_id: cloud.tenant_id,\n agent_type: input.agent_type,\n current_goal: input.current_goal ?? 'Interactive session',\n };\n if (input.context_used_pct > 0) {\n allocateBody['initial_context_used_pct'] = input.context_used_pct;\n }\n\n const allocateUrl = `${cloud.url}/api/v1/session`;\n const allocateResponse = await fetch(allocateUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${cloud.token}`,\n },\n body: JSON.stringify(allocateBody),\n });\n\n if (!allocateResponse.ok) {\n // Allocate failed \u2014 fall back to stub-local\n return null;\n }\n\n // Retry the report-state call\n response = await fetch(reportUrl, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${cloud.token}`,\n },\n body: JSON.stringify(reportBody),\n });\n }\n\n if (!response.ok) {\n // Server error or still 404 after allocate+retry \u2014 fall back to stub\n return null;\n }\n\n const data: CloudReportStateResponse = await response.json() as CloudReportStateResponse;\n if (!data.ok || !data.session || !data.directive) {\n return null;\n }\n\n // Map cloud response to tool payload shape\n return {\n directive: data.directive.action,\n message: data.directive.message,\n session_id: data.session.session_id,\n operator_id: data.session.operator_id,\n agent_type: input.agent_type,\n context_used_pct: data.session.context_used_pct,\n backend_mode: 'cloud-control-plane',\n ts: data.session.last_seen_at,\n schema_version: 1,\n ...(data.directive.action === 'execute_handoff_now'\n ? { handoff_path: suggestedHandoffPath(data.session.session_id, data.session.last_seen_at) }\n : {}),\n };\n } catch {\n // Network error, parse error, etc. \u2014 fail open to stub-local\n return null;\n }\n}\n\nexport async function handleReportSessionState(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Required fields: operator_id (non-empty string), session_id (non-empty string), ' +\n `agent_type (one of: ${VALID_AGENT_TYPES.join(' | ')}), context_used_pct (number 0-100). ` +\n `Optional: current_goal (string \u2264${MAX_GOAL_CHARS} chars), ` +\n `recent_files_touched (string[] \u2264${MAX_RECENT_FILES}), ` +\n `recent_tool_uses (string[] \u2264${MAX_RECENT_TOOLS}).`,\n );\n }\n\n // Try cloud mode first if configured\n const cloud = getCloudConfig();\n if (cloud !== null) {\n const cloudPayload = await tryCloudReportState(cloud, rawInput);\n if (cloudPayload !== null) {\n return jsonContent(cloudPayload);\n }\n // Cloud call failed \u2014 fall through to stub-local\n }\n\n // Stub-local fallback\n const directive = computeDirective(rawInput.context_used_pct);\n const ts = deps.now().toISOString();\n const payload: ReportSessionStatePayload = {\n directive,\n message: directiveMessage(directive),\n session_id: rawInput.session_id,\n operator_id: rawInput.operator_id,\n agent_type: rawInput.agent_type,\n context_used_pct: rawInput.context_used_pct,\n backend_mode: 'stub-local',\n ts,\n schema_version: 1,\n ...(directive === 'execute_handoff_now'\n ? { handoff_path: suggestedHandoffPath(rawInput.session_id, ts) }\n : {}),\n };\n\n return jsonContent(payload);\n}\n", "/**\n * Tool: `vo_spawn_successor` \u2014 Mode B auto-handoff (Phase 3 PR-AB;\n * `docs/vo/vo-roadmap-2026-05-26.md` \u00A73.4 step 5 Mode B).\n *\n * Spawns a DETACHED headless `claude -p` successor with the handoff doc\n * pre-injected into its initial prompt, so a context-exhausted session can hand\n * the lane to a fresh agent without the operator re-orienting it. The V3\n * PR-watcher spawn pattern: prompt via STDIN (never argv \u2014 on Windows `claude`\n * is a .cmd shim needing shell:true, and argv+shell would be an injection\n * hole), `detached` + `unref` so the MCP server doesn't hold the child,\n * stdout/stderr appended to a log under `~/.vo/successors/`.\n *\n * ADR-001 boundary: the successor is a WORKER continuing the lane under the\n * same gates as any session (verify-before-act, human merge approval). This\n * tool only ever acts when explicitly invoked \u2014 it is NOT an autonomous\n * trigger (`project_no_automatic_agent_triggers`).\n */\nimport { spawn } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { existsSync, mkdirSync, openSync, readFileSync, readdirSync, statSync } from 'node:fs';\nimport type { ToolDeps } from '../../types.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\n\nexport const TOOL_NAME = 'vo_spawn_successor';\n\n/** Hard cap on the handoff size injected into the successor prompt. */\nconst MAX_HANDOFF_BYTES = 64_000;\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n handoff_path: {\n type: 'string',\n description: 'Path to the handoff doc to pre-inject. Default: the newest .md in ~/.vo/handoffs/.',\n },\n goal: {\n type: 'string',\n description: 'Optional one-line goal override appended after the handoff.',\n },\n cwd: {\n type: 'string',\n description: 'Working directory for the successor (default: the repo the handoff names, else process cwd).',\n },\n max_turns: {\n type: 'number',\n description: 'Optional --max-turns bound for the successor.',\n },\n },\n required: [],\n additionalProperties: false,\n};\n\nexport const description =\n 'Mode B auto-handoff (roadmap \u00A73.4): spawn a DETACHED headless `claude -p` successor with a handoff doc pre-injected into its prompt. Defaults to the newest handoff in ~/.vo/handoffs/. Returns {spawned, pid, log_path, handoff_path}. The successor works under the same gates as any session (ADR-001: verify-before-act, human merge approval) \u2014 this tool never fires autonomously.';\n\ninterface ToolInput {\n readonly handoff_path?: string;\n readonly goal?: string;\n readonly cwd?: string;\n readonly max_turns?: number;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (o['handoff_path'] !== undefined && typeof o['handoff_path'] !== 'string') return false;\n if (o['goal'] !== undefined && typeof o['goal'] !== 'string') return false;\n if (o['cwd'] !== undefined && typeof o['cwd'] !== 'string') return false;\n if (o['max_turns'] !== undefined && typeof o['max_turns'] !== 'number') return false;\n return true;\n}\n\n/** Newest .md in ~/.vo/handoffs (by mtime), or null. Exported for tests. */\nexport function newestHandoff(dir = join(homedir(), '.vo', 'handoffs')): string | null {\n try {\n const entries = readdirSync(dir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => ({ f, m: statSync(join(dir, f)).mtimeMs }))\n .sort((a, b) => b.m - a.m);\n return entries.length > 0 && entries[0] ? join(dir, entries[0].f) : null;\n } catch {\n return null;\n }\n}\n\n/** Compose the successor's initial prompt. Exported for tests. */\n/**\n * The mandatory onboarding reading list every dispatched/successor agent must\n * read first. Mirrors AGENTS.md's \"spawned subagent\" contract + the VO doctrine\n * docs that CLAUDE.md only references (so they are NOT auto-loaded). Kept in\n * sync with scripts/virtual-office/code-runner/dispatch-onboarding.mjs.\n */\nconst MANDATORY_READS = [\n 'CLAUDE.md + AGENTS.md + README.md (repo root)',\n 'docs/current/virtual-office-agent-charter.md, -operating-model.md, -test-architect.md',\n 'docs/current/evidence-grounded-consensus-testing.md',\n 'docs/vo/ADR-001-* (verify + sign, human approves merge; no autonomous bot-merge / headless triggers) + docs/vo/vo-adr-002-two-plane-moat.md',\n 'docs/vo/vo-roadmap-2026-05-26.md (read the Change log tail for current state)',\n 'the operator memory index ~/.claude/projects/C--Users-greyl/memory/MEMORY.md',\n];\n\nexport function buildSuccessorPrompt(handoffMarkdown: string, goal?: string): string {\n const reads = MANDATORY_READS.map((r, i) => ` ${i + 1}. ${r}`).join('\\n');\n const lines = [\n 'You are the SUCCESSOR agent for a Virtual Office lane. The previous session',\n 'exhausted its context and wrote the handoff below. Read it fully, verify its',\n '\"verification needed\" items against live state (a handoff is a claim, not',\n 'evidence \u2014 verify via `git show origin/main:<path>`), then continue the lane.',\n '',\n 'MANDATORY READS before writing any code (NOT all auto-loaded \u2014 open them):',\n reads,\n '',\n 'NON-NEGOTIABLES: multi-model consensus verification is the core; test honesty',\n '(verified-answer-only, no fake green); verify-before-act + human merge approval;',\n 'never a full functions-shared deploy; Gen2 only; work in a worktree on your own',\n 'branch; finish line is MERGED + DEPLOYED + LIVE-VERIFIED, and VO changes update',\n 'the roadmap in the same PR.',\n '',\n '--- HANDOFF ---',\n handoffMarkdown,\n '--- END HANDOFF ---',\n ];\n if (goal && goal.trim().length > 0) lines.push('', `OPERATOR GOAL OVERRIDE: ${goal.trim()}`);\n return lines.join('\\n');\n}\n\n/** Build argv for the detached successor. Exported for tests. */\nexport function buildSuccessorArgs(maxTurns?: number): string[] {\n const args = ['-p', '--permission-mode', 'acceptEdits'];\n if (Number.isInteger(maxTurns) && (maxTurns as number) > 0) {\n args.push('--max-turns', String(maxTurns));\n }\n return args;\n}\n\nexport type SpawnLike = (\n cmd: string,\n args: string[],\n opts: Record<string, unknown>,\n) => { pid?: number; stdin: { write(s: string): void; end(): void }; unref(): void; on(ev: string, cb: (e: Error) => void): void };\n\nexport async function handleSpawnSuccessor(\n _deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n spawnImpl: SpawnLike = spawn as unknown as SpawnLike,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(TOOL_NAME, 'invalid input. Optional: { handoff_path, goal, cwd, max_turns }.');\n }\n const handoffPath = rawInput.handoff_path?.trim() || newestHandoff();\n if (!handoffPath || !existsSync(handoffPath)) {\n return jsonContent({\n tool: TOOL_NAME,\n schema_version: 1,\n payload: {\n spawned: false,\n reason: rawInput.handoff_path\n ? `handoff not found: ${rawInput.handoff_path}`\n : 'no handoff docs in ~/.vo/handoffs \u2014 write one first (the 85% directive does this)',\n },\n });\n }\n const handoff = readFileSync(handoffPath, 'utf8').slice(0, MAX_HANDOFF_BYTES);\n const prompt = buildSuccessorPrompt(handoff, rawInput.goal);\n\n // Env override keeps tests hermetic (no writes into the real home dir).\n const logDir = process.env['VO_MCP_SUCCESSOR_LOG_DIR']?.trim() || join(homedir(), '.vo', 'successors');\n mkdirSync(logDir, { recursive: true });\n const logPath = join(logDir, `successor-${Date.now()}.log`);\n const logFd = openSync(logPath, 'a');\n\n const child = spawnImpl('claude', buildSuccessorArgs(rawInput.max_turns), {\n cwd: rawInput.cwd?.trim() || process.cwd(),\n detached: true,\n stdio: ['pipe', logFd, logFd],\n // Windows: `claude` is a .cmd shim \u2014 needs a shell to resolve. The prompt\n // goes via STDIN below, never argv, so the shell never sees it.\n shell: process.platform === 'win32',\n windowsHide: true,\n });\n let spawnError: string | null = null;\n child.on('error', (e: Error) => {\n spawnError = e.message;\n });\n try {\n child.stdin.write(prompt);\n child.stdin.end();\n } catch {\n /* 'error' handler captures the failure */\n }\n child.unref();\n // Give a synchronous ENOENT a beat to surface before reporting success.\n await new Promise((r) => setTimeout(r, 150));\n\n return jsonContent({\n tool: TOOL_NAME,\n schema_version: 1,\n payload: spawnError\n ? { spawned: false, reason: `spawn failed: ${spawnError}`, handoff_path: handoffPath }\n : { spawned: true, pid: child.pid ?? null, log_path: logPath, handoff_path: handoffPath },\n });\n}\n", "/**\n * Shared helpers for the concierge/* MCP tool family.\n *\n * Mirrors the heal/* and pr/* family pattern (common-heal.ts /\n * common-pr.ts): one helper module per family carrying the gate type +\n * stub reason constants and a stub-mode envelope builder. Tool handlers\n * (`dispatch.ts`) call `buildCloudOrStubResponse` from `../cloud-call.ts`\n * which branches on `deps.adminCallables` to choose stub vs cloud path.\n *\n * The concierge family routes provider-scoped knowledge packs by\n * `tenant.cloud_provider`:\n *\n * - Caller provides `pack` directly \u2192 vo-control-plane returns that\n * pack's README + file index\n * - Caller provides `tenant_id` only \u2192 vo-control-plane looks up the\n * Tenant's `cloud_provider` and maps to the right pack (defaults to\n * `gcp` if `cloud_provider === 'unspecified'`)\n * - Caller provides both \u2192 `pack` wins (explicit overrides routing)\n *\n * The wire body (camelCase) passed to vo-control-plane mirrors that\n * shape: `{ pack?, tenantId? }`.\n *\n * V1 ships as a stub because the server-side endpoint\n * `/api/v1/admin/concierge/dispatch` is not yet implemented in\n * vo-control-plane. The MCP tool surface is live so cross-vendor MCP\n * clients (Cursor / Continue / Codex) have the contract; the cloud-mode\n * wiring lands in a separate vo-control-plane PR.\n */\n\n/** Reason text shared by every concierge-family stub. */\nexport const CONCIERGE_STUB_REASON =\n '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.';\n\n/**\n * Gate type for telemetry events. Distinct from `admin-action` (heal/pr\n * families) and from `kb-prefilter` (architecture-review family) so\n * fleet observability can slice concierge traffic separately.\n */\nexport const CONCIERGE_GATE_TYPE = 'concierge-dispatch' as const;\n\n/**\n * The 8 packs currently shipped under `vo-claude-plugin/concierge/*` as of\n * 2026-05-29. Kept here for stub-mode reference + cross-vendor consumers\n * that want to enumerate available packs without a server round-trip.\n *\n * **Sync invariant:** when packs change in `vo-claude-plugin/concierge/`,\n * update this list AND `vo-claude-plugin/commands/vo-concierge.md` AND\n * the server-side authoritative list (vo-control-plane). The vo-control-\n * plane endpoint is the source of truth at runtime; this constant is for\n * stub-mode + offline enumeration only.\n */\nexport const KNOWN_CONCIERGE_PACKS = [\n 'gcp',\n 'firebase',\n 'aws',\n 'cloudflare',\n 'vercel',\n 'netlify',\n 'tax',\n 'hybrid',\n] as const;\n\nexport type ConciergePackName = typeof KNOWN_CONCIERGE_PACKS[number];\n\n/** Type-guard for ConciergePackName. */\nexport function isKnownConciergePack(value: unknown): value is ConciergePackName {\n return typeof value === 'string' && (KNOWN_CONCIERGE_PACKS as readonly string[]).includes(value);\n}\n", "/**\n * Tool: `vo_concierge_dispatch`\n *\n * Provider-scoped knowledge-pack dispatcher. Cross-vendor MCP equivalent\n * of the Claude-Code-only `/vo-concierge` slash command in\n * `vo-claude-plugin/commands/vo-concierge.md`. Cursor, Continue, and\n * Codex have no slash-command surface \u2014 they reach packs through this\n * tool instead.\n *\n * Input shape:\n * {\n * pack?: 'gcp' | 'firebase' | 'aws' | 'cloudflare' | 'vercel'\n * | 'netlify' | 'tax' | 'hybrid' // explicit pack\n * tenant_id?: string // route via tenant.cloud_provider\n * }\n *\n * Routing precedence (resolved server-side by vo-control-plane):\n * 1. `pack` if provided \u2192 return that pack\n * 2. `tenant_id` if provided \u2192 look up Tenant.cloud_provider, map to pack\n * 3. neither \u2192 server-side default behavior (lists available packs)\n *\n * Output shape (cloud mode \u2014 when vo-control-plane returns success):\n * {\n * verdict: 'pass',\n * response_data: {\n * pack_name: ConciergePackName,\n * readme_markdown: string, // README.md verbatim\n * file_index: Array<{\n * filename: string, // e.g. 'iam-and-secrets.md'\n * description: string, // one-liner\n * }>,\n * routed_from?: 'pack' | 'tenant_cloud_provider' | 'default',\n * }\n * }\n *\n * Returns `verdict: 'unimplemented'` only when this MCP runtime has no\n * cloud mode (adminCallables null \u2014 set VO_CONTROL_PLANE_URL +\n * VO_CONTROL_PLANE_ADMIN_TOKEN to enable). The server-side\n * `/api/v1/admin/concierge/dispatch` endpoint is built + deployed\n * (vo-control-plane #5724/#5734); in cloud mode this returns\n * `verdict: 'pass'` with the routed pack content.\n *\n * Cache: stub responses are NOT cached (HP2-1 hardening \u2014 same rule as\n * other unimplemented envelopes); cloud-mode `pass` responses are also\n * not cached in V1 because pack content is small (~5 KB) and changes\n * with every plugin release. PR 4 of the concierge series can revisit.\n */\nimport type { ToolDeps } from '../../types.js';\nimport type { jsonContent } from '../common.js';\nimport { invalidParams, type ToolInputSchema } from '../common.js';\nimport { buildCloudOrStubResponse } from '../cloud-call.js';\nimport {\n CONCIERGE_GATE_TYPE,\n CONCIERGE_STUB_REASON,\n KNOWN_CONCIERGE_PACKS,\n isKnownConciergePack,\n} from './common-concierge.js';\n\nexport const TOOL_NAME = 'vo_concierge_dispatch';\nconst CALLABLE_NAME = 'voConciergeDispatch' as const;\nconst ADMIN_PATH = '/api/v1/admin/concierge/dispatch' as const;\n\ninterface ConciergeDispatchInput {\n readonly pack?: string;\n readonly tenant_id?: string;\n}\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n pack: {\n type: 'string',\n description: `Explicit knowledge pack name. One of: ${KNOWN_CONCIERGE_PACKS.join(', ')}. When provided, overrides tenant-based routing. Omit to let server route by tenant_id.`,\n enum: [...KNOWN_CONCIERGE_PACKS],\n },\n tenant_id: {\n type: 'string',\n description: 'Tenant identifier. When provided (and pack is omitted), the server looks up Tenant.cloud_provider and dispatches to the matching pack. Ignored when pack is also provided.',\n },\n },\n additionalProperties: false,\n};\n\nexport const description =\n \"Dispatches a provider-scoped knowledge pack (gcp | firebase | aws | cloudflare | vercel | netlify | tax | hybrid). Cross-vendor MCP equivalent of the /vo-concierge Claude-Code slash command. Route explicitly via `pack`, or via tenant.cloud_provider by passing `tenant_id`. Returns the pack's README (`readme_markdown`) + file index. In cloud mode, dispatches via vo-control-plane and returns `verdict: 'pass'` with the pack/directory data; without cloud config, returns `verdict: 'unimplemented'`.\";\n\nfunction isToolInput(v: unknown): v is ConciergeDispatchInput {\n if (typeof v !== 'object' || v === null) return false;\n const obj = v as Record<string, unknown>;\n if (obj.pack !== undefined && typeof obj.pack !== 'string') return false;\n if (obj.tenant_id !== undefined && typeof obj.tenant_id !== 'string') return false;\n return true;\n}\n\nexport async function handleConciergeDispatch(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Expected { pack?: string, tenant_id?: string }.',\n );\n }\n\n // Validate `pack` value early so cross-vendor clients get a clear\n // error for unknown packs without round-tripping to cloud mode. We\n // tolerate empty/missing \u2014 that's the \"route via tenant_id\" path.\n if (rawInput.pack !== undefined && rawInput.pack !== '' && !isKnownConciergePack(rawInput.pack)) {\n throw invalidParams(\n TOOL_NAME,\n `unknown pack: ${JSON.stringify(rawInput.pack)}. Known packs: ${KNOWN_CONCIERGE_PACKS.join(', ')}.`,\n );\n }\n\n // Build the normalized input passed through to cloud / stub. Strip\n // undefined fields so log excerpts are clean.\n const normalizedInput: Record<string, unknown> = {};\n if (rawInput.pack) normalizedInput.pack = rawInput.pack;\n if (rawInput.tenant_id) normalizedInput.tenant_id = rawInput.tenant_id;\n\n // Cloud body uses camelCase per vo-control-plane convention.\n const cloudBody: Record<string, unknown> = {};\n if (rawInput.pack) cloudBody.pack = rawInput.pack;\n if (rawInput.tenant_id) cloudBody.tenantId = rawInput.tenant_id;\n\n return buildCloudOrStubResponse({\n toolName: TOOL_NAME,\n callableName: CALLABLE_NAME,\n adminPath: ADMIN_PATH,\n normalizedInput,\n cloudBody,\n // Concierge dispatch returns `{ok, mode, routed_from, pack|packs}`,\n // NOT the callable-proxy `{ok, callable, result}` shape \u2014 take the\n // raw envelope as response_data instead of mandating `.result`.\n rawEnvelope: true,\n gateType: CONCIERGE_GATE_TYPE,\n stubReason: CONCIERGE_STUB_REASON,\n // Read-only pack fetch \u2014 stays live under VO_ADMIN_CALLABLES_READONLY.\n readOnly: true,\n deps,\n });\n}\n", "/**\n * Tool: `vo_sync_config` \u2014 cloud memory sync (Increment 7, Lane 2 PR2).\n *\n * Syncs memory entries between local `~/.claude/projects/<slug>/memory/` and\n * the cloud control-plane `/api/v1/agent-config/memory/me` routes (merged #6593).\n *\n * Auth: OPERATOR-role only. Uses the stored `vo_credential` from `vo-mcp login`\n * (NOT the admin god-token). Reads from `credential-store.ts` via `auth-token-source.ts`\n * (same pattern as all cloud tools). Fails closed when no credential is stored.\n *\n * Actions:\n * - **pull**: GET `/me` \u2192 write each entry's `content` to local memory files.\n * - **push**: read local memory dir \u2192 GET `/me` to map file_name\u2192memory_id \u2192\n * PUT `/:memory_id` if exists, else POST `/me` (idempotent; entry_type='index'\n * for MEMORY.md else 'topic').\n */\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';\nimport type { ToolDeps } from '../../types.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\n\nexport const TOOL_NAME = 'vo_sync_config';\n\nexport const inputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n action: {\n type: 'string',\n enum: ['pull', 'push'],\n description: 'pull: download cloud memory to local files. push: upload local files to cloud.',\n },\n cwd: {\n type: 'string',\n description: 'Working directory to derive project slug from (default: process.cwd()).',\n },\n },\n required: ['action'],\n additionalProperties: false,\n};\n\nexport const description =\n '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.';\n\ninterface ToolInput {\n readonly action: 'pull' | 'push';\n readonly cwd?: string;\n}\n\nfunction isToolInput(v: unknown): v is ToolInput {\n if (typeof v !== 'object' || v === null) return false;\n const o = v as Record<string, unknown>;\n if (o['action'] !== 'pull' && o['action'] !== 'push') return false;\n if (o['cwd'] !== undefined && typeof o['cwd'] !== 'string') return false;\n return true;\n}\n\n/**\n * Derive the Claude Code project slug from a cwd. Mirrors how Claude Code\n * generates project dirs under `~/.claude/projects/`: the drive letter is\n * upper-cased, then EVERY non-alphanumeric character (`:`, path separators,\n * spaces, dots, \u2026) becomes a single `-`. Consecutive separators are NOT\n * collapsed, so the `C:\\` prefix yields `C--` (the colon and the separator\n * each map to a dash).\n *\n * Verified against the real on-disk dirs under `~/.claude/projects/`:\n * `C:\\Users\\greyl` \u2192 `C--Users-greyl`\n * `C:\\my apps\\Nexus` \u2192 `C--my-apps-Nexus`\n */\nexport function deriveProjectSlug(cwd: string): string {\n return cwd\n .replace(/\\\\/g, '/') // normalize Windows separators\n .replace(/\\/+$/g, '') // strip trailing separator(s) so they don't become trailing dashes\n .replace(/^([a-zA-Z]):/, (_m, drive: string) => `${drive.toUpperCase()}:`) // upper-case the drive letter\n .replace(/[^a-zA-Z0-9]/g, '-'); // every remaining non-alphanumeric char \u2192 a single dash\n}\n\n/**\n * Get the local memory directory path for the given cwd.\n */\nexport function getMemoryDir(cwd: string): string {\n const slug = deriveProjectSlug(cwd);\n return join(homedir(), '.claude', 'projects', slug, 'memory');\n}\n\ninterface MemoryEntry {\n readonly memory_id: string;\n readonly operator_id: string;\n readonly entry_type: 'index' | 'topic';\n readonly file_name: string;\n readonly content: string;\n readonly session_id?: string;\n readonly created_at?: string;\n readonly updated_at?: string;\n}\n\ninterface GetMemoryResponse {\n readonly ok: boolean;\n readonly entries: MemoryEntry[];\n}\n\ninterface CreateMemoryBody {\n readonly entry_type: 'index' | 'topic';\n readonly file_name: string;\n readonly content: string;\n readonly session_id?: string;\n}\n\ninterface UpdateMemoryBody {\n readonly content: string;\n readonly session_id?: string;\n}\n\ninterface CreateMemoryResponse {\n readonly ok: boolean;\n readonly memory_id?: string;\n}\n\ninterface UpdateMemoryResponse {\n readonly ok: boolean;\n}\n\n/**\n * Minimal fetch-like surface for testability. Production uses `globalThis.fetch`.\n */\nexport type FetchLike = (\n url: string,\n init?: {\n method?: string;\n headers?: Record<string, string>;\n body?: string;\n },\n) => Promise<{\n status: number;\n text: () => Promise<string>;\n}>;\n\n/**\n * Pull cloud memory entries to local files.\n */\nasync function pullMemory(\n controlPlaneUrl: string,\n token: string,\n memoryDir: string,\n fetchFn: FetchLike,\n): Promise<{ pulled: number; files: string[] }> {\n const url = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\n const response = await fetchFn(url, {\n method: 'GET',\n headers: {\n authorization: `Bearer ${token}`,\n },\n });\n\n if (response.status !== 200) {\n const text = await response.text();\n throw new Error(`GET /api/v1/agent-config/memory/me returned HTTP ${response.status}: ${text.slice(0, 200)}`);\n }\n\n const data = JSON.parse(await response.text()) as GetMemoryResponse;\n if (!data.ok || !Array.isArray(data.entries)) {\n throw new Error('GET /api/v1/agent-config/memory/me response missing ok=true or entries array');\n }\n\n mkdirSync(memoryDir, { recursive: true });\n const files: string[] = [];\n\n for (const entry of data.entries) {\n const filePath = join(memoryDir, entry.file_name);\n writeFileSync(filePath, entry.content, 'utf8');\n files.push(entry.file_name);\n }\n\n return { pulled: data.entries.length, files };\n}\n\n/**\n * Push local memory files to cloud.\n */\nasync function pushMemory(\n controlPlaneUrl: string,\n token: string,\n memoryDir: string,\n sessionId: string,\n fetchFn: FetchLike,\n): Promise<{ pushed: number; created: number; updated: number }> {\n if (!existsSync(memoryDir)) {\n return { pushed: 0, created: 0, updated: 0 };\n }\n\n // Read local files\n const localFiles = readdirSync(memoryDir)\n .filter((f) => f.endsWith('.md'))\n .map((f) => ({\n file_name: f,\n content: readFileSync(join(memoryDir, f), 'utf8'),\n entry_type: (f === 'MEMORY.md' ? 'index' : 'topic') as 'index' | 'topic',\n }));\n\n if (localFiles.length === 0) {\n return { pushed: 0, created: 0, updated: 0 };\n }\n\n // GET existing entries to map file_name \u2192 memory_id\n const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\n const getResponse = await fetchFn(getUrl, {\n method: 'GET',\n headers: {\n authorization: `Bearer ${token}`,\n },\n });\n\n const existingMap = new Map<string, string>();\n if (getResponse.status === 200) {\n const getData = JSON.parse(await getResponse.text()) as GetMemoryResponse;\n if (getData.ok && Array.isArray(getData.entries)) {\n for (const entry of getData.entries) {\n existingMap.set(entry.file_name, entry.memory_id);\n }\n }\n }\n\n let created = 0;\n let updated = 0;\n\n for (const localFile of localFiles) {\n const memoryId = existingMap.get(localFile.file_name);\n\n if (memoryId) {\n // UPDATE\n const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${memoryId}`;\n const updateBody: UpdateMemoryBody = {\n content: localFile.content,\n session_id: sessionId,\n };\n const updateResponse = await fetchFn(updateUrl, {\n method: 'PUT',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(updateBody),\n });\n\n if (updateResponse.status !== 200) {\n const text = await updateResponse.text();\n throw new Error(\n `PUT /api/v1/agent-config/memory/${memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`,\n );\n }\n\n const updateData = JSON.parse(await updateResponse.text()) as UpdateMemoryResponse;\n if (!updateData.ok) {\n throw new Error(`PUT /api/v1/agent-config/memory/${memoryId} returned ok=false`);\n }\n updated++;\n } else {\n // CREATE\n const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\n const createBody: CreateMemoryBody = {\n entry_type: localFile.entry_type,\n file_name: localFile.file_name,\n content: localFile.content,\n session_id: sessionId,\n };\n const createResponse = await fetchFn(createUrl, {\n method: 'POST',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(createBody),\n });\n\n if (createResponse.status !== 200 && createResponse.status !== 201) {\n const text = await createResponse.text();\n throw new Error(\n `POST /api/v1/agent-config/memory/me returned HTTP ${createResponse.status}: ${text.slice(0, 200)}`,\n );\n }\n\n const createData = JSON.parse(await createResponse.text()) as CreateMemoryResponse;\n if (!createData.ok) {\n throw new Error('POST /api/v1/agent-config/memory/me returned ok=false');\n }\n created++;\n }\n }\n\n return { pushed: localFiles.length, created, updated };\n}\n\n/**\n * Result of a memory sync run \u2014 the same payload shape the MCP tool returns, but\n * as a plain object so the `vo-mcp sync` CLI and the SessionStart/PostToolUse\n * enforcement hooks can consume it without the MCP envelope. Single source of\n * truth for push/pull behaviour across runtimes (Claude Code, Codex, Cursor, the\n * cloud runner \u2014 any environment that can run `node dist/cli.js sync`).\n */\nexport interface MemorySyncResult {\n readonly synced: boolean;\n readonly action?: 'pull' | 'push';\n readonly reason?: string;\n readonly pulled?: number;\n readonly files?: string[];\n readonly pushed?: number;\n readonly created?: number;\n readonly updated?: number;\n readonly memory_dir?: string;\n}\n\n/** Reasons that mean \"cloud sync isn't configured here\" \u2014 a no-op, NOT a failure.\n * The hook/CLI layer treats these as exit-0 so a session is never blocked by the\n * absence of cloud config or a stored credential. */\nexport function isNoopSyncReason(reason: string | undefined): boolean {\n if (!reason) return false;\n return /not set|No auth configured|Failed to obtain auth token/.test(reason);\n}\n\n/**\n * Core memory sync \u2014 resolve the control-plane URL + operator credential, then\n * push or pull `~/.claude/projects/<slug>/memory/`. Shared by the\n * `vo_sync_config` MCP tool AND the `vo-mcp sync` CLI used by the enforcement\n * hooks. Fail-soft: returns `{ synced:false, reason }` when cloud mode is off or\n * auth is missing rather than throwing, so callers can no-op cleanly.\n */\nexport async function runMemorySync(\n action: 'pull' | 'push',\n cwd: string,\n sessionId: string,\n fetchFn: FetchLike = globalThis.fetch as unknown as FetchLike,\n): Promise<MemorySyncResult> {\n const controlPlaneUrl = process.env['VO_CONTROL_PLANE_URL'];\n if (!controlPlaneUrl) {\n return { synced: false, reason: 'VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled' };\n }\n\n // Auth via the existing auth-token-source infra (vo_credential, refresh, etc.).\n const { createAuthTokenSourceFromEnv } = await import('../../cloud/auth-token-source.js');\n const { readStoredCredential } = await import('../../cloud/credential-store.js');\n const tokenSource = createAuthTokenSourceFromEnv(process.env, fetchFn, () => readStoredCredential(process.env));\n if (!tokenSource) {\n return { synced: false, reason: 'No auth configured. Run `vo-mcp login` to authenticate as an operator.' };\n }\n const token = await tokenSource.getToken();\n if (!token) {\n return { synced: false, reason: 'Failed to obtain auth token. Run `vo-mcp login` to re-authenticate.' };\n }\n\n const memoryDir = getMemoryDir(cwd);\n const baseUrl = controlPlaneUrl.replace(/\\/+$/, '');\n try {\n if (action === 'pull') {\n const result = await pullMemory(baseUrl, token, memoryDir, fetchFn);\n return { synced: true, action: 'pull', pulled: result.pulled, files: result.files, memory_dir: memoryDir };\n }\n const result = await pushMemory(baseUrl, token, memoryDir, sessionId, fetchFn);\n return {\n synced: true,\n action: 'push',\n pushed: result.pushed,\n created: result.created,\n updated: result.updated,\n memory_dir: memoryDir,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { synced: false, reason: `Sync failed: ${message}` };\n }\n}\n\nexport async function handleSyncConfig(\n deps: ToolDeps,\n rawInput: unknown,\n _signal?: AbortSignal,\n fetchFn: FetchLike = globalThis.fetch as unknown as FetchLike,\n): Promise<ReturnType<typeof jsonContent>> {\n if (!isToolInput(rawInput)) {\n throw invalidParams(\n TOOL_NAME,\n 'invalid input. Required: { action: \"pull\" | \"push\" }. Optional: { cwd: \"<path>\" }.',\n );\n }\n const cwd = rawInput.cwd?.trim() || process.cwd();\n const result = await runMemorySync(rawInput.action, cwd, deps.session.sessionId, fetchFn);\n return jsonContent({ tool: TOOL_NAME, schema_version: 1, payload: result });\n}\n", "/**\n * Content-hash cache (V1 launch gate #8).\n *\n * Backend: Node's built-in `node:sqlite` (sync, file-backed, Node 22+).\n * Keyed by `sha256(canonicalize(tool_input))`. Cache value =\n * JSON-serialized full tool response envelope.\n *\n * Design notes:\n * - Sync API on purpose. MCP tool handlers are async at the protocol\n * layer but cache hits should not pay an event-loop hop.\n * - `node:sqlite` is \"experimental\" in Node 22 \u2014 but it's the shipped\n * surface; switching to a native bindings library can be done by\n * swapping THIS file alone. The exported `ContentHashCache` interface\n * is the stable boundary.\n * - TTL is optional and stored alongside the value. If `expires_at`\n * is in the past, `get` returns null and deletes the row.\n * - Tests use in-memory DBs (`:memory:` path).\n */\nimport { createHash } from 'node:crypto';\nimport { chmodSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { DatabaseSync } from 'node:sqlite';\nimport { canonicalize, type CanonicalizeOptions } from './canonicalize.js';\n\nexport interface CacheEntry<T> {\n readonly key: string;\n readonly value: T;\n readonly cachedAt: number;\n}\n\nexport interface ContentHashCache {\n /** Compute the stable hash key for a tool input. */\n keyFor(toolName: string, input: unknown, opts?: CanonicalizeOptions): string;\n /** Read a cache entry. Returns null on miss or expired row. */\n get<T>(key: string): CacheEntry<T> | null;\n /** Write a cache entry. `ttlMs` optional \u2014 omit for no expiration. */\n set<T>(key: string, value: T, ttlMs?: number): void;\n /**\n * Delete a single cache entry by key. Returns true if a row was removed,\n * false if the key was already absent. Added PR-Q 2026-05-25 to close a\n * documented gap that previously forced the engine's CacheBackend\n * adapter (`engine-client.ts::createCacheBackendAdapter`) to ship as a\n * silent no-op.\n */\n delete(key: string): boolean;\n /**\n * Remove all cache entries (full purge). Returns the number of rows\n * removed. Added PR-Q 2026-05-25 alongside `delete()`. Useful for\n * operators who want to flush after a `vo_arch_defaults` corpus edit\n * without re-namespacing or `rm`ing the .db file.\n */\n clear(): number;\n /** For tests / diagnostics. */\n size(): number;\n /** Close the underlying handle. Tests must call this to release locks. */\n close(): void;\n}\n\nexport interface SqliteCacheOptions {\n /** Absolute path to the .db file, or ':memory:' for in-memory testing. */\n readonly dbPath: string;\n /**\n * Versioning namespace mixed into every cache key (2026-05-24 audit\n * BLOCKER #3 fix). When the underlying engine, KB corpus, or vo-mcp\n * package itself ships a new version, the namespace string changes\n * and previously-cached envelopes no longer hit \u2014 preventing stale\n * verdicts from old code being returned by new logic.\n *\n * Empty string (or omitted) preserves the pre-2026-05-24 keying\n * behavior \u2014 same input \u2192 same key regardless of version. Tests use\n * the empty default for stable cache-key fixtures.\n *\n * Recommended format (see cli.ts for the production wiring):\n * `vo_mcp@${voMcpVersion}|engine@${engineVersion ?? 'none'}|corpus@${corpusHash ?? 'none'}`\n *\n * Trade-off: on the first run after a version bump, the on-disk\n * cache file is fully invalidated (all keys miss). That's the desired\n * behavior \u2014 the upgrade IS the moment we want fresh results.\n */\n readonly cacheVersionNamespace?: string;\n}\n\ninterface CacheRow {\n readonly key: string;\n readonly value: string;\n readonly cached_at: number;\n readonly expires_at: number | null;\n}\n\nexport function createSqliteCache(options: SqliteCacheOptions): ContentHashCache {\n const fileBacked = options.dbPath !== ':memory:';\n if (fileBacked) {\n // 0o700: only the operator account can list / open the directory.\n // Best-effort on Windows (POSIX bits no-op there; rely on parent ACL).\n mkdirSync(dirname(options.dbPath), { recursive: true, mode: 0o700 });\n }\n // Capture the namespace once at construction. Default '' preserves the\n // pre-2026-05-24 key shape so tests with no namespace get the same keys.\n const versionNamespace = options.cacheVersionNamespace ?? '';\n const db = new DatabaseSync(options.dbPath);\n // WAL: lets multiple MCP-server processes (Claude Code + Cursor + Claude\n // Desktop sharing ~/.claude/vo-mcp-cache.db) read concurrently with a\n // single writer. busy_timeout: absorbs short write contention without\n // SQLITE_BUSY-throwing into tool handlers. PRAGMA NORMAL: durability vs\n // performance trade-off acceptable for a cache (worst case = lose a\n // freshly-written entry on power loss, recomputable from the source).\n // :memory: dbs don't need WAL but the PRAGMA is harmless.\n db.exec('PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000; PRAGMA synchronous=NORMAL;');\n db.exec(`\n CREATE TABLE IF NOT EXISTS cache (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL,\n cached_at INTEGER NOT NULL,\n expires_at INTEGER\n );\n `);\n if (fileBacked) {\n // 0o600 \u2014 readable+writable only by the operator account. The cache may\n // hold canonicalized tool inputs (code snippets, prompts) that, while\n // sanitized at logging time, are NOT scrubbed in the cached value.\n // Best-effort: chmod fails harmlessly on Windows.\n try {\n chmodSync(options.dbPath, 0o600);\n } catch {\n /* ignore \u2014 Windows / unsupported FS */\n }\n }\n\n const insertStmt = db.prepare(\n 'INSERT OR REPLACE INTO cache (key, value, cached_at, expires_at) VALUES (?, ?, ?, ?)',\n );\n const selectStmt = db.prepare(\n 'SELECT key, value, cached_at, expires_at FROM cache WHERE key = ?',\n );\n const deleteStmt = db.prepare('DELETE FROM cache WHERE key = ?');\n const clearAllStmt = db.prepare('DELETE FROM cache');\n const countStmt = db.prepare('SELECT COUNT(*) AS c FROM cache');\n\n return {\n keyFor(toolName: string, input: unknown, opts?: CanonicalizeOptions): string {\n const canonical = canonicalize(input, opts);\n const hash = createHash('sha256');\n // Version namespace is prefixed when non-empty. Empty default = the\n // pre-2026-05-24 key shape (back-compat). Using `|` as a separator\n // because canonical strings start with `{`/`\"`/`[` \u2014 `|` cannot\n // appear in canonicalized output ambiguously.\n if (versionNamespace.length > 0) {\n hash.update(versionNamespace);\n hash.update('|');\n }\n hash.update(toolName);\n hash.update(' ');\n hash.update(canonical);\n return hash.digest('hex');\n },\n\n get<T>(key: string): CacheEntry<T> | null {\n const row = selectStmt.get(key) as CacheRow | undefined;\n if (!row) return null;\n if (row.expires_at !== null && row.expires_at < Date.now()) {\n deleteStmt.run(key);\n return null;\n }\n return {\n key: row.key,\n value: JSON.parse(row.value) as T,\n cachedAt: row.cached_at,\n };\n },\n\n set<T>(key: string, value: T, ttlMs?: number): void {\n const now = Date.now();\n const expiresAt = typeof ttlMs === 'number' ? now + ttlMs : null;\n insertStmt.run(key, JSON.stringify(value), now, expiresAt);\n },\n\n delete(key: string): boolean {\n // node:sqlite `RunResult.changes` is a bigint in some Node 22 builds and\n // a number in others; coerce defensively before comparing.\n const result = deleteStmt.run(key);\n return Number(result.changes) > 0;\n },\n\n clear(): number {\n const result = clearAllStmt.run();\n return Number(result.changes);\n },\n\n size(): number {\n const r = countStmt.get() as { c: number } | undefined;\n return r?.c ?? 0;\n },\n\n close(): void {\n db.close();\n },\n };\n}\n", "/**\n * Canonicalize a tool input value into a stable string for sha256 hashing.\n *\n * Per handoff \u00A77:\n * - object keys sorted alphabetically\n * - whitespace trimmed from strings (interior whitespace preserved; leading/trailing stripped)\n * - line endings normalized to LF\n * - volatile fields (timestamps, session IDs) excluded\n *\n * `excludeKeys` lists keys to drop at ANY depth. Callers pass the set\n * relevant to their tool. We avoid hardcoding a global blacklist because\n * what's \"volatile\" depends on the tool.\n */\n\nconst DEFAULT_VOLATILE_KEYS = new Set<string>([\n 'ts',\n 'timestamp',\n 'session_id',\n 'sessionId',\n 'request_id',\n 'requestId',\n 'nonce',\n]);\n\nexport interface CanonicalizeOptions {\n /** Additional keys to exclude beyond the defaults. */\n readonly excludeKeys?: ReadonlySet<string>;\n}\n\nexport function canonicalize(value: unknown, options: CanonicalizeOptions = {}): string {\n const excluded = new Set<string>([\n ...DEFAULT_VOLATILE_KEYS,\n ...(options.excludeKeys ?? new Set<string>()),\n ]);\n const normalized = normalize(value, excluded);\n return JSON.stringify(normalized);\n}\n\nfunction normalize(value: unknown, excluded: ReadonlySet<string>): unknown {\n if (value === null || value === undefined) return null;\n if (typeof value === 'string') return normalizeString(value);\n if (typeof value === 'number' || typeof value === 'boolean') return value;\n if (Array.isArray(value)) return value.map((item) => normalize(item, excluded));\n if (typeof value === 'object') {\n const out: Record<string, unknown> = {};\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj)\n .filter((k) => !excluded.has(k))\n .sort();\n for (const key of keys) {\n out[key] = normalize(obj[key], excluded);\n }\n return out;\n }\n // Functions, symbols, bigints \u2014 not part of our schema; coerce to string for stability.\n return String(value);\n}\n\nfunction normalizeString(s: string): string {\n // Normalize line endings (CRLF / CR -> LF), then trim leading/trailing\n // whitespace. Interior whitespace and indentation preserved \u2014 they're\n // semantically meaningful (e.g. test bodies).\n return s.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n').trim();\n}\n", "/**\n * Stub ratchet client \u2014 Phase 1 scaffold.\n *\n * Implements `RatchetClient` with a tiny rule set good enough to demonstrate\n * the open shape end-to-end. Detects three common \"hollow assertion\"\n * shapes that any honest test reviewer would flag:\n *\n * - `expect(...).toBeDefined()`\n * - `expect(...).toBeTruthy()` / `toBeFalsy()` against a literal\n * - `expect(true).toBe(true)` style tautologies\n *\n * It also rewards presence of equality / property assertions\n * (`toBe(`, `toEqual(`, `toMatchObject(`, `toContain(`).\n *\n * Phase 2 swaps this whole module for `import { ... } from '@algosuite/ratchets-generic'`.\n * The thresholds below are placeholders, not the real moat content\n * (EXTRACTION_AUDIT.md documents the deferral).\n */\nimport type {\n AssertionStrengthRequest,\n AssertionStrengthResult,\n RatchetClient,\n} from './client.js';\nimport type { AssertionFinding } from '../types.js';\n\nconst HOLLOW_PATTERNS: ReadonlyArray<{\n readonly regex: RegExp;\n readonly code: string;\n readonly severity: AssertionFinding['severity'];\n readonly message: string;\n}> = [\n {\n regex: /expect\\([^)]*\\)\\.toBeDefined\\(\\)/g,\n code: 'hollow.toBeDefined',\n severity: 'warn',\n message: 'toBeDefined() only proves the call returned something; assert a value.',\n },\n {\n regex: /expect\\((true|false|1|0|'[^']*'|\"[^\"]*\")\\)\\.toBe\\(\\1\\)/g,\n code: 'hollow.tautology',\n severity: 'error',\n message: 'Tautological assertion: literal compared to itself.',\n },\n {\n regex: /expect\\([^)]*\\)\\.toBeTruthy\\(\\)|expect\\([^)]*\\)\\.toBeFalsy\\(\\)/g,\n code: 'hollow.toBeTruthy',\n severity: 'warn',\n message: 'toBeTruthy/toBeFalsy hide what value you expected; prefer toBe(...) / toEqual(...).',\n },\n];\n\nconst STRONG_PATTERNS: readonly RegExp[] = [\n /\\.toBe\\(/g,\n /\\.toEqual\\(/g,\n /\\.toMatchObject\\(/g,\n /\\.toContain\\(/g,\n /\\.toHaveBeenCalledWith\\(/g,\n /\\.toStrictEqual\\(/g,\n];\n\n/**\n * Toy heuristic scoring \u2014 DO NOT treat these numbers as the real ratchet.\n * Open-shell deliberately: real thresholds live in the closed library.\n */\nconst MAX_SCORE = 100;\nconst WEAK_FLOOR = 30;\nconst HOLLOW_PENALTY = 25;\nconst HOLLOW_TAUTOLOGY_PENALTY = 50;\nconst STRONG_REWARD_PER = 8;\n\nexport function createStubRatchetClient(): RatchetClient {\n return {\n async checkAssertionStrength(\n req: AssertionStrengthRequest,\n ): Promise<AssertionStrengthResult> {\n const findings: AssertionFinding[] = [];\n let score = 50; // neutral start\n let hollowHits = 0;\n let tautologyHits = 0;\n\n for (const pat of HOLLOW_PATTERNS) {\n // Reset lastIndex in case the regex is reused with /g.\n pat.regex.lastIndex = 0;\n let m: RegExpExecArray | null;\n while ((m = pat.regex.exec(req.source)) !== null) {\n findings.push({\n line_excerpt: clip(m[0], 80),\n severity: pat.severity,\n code: pat.code,\n message: pat.message,\n });\n if (pat.code === 'hollow.tautology') tautologyHits += 1;\n else hollowHits += 1;\n }\n }\n\n let strongHits = 0;\n for (const pat of STRONG_PATTERNS) {\n pat.lastIndex = 0;\n const matches = req.source.match(pat);\n if (matches) strongHits += matches.length;\n }\n\n score -= hollowHits * HOLLOW_PENALTY;\n score -= tautologyHits * HOLLOW_TAUTOLOGY_PENALTY;\n score += strongHits * STRONG_REWARD_PER;\n score = Math.max(0, Math.min(MAX_SCORE, score));\n\n const verdict: AssertionStrengthResult['verdict'] =\n tautologyHits > 0 || (hollowHits > 0 && strongHits === 0)\n ? 'hollow'\n : score < WEAK_FLOOR\n ? 'weak'\n : 'strong';\n\n const summary = buildSummary({\n verdict,\n score,\n strongHits,\n hollowHits,\n tautologyHits,\n filePath: req.filePath,\n });\n\n return { score, maxScore: MAX_SCORE, verdict, findings, summary };\n },\n };\n}\n\nfunction clip(s: string, n: number): string {\n return s.length <= n ? s : s.slice(0, n) + '\u2026';\n}\n\nfunction buildSummary(args: {\n verdict: AssertionStrengthResult['verdict'];\n score: number;\n strongHits: number;\n hollowHits: number;\n tautologyHits: number;\n filePath: string | undefined;\n}): string {\n const fileTag = args.filePath ? `${args.filePath}: ` : '';\n return (\n `${fileTag}verdict=${args.verdict} score=${args.score}/${MAX_SCORE} ` +\n `(strong=${args.strongHits} hollow=${args.hollowHits} tautology=${args.tautologyHits}) ` +\n `[stub-ratchet \u2014 real thresholds load from @algosuite/ratchets-generic in Phase 2]`\n );\n}\n", "/**\n * Null consensus engine client \u2014 returns `ok: false` with a stable reason.\n *\n * Used when the closed `@algosuite/consensus-engine` package is not\n * wired in (Phase 1 default; the engine is loaded only in environments\n * that have real model credentials configured), AND as a diagnostic\n * stub for the more-specific reasons surfaced by `engine-client.ts`\n * (since 2026-05-24 audit MEDIUM \"engine_unavailable reason-string\n * differentiation\" \u2014 the previous single `'consensus-engine-package-\n * pending'` couldn't tell an operator which of \"no API keys\",\n * \"@algosuite/consensus-engine not installed\", \"functions-shared not\n * resolvable\", or \"all panel adapters failed to construct\" was the\n * actual cause).\n *\n * The `vo_consensus_judgment` tool inspects the result and falls back to\n * the existing 'unimplemented' envelope when the engine is unavailable,\n * so cross-vendor MCP clients never see a missing-tool error \u2014 they get\n * `{ verdict: 'unimplemented', reason: '<whichever-specific-reason>' }`\n * which is operator-actionable.\n */\nimport type { ConsensusEngineClient, ConsensusResult } from './client.js';\n\n/**\n * Default reason. Kept stable since 2026-05-21 for back-compat with\n * existing event-log pins and downstream metric dashboards that group\n * on this string. New diagnostic call sites pass their own reason.\n */\nexport const NULL_CLIENT_DEFAULT_REASON = 'consensus-engine-package-pending';\n\n/**\n * Specific reasons emitted by `tryCreateEngineConsensusClientFromEnv*`\n * when it can identify exactly why the engine isn't wired. Exported so\n * tests and downstream observability code can pin them.\n */\nexport const ENGINE_UNAVAILABLE_REASONS = {\n /** Fewer than 2 provider API keys present in env \u2014 can't form a panel. */\n CREDENTIALS_MISSING: 'consensus-credentials-missing',\n /** `@algosuite/consensus-engine` module not resolvable / installed. */\n ENGINE_PACKAGE_NOT_INSTALLED: 'consensus-engine-package-not-installed',\n /** `functions-shared` module not resolvable in the host's node_modules. */\n FUNCTIONS_SHARED_NOT_INSTALLED: 'consensus-functions-shared-not-installed',\n /** Engine + functions-shared loaded but adapter construction failed for all providers. */\n PANEL_CONSTRUCTION_FAILED: 'consensus-panel-construction-failed',\n} as const;\n\nexport type EngineUnavailableReason =\n (typeof ENGINE_UNAVAILABLE_REASONS)[keyof typeof ENGINE_UNAVAILABLE_REASONS]\n | typeof NULL_CLIENT_DEFAULT_REASON;\n\nexport function createNullConsensusEngineClient(\n reason: string = NULL_CLIENT_DEFAULT_REASON,\n): ConsensusEngineClient {\n return {\n async run(): Promise<ConsensusResult> {\n return { ok: false, reason };\n },\n };\n}\n", "/**\n * Engine-backed consensus client.\n *\n * Bridges the open-shell `ConsensusEngineClient` interface to the closed\n * `@algosuite/consensus-engine` package via dynamic import. Phase 1 keeps\n * the open shell free of a build-time dependency on the closed package\n * (mirrors the `ratchets-generic` boundary pattern in `src/ratchets/`).\n *\n * Two factory variants:\n * - `createEngineConsensusClient({ panel })` \u2014 caller supplies the\n * pre-built model adapters. This is the production path: the cli\n * constructs Anthropic + Google adapters from env credentials and\n * hands them to this factory.\n * - `tryCreateEngineConsensusClientFromEnv()` \u2014 convenience wrapper.\n * Phase 1 returns `null` (no credential resolution wired). Phase 2\n * wires real Anthropic + Google SDK adapter construction from\n * `ANTHROPIC_API_KEY` + `GEMINI_API_KEY` env vars.\n *\n * On any engine error the client returns `{ ok: false, reason }` \u2014\n * never throws \u2014 so the `vo_consensus_judgment` tool can fall back to\n * the stable 'unimplemented' envelope.\n */\nimport { randomUUID } from 'node:crypto';\nimport type {\n ConsensusEngineClient,\n ConsensusRequest,\n ConsensusResult,\n ConsensusVerdict,\n} from './client.js';\nimport type { ContentHashCache } from '../cache/sqlite-cache.js';\nimport type { EventsWriter } from '../logging/events-writer.js';\nimport type { SessionContext, ConsensusCallEvent } from '../types.js';\nimport { sanitizeExcerpt } from '../logging/events-writer.js';\nimport { VO_MCP_VERSION, sha256Hex } from '../tools/common.js';\nimport {\n createNullConsensusEngineClient,\n ENGINE_UNAVAILABLE_REASONS,\n} from './null-client.js';\nimport {\n resolveAgreementGate,\n buildSourceGroundedFields,\n mapFanOutDiagnostics,\n mapCitationGrade,\n mapShadowSynthesis,\n shadowEnabled,\n type EngineShadowSynthesisShape,\n} from './engine-options.js';\nimport { createClaimClassifier } from './claim-classifier.js';\n\n// 2026-06-01 boot-hang fix: consensus-engine + functions-shared are loaded\n// via LAZY dynamic import inside loadFactoryAndCallers (NOT static top-level\n// imports). functions-shared transitively pulls firebase-admin + the three\n// provider SDKs; importing it at module-load added ~27s to vo-mcp startup,\n// blowing past the MCP stdio init timeout so the server never connected and\n// ZERO tools registered. The packages remain workspace:* deps \u2014 only the\n// import TIMING moved back to on-first-use, restoring the O(ms) boot the\n// factory docstrings below already promise. See loadFactoryAndCallers.\n\n/**\n * Loosely-typed adapter shape that mirrors the closed engine's\n * `ModelAdapter` interface. Declared structurally here so the open shell\n * doesn't import any closed types at build time. The closed package's\n * own types are the source of truth \u2014 this is the minimum surface the\n * shell needs to forward.\n */\nexport interface ConsensusModelAdapter {\n readonly model: string;\n readonly provider: string;\n call(args: {\n readonly system_prompt: string;\n readonly user_prompt: string;\n readonly abort_signal?: AbortSignal;\n }): Promise<{\n readonly model: string;\n readonly provider: string;\n readonly verdict: ConsensusVerdict | 'error';\n readonly confidence: number;\n readonly reasoning_excerpt: string;\n readonly raw_response_excerpt: string;\n readonly duration_ms: number;\n readonly error?: { readonly category: string; readonly message: string };\n }>;\n}\n\nexport interface EngineClientOptions {\n readonly panel: readonly ConsensusModelAdapter[];\n readonly per_model_timeout_ms?: number;\n /** For testability \u2014 caller may inject the loaded engine module. */\n readonly engineModule?: EngineModuleShape;\n /**\n * BEHAVIOUR-CHANGING opt-in (engine Feature 1 agreement-gate). When set to\n * `true` the engine fans out a cheap probe subset first and skips the rest of\n * the panel when the probe agrees strongly (an early-exit cost saving). When\n * `undefined` (the default) the `VO_CONSENSUS_AGREEMENT_GATE` env var decides;\n * when neither is truthy the whole panel is always called \u2014 UNCHANGED gate\n * behaviour. See engine-options.resolveAgreementGate.\n */\n readonly agreement_gate_enabled?: boolean;\n /** Override env lookups for the agreement-gate flag \u2014 primarily for tests. */\n readonly env?: Readonly<Record<string, string | undefined>>;\n}\n\n/**\n * Structural mirror of the engine's `agreement_gate` option (EngineOptions).\n * Re-declared here so the open shell carries no closed import.\n */\ninterface EngineAgreementGateShape {\n readonly enabled: boolean;\n readonly probe_size?: number;\n readonly modal_agreement_threshold?: number;\n readonly min_confidence?: number;\n}\n\n/** Engine response shape \u2014 shared by `runConsensus` and `runSourceGroundedConsensus`. */\ninterface EngineConsensusResponseShape {\n synthesized_verdict: {\n verdict: ConsensusVerdict;\n confidence: number;\n reasoning_excerpt: string;\n synthesizer: string;\n dissent_summary: string | null;\n /** Engine Feature 2 (calibrated-confidence) \u2014 additive; attached by the engine on every runConsensus. */\n calibrated_confidence?: number;\n confidence_badge?: 'high' | 'medium' | 'low';\n };\n per_model_verdicts: ReadonlyArray<{\n model: string;\n provider: string;\n verdict: ConsensusVerdict | 'error';\n confidence: number;\n reasoning_excerpt: string;\n raw_response_excerpt: string;\n duration_ms: number;\n error?: { category: string; message: string };\n }>;\n degraded: boolean;\n duration_ms: number;\n engine_version: string;\n /** Phase 2 Lane D-1 \u2014 optional; set by `human-tiebreak` synthesizer. */\n escalation_required?: boolean;\n escalation_reason?: string;\n /** Engine Feature 1 (agreement-gate) \u2014 present only when the gate ran this call. */\n fan_out_diagnostics?: {\n models_called: number;\n panel_size: number;\n early_exit: boolean;\n refused: number;\n };\n /** Stage A7-shadow \u2014 present only when shadow mode was enabled for this call. */\n shadow_synthesis?: EngineShadowSynthesisShape;\n}\n\ninterface EngineModuleShape {\n runConsensus(\n request: { gate_type: string; prompt: string; system_prompt?: string },\n options: {\n panel: readonly ConsensusModelAdapter[];\n per_model_timeout_ms?: number;\n /** Engine Feature 1 (agreement-gate) opt-in. Absent \u21D2 whole panel always called. */\n agreement_gate?: EngineAgreementGateShape;\n /** Stage A7-shadow opt-in. When enabled, the response carries `shadow_synthesis`. */\n shadow_synthesis?: { enabled: boolean };\n },\n ): Promise<{\n ok: boolean;\n response?: EngineConsensusResponseShape;\n error?: { kind: string; reason: string };\n }>;\n /**\n * Engine source-grounded path (Tier-4). OPTIONAL on the module shape so an\n * engine build without it (or an injected fake that omits it) degrades\n * gracefully. When present AND the request carries `source_urls`, the\n * engine-client routes to this instead of `runConsensus`, activating the\n * retrieval-confidence + citation-grader features.\n */\n runSourceGroundedConsensus?(\n request: { gate_type: string; prompt: string; system_prompt?: string },\n options: {\n panel: readonly ConsensusModelAdapter[];\n per_model_timeout_ms?: number;\n agreement_gate?: EngineAgreementGateShape;\n source_urls: readonly string[];\n allow_url?: (url: string) => boolean;\n citation_grader?: {\n enabled: boolean;\n classifier: { classify(args: { claim: string; sources: string }): Promise<{ label: string; confidence: number }> };\n escalate_below?: number;\n };\n },\n ): Promise<\n | {\n ok: true;\n engine: { ok: true; response: EngineConsensusResponseShape };\n cite_warning: boolean;\n partial_sources: boolean;\n citation_grade?: {\n ok: boolean;\n unsupported_count: number;\n uncertain_count: number;\n graded_claims: ReadonlyArray<{\n claim: string;\n label: 'supported' | 'unsupported' | 'uncertain';\n confidence: number;\n cited_ids: readonly number[];\n }>;\n };\n escalation_required?: boolean;\n escalation_reason?: string;\n low_confidence_sources?: readonly number[];\n }\n | { ok: false; reason: string }\n >;\n}\n\n/**\n * A never-resolving promise that REJECTS with the `__VO_MCP_CANCELLED__`\n * sentinel the moment the abort signal fires (or immediately if already\n * aborted). Used to `Promise.race` the engine call so a cancellation returns\n * promptly even if the engine ignores the signal. Shared by the plain and\n * source-grounded engine call paths.\n */\nfunction raceAbort(signal: AbortSignal): Promise<never> {\n return new Promise<never>((_, reject) => {\n const onAbort = (): void => {\n reject(new Error('__VO_MCP_CANCELLED__'));\n };\n if (signal.aborted) onAbort();\n else signal.addEventListener('abort', onAbort, { once: true });\n });\n}\n\nasync function loadEngineModule(): Promise<EngineModuleShape | null> {\n try {\n // Dynamic import keeps `@algosuite/consensus-engine` out of the\n // vo-mcp build graph. If the closed package isn't installed in the\n // host's node_modules, the import throws \u2014 we treat that as\n // unavailable and the null-client fallback kicks in.\n //\n // The string is split so a static analyzer doesn't try to resolve\n // it at build time inside the open package.\n const moduleName = ['@algosuite', 'consensus-engine'].join('/');\n const mod = (await import(moduleName)) as unknown;\n if (\n mod !== null &&\n typeof mod === 'object' &&\n 'runConsensus' in mod &&\n typeof (mod as { runConsensus: unknown }).runConsensus === 'function'\n ) {\n return mod as EngineModuleShape;\n }\n return null;\n } catch {\n return null;\n }\n}\n\nexport function createEngineConsensusClient(options: EngineClientOptions): ConsensusEngineClient {\n const cachedEngine: EngineModuleShape | undefined = options.engineModule;\n let loaded: Promise<EngineModuleShape | null> | null = null;\n\n async function getEngine(): Promise<EngineModuleShape | null> {\n if (cachedEngine !== undefined) return cachedEngine;\n if (loaded === null) loaded = loadEngineModule();\n return loaded;\n }\n\n return {\n async run(request: ConsensusRequest): Promise<ConsensusResult> {\n // Cancellation fast-path. If the signal already fired (operator\n // cancelled before we even started), don't bother loading the\n // engine or building the panel \u2014 return immediately. Added\n // 2026-05-25 (audit HIGH \u2014 MCP `notifications/cancelled`).\n const signal = request.signal;\n if (signal?.aborted) {\n return { ok: false, reason: 'cancelled' };\n }\n const engine = await getEngine();\n if (engine === null) {\n return { ok: false, reason: 'consensus-engine-package-pending' };\n }\n if (options.panel.length < 2) {\n return { ok: false, reason: 'panel-too-small' };\n }\n try {\n const engineRequest: { gate_type: string; prompt: string; system_prompt?: string } = {\n gate_type: request.gate_type,\n prompt: request.prompt,\n ...(request.system_prompt !== undefined ? { system_prompt: request.system_prompt } : {}),\n };\n // Wrap each adapter so per-model `call({...})` invocations receive\n // the abort signal via the existing `abort_signal` arg the closed\n // engine's ModelAdapter shape already accepts (line ~53). This is\n // the cancellation level that actually matters \u2014 provider fetch()\n // calls abort instead of running to completion.\n const panel: readonly ConsensusModelAdapter[] = signal === undefined\n ? options.panel\n : options.panel.map((adapter) => ({\n ...adapter,\n call: (args) => adapter.call({ ...args, abort_signal: signal }),\n }));\n // Feature 1 (agreement-gate, BEHAVIOUR-CHANGING) \u2014 resolve the opt-in.\n // DEFAULT OFF: only present when the client config or the\n // VO_CONSENSUS_AGREEMENT_GATE env flag turns it on. When absent the\n // engine fans out the whole panel exactly as before.\n const agreementGate = resolveAgreementGate({\n ...(options.agreement_gate_enabled !== undefined\n ? { configEnabled: options.agreement_gate_enabled }\n : {}),\n ...(options.env !== undefined ? { env: options.env } : {}),\n });\n const engineOptions: {\n panel: readonly ConsensusModelAdapter[];\n per_model_timeout_ms?: number;\n agreement_gate?: EngineAgreementGateShape;\n shadow_synthesis?: { enabled: boolean };\n } = {\n panel,\n ...(options.per_model_timeout_ms !== undefined\n ? { per_model_timeout_ms: options.per_model_timeout_ms }\n : {}),\n ...(agreementGate !== undefined ? { agreement_gate: agreementGate } : {}),\n // Stage A7-shadow: run the adaptive verdict alongside the live one for grading.\n // Cheap (pure log-odds over already-fetched verdicts; no extra model calls),\n // PII-free, and never alters the live verdict. ON by default; kill with\n // VO_CONSENSUS_SHADOW=0. Cold-start has no skill registry \u2192 neutral priors.\n shadow_synthesis: { enabled: shadowEnabled(options.env) },\n };\n\n // Source-grounded routing (Tier-4). When the caller supplied source URLs\n // AND the engine build exposes runSourceGroundedConsensus, route there so\n // the retrieval-confidence + citation-grader features activate. Otherwise\n // stay on the unchanged plain runConsensus fan-out.\n const sources = request.source_urls;\n const useSourceGrounded =\n sources !== undefined &&\n sources.length > 0 &&\n typeof engine.runSourceGroundedConsensus === 'function';\n\n let response: EngineConsensusResponseShape;\n let sourceExtras:\n | {\n citation_grade?: ReturnType<typeof mapCitationGrade>;\n low_confidence_sources?: readonly number[];\n escalation_required?: boolean;\n escalation_reason?: string;\n }\n | undefined;\n\n if (useSourceGrounded) {\n // Build the engine source-grounded option fields. citation_grader is\n // forwarded when the caller enables it AND a classifier is available.\n // When grading is enabled but the caller injected no classifier, we\n // supply a REAL default one built from the panel's first adapter\n // (reusing the live model the engine-client already constructed from\n // credentials) \u2014 this is what lights citation grading up instead of\n // leaving it dormant. The grader still stays OFF unless the caller\n // sets `citation_grader.enabled === true`.\n // panel.length >= 2 is guaranteed by the panel-too-small guard above,\n // so `firstAdapter` is always defined \u2014 but resolve it defensively\n // (no non-null assertion) and only build a default classifier when an\n // adapter exists. The default classifier reuses this live panel adapter\n // (built from real credentials) as its single-model caller.\n const [firstAdapter] = panel;\n const classifierOptions = signal === undefined ? {} : { signal };\n const defaultClassifier =\n firstAdapter === undefined\n ? undefined\n : createClaimClassifier(firstAdapter, classifierOptions);\n const sgFields = buildSourceGroundedFields(request.source_grounded, defaultClassifier);\n const sgOptions = {\n ...engineOptions,\n source_urls: sources,\n ...(sgFields.allow_url !== undefined ? { allow_url: sgFields.allow_url } : {}),\n ...(sgFields.citation_grader !== undefined\n ? { citation_grader: sgFields.citation_grader }\n : {}),\n };\n if (typeof engine.runSourceGroundedConsensus !== 'function') {\n return { ok: false, reason: 'source-grounded engine missing runSourceGroundedConsensus' };\n }\n const sgCall = engine.runSourceGroundedConsensus(engineRequest, sgOptions);\n const sgResult = signal === undefined\n ? await sgCall\n : await Promise.race([sgCall, raceAbort(signal)]);\n if (!sgResult.ok) {\n return { ok: false, reason: sgResult.reason };\n }\n response = sgResult.engine.response;\n sourceExtras = {\n ...(sgResult.citation_grade !== undefined\n ? { citation_grade: mapCitationGrade(sgResult.citation_grade) }\n : {}),\n ...(sgResult.low_confidence_sources !== undefined\n ? { low_confidence_sources: sgResult.low_confidence_sources }\n : {}),\n ...(sgResult.escalation_required !== undefined\n ? { escalation_required: sgResult.escalation_required }\n : {}),\n ...(sgResult.escalation_reason !== undefined\n ? { escalation_reason: sgResult.escalation_reason }\n : {}),\n };\n } else {\n // Promise.race the engine call against the signal so we return a\n // `cancelled` result PROMPTLY even if the closed engine ignores the\n // signal or the wrapped adapters don't honor it. Orphaned promises\n // may keep running in the background but the awaited call returns\n // immediately \u2014 that's the contract the MCP spec requires.\n const result = signal === undefined\n ? await engine.runConsensus(engineRequest, engineOptions)\n : await Promise.race([engine.runConsensus(engineRequest, engineOptions), raceAbort(signal)]);\n if (!result.ok || result.response === undefined) {\n return {\n ok: false,\n reason: result.error?.reason ?? 'engine-returned-not-ok',\n };\n }\n response = result.response;\n }\n\n // The synthesized_verdict already carries the engine's additive\n // calibrated_confidence + confidence_badge (Feature 2) when present \u2014\n // it is forwarded by value, so those fields flow through automatically.\n return {\n ok: true,\n synthesized_verdict: response.synthesized_verdict,\n per_model_verdicts: response.per_model_verdicts,\n degraded: response.degraded,\n duration_ms: response.duration_ms,\n engine_version: response.engine_version,\n // Phase 2 Lane D-1 \u2014 forward escalation signal when present. The\n // source-grounded layer's own escalation (from the citation grade)\n // takes precedence when set, else the synthesizer's.\n ...(sourceExtras?.escalation_required !== undefined\n ? { escalation_required: sourceExtras.escalation_required }\n : response.escalation_required !== undefined\n ? { escalation_required: response.escalation_required }\n : {}),\n ...(sourceExtras?.escalation_reason !== undefined\n ? { escalation_reason: sourceExtras.escalation_reason }\n : response.escalation_reason !== undefined\n ? { escalation_reason: response.escalation_reason }\n : {}),\n // Feature 1 (agreement-gate) \u2014 fan-out diagnostics (additive telemetry).\n ...(mapFanOutDiagnostics(response.fan_out_diagnostics) !== undefined\n ? { fan_out_diagnostics: mapFanOutDiagnostics(response.fan_out_diagnostics) }\n : {}),\n // Stage A7-shadow \u2014 adaptive-vs-incumbent comparison (PII-free; present iff shadow on).\n ...(mapShadowSynthesis(response.shadow_synthesis) !== undefined\n ? { shadow_synthesis: mapShadowSynthesis(response.shadow_synthesis) }\n : {}),\n // Source-grounded additive outputs (Tier-4 features).\n ...(useSourceGrounded ? { source_grounded: true } : {}),\n ...(sourceExtras?.citation_grade !== undefined\n ? { citation_grade: sourceExtras.citation_grade }\n : {}),\n ...(sourceExtras?.low_confidence_sources !== undefined\n ? { low_confidence_sources: sourceExtras.low_confidence_sources }\n : {}),\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n // Distinguish cancellation from a genuine engine throw. The\n // `__VO_MCP_CANCELLED__` sentinel comes from the Promise.race\n // abort branch above. Also catch native AbortError from anywhere\n // in the engine that respects the signal.\n if (message === '__VO_MCP_CANCELLED__' || signal?.aborted || (err instanceof Error && err.name === 'AbortError')) {\n return { ok: false, reason: 'cancelled' };\n }\n return { ok: false, reason: `engine-threw: ${message}` };\n }\n },\n };\n}\n\n/**\n * Phase 2 Lane A: env-driven adapter construction.\n *\n * Probes `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GOOGLE_API_KEY` (the\n * actual env var names used by `functions-shared`'s `call*WithMetrics`\n * helpers \u2014 NOT `GEMINI_API_KEY` despite the dispatch's suggested\n * naming; see flagged operator decision in the PR body).\n *\n * Per Phase 2 Lane A scope: when at least TWO credentials are present,\n * builds the corresponding `ModelAdapter`s via the closed engine's\n * `createAdapter` factory and returns a populated `ConsensusEngineClient`.\n * When fewer than two are configured (cannot form a 2-model panel) it\n * returns null and the open-shell wiring falls through to the null\n * client, which surfaces the stable 'unimplemented' envelope.\n *\n * Caller injection is LAZY: the engine and `functions-shared` modules\n * are loaded on first adapter call, not at boot, so:\n * - The MCP server boots in O(ms) regardless of which deps are present.\n * - Tests can inject mocks via `tryCreateEngineConsensusClientFromEnvAsync`.\n *\n * Operator decisions flagged in the PR body:\n * - Env var names (canonical names from `functions-shared`)\n * - Behavior when only 1 of 3 keys is present (return null \u2192 null-client)\n * - Default model ids per provider (placeholders pending operator confirm)\n */\n\n/** Default model ids for each provider \u2014 placeholders pending operator confirm. */\nconst DEFAULT_MODELS: Readonly<Record<'anthropic' | 'openai' | 'google' | 'deepseek', string>> = {\n // These ids match the strategic-roadmap \u00A74 `newsStandard` / `newsDeep` panel\n // intent \u2014 current production model ids. Per handoff \u00A7C-3 these MUST come\n // from `CONSENSUS_PANELS` in `functions-shared/shared-model-resolvers.ts`\n // for V1; placeholder defaults here keep Phase 2 Lane A non-blocking.\n anthropic: 'claude-opus-4-7',\n openai: 'gpt-5',\n // gemini-2.5-FLASH (not -pro): flash accepts the default thinkingBudget=0 from\n // callGeminiWithMetrics; 2.5-pro REJECTS budget 0 (\"only works in thinking mode\").\n // Flash is also ~10x cheaper. 2026-06-02.\n google: 'gemini-2.5-flash',\n deepseek: 'deepseek-chat',\n};\n\ntype ProviderKey = 'anthropic' | 'openai' | 'google' | 'deepseek';\n\ninterface EngineFactoryShape {\n createAdapter(\n providerId: ProviderKey,\n options: {\n readonly model: string;\n readonly caller: (\n prompt: string,\n systemPrompt: string,\n model: string,\n maxTokens?: number,\n privacyOptions?: unknown,\n signal?: AbortSignal,\n options?: unknown,\n ) => Promise<{ content: string; inputTokens: number; outputTokens: number; totalTokens: number }>;\n readonly envSource?: Readonly<Record<string, string | undefined>>;\n },\n ): ConsensusModelAdapter;\n}\n\ninterface FunctionsSharedShape {\n callAnthropicWithMetrics: EngineFactoryShape['createAdapter'] extends (id: ProviderKey, o: { caller: infer C }) => unknown ? C : never;\n callOpenAIWithMetrics: EngineFactoryShape['createAdapter'] extends (id: ProviderKey, o: { caller: infer C }) => unknown ? C : never;\n callGeminiWithMetrics: EngineFactoryShape['createAdapter'] extends (id: ProviderKey, o: { caller: infer C }) => unknown ? C : never;\n callDeepSeekWithMetrics: EngineFactoryShape['createAdapter'] extends (id: ProviderKey, o: { caller: infer C }) => unknown ? C : never;\n}\n\n/** Per-env credential probe. Returns the set of providers with non-empty keys. */\nexport function probeProviders(\n env: Readonly<Record<string, string | undefined>> = process.env as Readonly<Record<string, string | undefined>>,\n): readonly ProviderKey[] {\n const out: ProviderKey[] = [];\n if ((env['ANTHROPIC_API_KEY'] ?? '').trim().length > 0) out.push('anthropic');\n if ((env['OPENAI_API_KEY'] ?? '').trim().length > 0) out.push('openai');\n if ((env['GOOGLE_API_KEY'] ?? '').trim().length > 0) out.push('google');\n if ((env['DEEPSEEK_API_KEY'] ?? '').trim().length > 0) out.push('deepseek');\n return out;\n}\n\nexport interface EngineFromEnvOptions {\n /** Override `process.env` lookup \u2014 primarily for tests. */\n readonly envSource?: Readonly<Record<string, string | undefined>>;\n /** Injected engine module \u2014 bypasses dynamic-import path for tests. */\n readonly engineModule?: EngineFactoryShape & EngineModuleShape;\n /** Injected functions-shared module \u2014 bypasses dynamic-import path for tests. */\n readonly functionsSharedModule?: FunctionsSharedShape;\n /** Override per-provider model ids. */\n readonly models?: Partial<Record<ProviderKey, string>>;\n /** Per-model timeout in ms; forwarded to the engine. */\n readonly per_model_timeout_ms?: number;\n}\n\n/**\n * Discriminated result of `loadFactoryAndCallers`. Diagnostic \u2014 the\n * `reason` on failure tells the operator which import or shape check\n * tripped (rather than a generic null). Added 2026-05-24 (audit MEDIUM \u2014\n * \"engine_unavailable reason-string differentiation\").\n */\ntype LoadFactoryResult =\n | { readonly ok: true; readonly engine: EngineFactoryShape & EngineModuleShape; readonly shared: FunctionsSharedShape }\n | {\n readonly ok: false;\n readonly reason:\n | typeof ENGINE_UNAVAILABLE_REASONS.ENGINE_PACKAGE_NOT_INSTALLED\n | typeof ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED;\n };\n\n// Plan PR #1 supersede (vo-pivot-plan-corrections \u00A75a): static imports\n// replace the prior dynamic-import-or-null dance now that consensus-engine\n// and functions-shared are workspace:* deps. Structural type guards retain\n// defense-in-depth against package-export drift; injection paths preserved\n// as higher-priority branch for tests. Diagnostic ENGINE_UNAVAILABLE_REASONS\n// (PR #5291) preserved end-to-end.\nasync function loadFactoryAndCallers(\n injectedEngine?: EngineFactoryShape & EngineModuleShape,\n injectedShared?: FunctionsSharedShape,\n): Promise<LoadFactoryResult> {\n let engineMod: unknown = injectedEngine;\n if (engineMod === undefined) {\n try {\n // Split specifier mirrors loadEngineModule's bundle-opacity trick.\n engineMod = await import(['@algosuite', 'consensus-engine'].join('/'));\n } catch {\n return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.ENGINE_PACKAGE_NOT_INSTALLED };\n }\n }\n if (\n engineMod === null ||\n typeof engineMod !== 'object' ||\n typeof (engineMod as { createAdapter?: unknown }).createAdapter !== 'function' ||\n typeof (engineMod as { runConsensus?: unknown }).runConsensus !== 'function'\n ) {\n return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.ENGINE_PACKAGE_NOT_INSTALLED };\n }\n\n let sharedMod: unknown = injectedShared;\n if (sharedMod === undefined) {\n try {\n sharedMod = await import(['functions', 'shared'].join('-'));\n } catch {\n return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED };\n }\n }\n if (\n sharedMod === null ||\n typeof sharedMod !== 'object' ||\n typeof (sharedMod as { callAnthropicWithMetrics?: unknown }).callAnthropicWithMetrics !== 'function' ||\n typeof (sharedMod as { callOpenAIWithMetrics?: unknown }).callOpenAIWithMetrics !== 'function' ||\n typeof (sharedMod as { callGeminiWithMetrics?: unknown }).callGeminiWithMetrics !== 'function'\n ) {\n return { ok: false, reason: ENGINE_UNAVAILABLE_REASONS.FUNCTIONS_SHARED_NOT_INSTALLED };\n }\n\n return {\n ok: true,\n engine: engineMod as EngineFactoryShape & EngineModuleShape,\n shared: sharedMod as FunctionsSharedShape,\n };\n}\n\n/**\n * Async variant \u2014 preferred for tests and for callers that can await.\n *\n * **2026-05-24 \u2014 audit MEDIUM diagnostic differentiation.** Previously\n * returned `null` for any failure (no credentials / package missing /\n * functions-shared missing / panel construction failed), and the cli.ts\n * fallback then surfaced a generic `'consensus-engine-package-pending'`\n * reason that didn't tell the operator which knob to turn. Now ALWAYS\n * returns a non-null `ConsensusEngineClient`:\n *\n * - Success \u2192 a real engine-backed client.\n * - Failure \u2192 a diagnostic null-client whose `run()` returns\n * `{ ok: false, reason: 'consensus-<specific-reason>' }`.\n *\n * The four specific reasons live in `null-client.ENGINE_UNAVAILABLE_REASONS`:\n * - `consensus-credentials-missing` \u2014 fewer than 2 API keys\n * - `consensus-engine-package-not-installed` \u2014 engine module missing/malformed\n * - `consensus-functions-shared-not-installed` \u2014 functions-shared missing/malformed\n * - `consensus-panel-construction-failed` \u2014 engine + creds + shared all\n * present but every per-provider adapter constructor threw\n *\n * Cross-vendor MCP clients still see the stable `unimplemented` envelope\n * via the tool-layer fallback; the only thing that changes is the\n * operator-actionable `reason` field on that envelope.\n */\nexport async function tryCreateEngineConsensusClientFromEnvAsync(\n options: EngineFromEnvOptions = {},\n): Promise<ConsensusEngineClient> {\n const env = options.envSource ?? (process.env as Readonly<Record<string, string | undefined>>);\n const providers = probeProviders(env);\n if (providers.length < 2) {\n return createNullConsensusEngineClient(ENGINE_UNAVAILABLE_REASONS.CREDENTIALS_MISSING);\n }\n\n const loaded = await loadFactoryAndCallers(options.engineModule, options.functionsSharedModule);\n if (!loaded.ok) {\n return createNullConsensusEngineClient(loaded.reason);\n }\n\n const callerByProvider: Readonly<Record<ProviderKey, FunctionsSharedShape['callAnthropicWithMetrics']>> = {\n anthropic: loaded.shared.callAnthropicWithMetrics,\n openai: loaded.shared.callOpenAIWithMetrics,\n google: loaded.shared.callGeminiWithMetrics,\n deepseek: loaded.shared.callDeepSeekWithMetrics,\n };\n const modelByProvider: Readonly<Record<ProviderKey, string>> = {\n anthropic: options.models?.anthropic ?? DEFAULT_MODELS.anthropic,\n openai: options.models?.openai ?? DEFAULT_MODELS.openai,\n google: options.models?.google ?? DEFAULT_MODELS.google,\n deepseek: options.models?.deepseek ?? DEFAULT_MODELS.deepseek,\n };\n\n const panel: ConsensusModelAdapter[] = [];\n for (const p of providers) {\n try {\n const adapter = loaded.engine.createAdapter(p, {\n model: modelByProvider[p],\n caller: callerByProvider[p],\n envSource: env,\n });\n panel.push(adapter);\n } catch {\n // Adapter construction failure for ONE provider doesn't kill the\n // panel \u2014 drop that provider and continue. If the surviving panel\n // is too small the surviving-providers branch below surfaces the\n // specific panel-construction-failed reason.\n }\n }\n if (panel.length < 2) {\n return createNullConsensusEngineClient(ENGINE_UNAVAILABLE_REASONS.PANEL_CONSTRUCTION_FAILED);\n }\n\n const clientOptions: EngineClientOptions = {\n panel,\n engineModule: loaded.engine,\n ...(options.per_model_timeout_ms !== undefined\n ? { per_model_timeout_ms: options.per_model_timeout_ms }\n : {}),\n };\n return createEngineConsensusClient(clientOptions);\n}\n\n/**\n * Synchronous variant for cli.ts compatibility. Performs the credential\n * probe synchronously and constructs a lazy client whose `run()` method\n * resolves the engine + functions-shared modules on first call.\n *\n * **2026-05-24 \u2014 audit MEDIUM:** now ALWAYS returns a non-null client\n * (matches the async variant's contract). On the failure path the client\n * returns `{ ok: false, reason: 'consensus-<specific>' }` so the\n * operator sees an actionable diagnostic. cli.ts no longer needs to\n * fall through to the legacy null-client; it can use the returned\n * client directly. The legacy null-client (`createNullConsensusEngineClient()`\n * with no args) remains exported for any caller that wants the original\n * `'consensus-engine-package-pending'` reason explicitly.\n *\n * Fast-boot is preserved: when \u22652 keys are present the returned client\n * lazily loads modules on first `run()`. When < 2 keys, the diagnostic\n * stub returns immediately with `consensus-credentials-missing` \u2014 no\n * module loading.\n */\nexport function tryCreateEngineConsensusClientFromEnv(\n options: EngineFromEnvOptions = {},\n): ConsensusEngineClient {\n const env = options.envSource ?? (process.env as Readonly<Record<string, string | undefined>>);\n if (probeProviders(env).length < 2) {\n return createNullConsensusEngineClient(ENGINE_UNAVAILABLE_REASONS.CREDENTIALS_MISSING);\n }\n\n // Cache the lazily-resolved client so we don't reload modules on\n // every consensus call.\n let resolved: Promise<ConsensusEngineClient> | null = null;\n\n return {\n async run(request) {\n if (resolved === null) {\n resolved = tryCreateEngineConsensusClientFromEnvAsync(options);\n }\n const real = await resolved;\n return real.run(request);\n },\n };\n}\n\n/**\n * Phase 2 Lane C \u2014 bridge `ContentHashCache` (vo-mcp's sqlite-backed\n * tool-response cache) to the engine's `CacheBackend` interface.\n *\n * The engine doesn't currently call into the cache through its public\n * `runConsensus` entry point \u2014 engine-level response caching is wired\n * at the open-shell tool layer (`consensus-judgment.ts` already uses\n * `deps.cache` directly). This adapter exists so future engine builds\n * can accept an injected backend and so callers outside vo-mcp (e.g.\n * a future direct-import Tax integration) can share the same store.\n *\n * Stored values are JSON-serialized round-trip via `ContentHashCache`'s\n * existing serializer. The adapter is async-over-sync \u2014 the underlying\n * sqlite is sync but the engine's contract is async.\n */\nexport interface CacheBackendShape {\n get<T>(key: string): Promise<T | null>;\n set<T>(key: string, value: T, ttlMs?: number): Promise<void>;\n delete(key: string): Promise<void>;\n clear(): Promise<void>;\n}\n\nexport function createCacheBackendAdapter(store: ContentHashCache): CacheBackendShape {\n return {\n async get<T>(key: string): Promise<T | null> {\n const row = store.get<T>(key);\n return row === null ? null : row.value;\n },\n async set<T>(key: string, value: T, ttlMs?: number): Promise<void> {\n store.set(key, value, ttlMs);\n },\n async delete(key: string): Promise<void> {\n // PR-Q 2026-05-25: `ContentHashCache.delete()` is now a real method.\n // Previously this adapter silently no-op'd (the \"known limitation\"\n // documented in the prior implementation), which broke the engine's\n // CacheBackend contract at the integration boundary.\n store.delete(key);\n },\n async clear(): Promise<void> {\n // PR-Q 2026-05-25: same as `delete` \u2014 real implementation. Previously\n // this silently no-op'd; the engine layer documented the gap but it\n // would have bitten anyone calling the contract expecting actual purge.\n store.clear();\n },\n };\n}\n\n/**\n * Phase 2 Lane C \u2014 bridge `EventsWriter` to the engine's `LoggingBackend`\n * interface, merging engine-side log events with the MCP session context\n * needed to form a full `ConsensusCallEvent` (V1 gate #7 schema).\n *\n * Sanitization of free-text fields happens INSIDE this adapter (per\n * handoff \u00A7C-7) so the engine's already-sanitized excerpts can pass\n * through unchanged, but anything the adapter synthesizes here is\n * sanitized once more defensively.\n */\nexport interface LoggingBackendShape {\n emit(event: EngineLogEventShape): Promise<void>;\n}\n\n/** Mirrors the engine's `EngineLogEvent` structurally. Re-declared so the open shell has no closed import. */\nexport interface EngineLogEventShape {\n readonly ts: string;\n readonly input_hash: string;\n readonly input_excerpt: string;\n readonly input_size_bytes: number;\n readonly gate_type: string;\n readonly synthesizer: string;\n readonly per_model_verdicts: ReadonlyArray<{\n readonly model: string;\n readonly provider: string;\n readonly verdict: 'pass' | 'fail' | 'uncertain' | 'error';\n readonly confidence: number;\n readonly raw_response_excerpt: string;\n /** Per-model wall-clock duration. Optional for back-compat; defaults to 0 in the adapter. */\n readonly duration_ms?: number;\n /** Set when `verdict === 'error'` \u2014 category + message for triage. */\n readonly error?: { readonly category: string; readonly message: string };\n /** First ~500 chars of reasoning. Optional; null when not provided. */\n readonly reasoning_excerpt?: string;\n }>;\n readonly synthesized_verdict: {\n readonly verdict: 'pass' | 'fail' | 'uncertain';\n readonly confidence: number;\n readonly reasoning_excerpt: string;\n readonly synthesizer: string;\n readonly dissent_summary: string | null;\n } | null;\n readonly consensus_confidence: number | null;\n readonly duration_ms: number;\n readonly degraded: boolean;\n readonly engine_version: string;\n readonly cache_hit: boolean;\n /** Per-model input token counts. Optional until engine reports them. */\n readonly per_model_tokens_in?: Readonly<Record<string, number>>;\n /** Per-model output token counts. Optional until engine reports them. */\n readonly per_model_tokens_out?: Readonly<Record<string, number>>;\n /** Total cost in USD for the call. Optional until engine reports it. */\n readonly total_cost_usd?: number;\n}\n\nexport function createLoggingBackendAdapter(\n writer: EventsWriter,\n session: SessionContext,\n toolName = 'vo_consensus_judgment',\n): LoggingBackendShape {\n return {\n async emit(event: EngineLogEventShape): Promise<void> {\n // Translate engine-shaped event into the MCP-side ConsensusCallEvent.\n // V1 schema completion (2026-05-24 BLOCKER #1): preserve every field\n // the EngineLogEventShape carries; previously the adapter dropped 7+\n // fields breaking the cloud-ingestion contract.\n const merged: ConsensusCallEvent = {\n schema_version: 1,\n event_id: randomUUID(),\n ts: event.ts,\n tenant_id: session.tenantId,\n operator_id: session.operatorId,\n session_id: session.sessionId,\n client_id: session.clientId,\n mode: session.mode,\n tool: toolName,\n gate_type: event.gate_type,\n input_hash: event.input_hash,\n input_excerpt: sanitizeExcerpt(event.input_excerpt),\n input_size_bytes: event.input_size_bytes,\n per_model_verdicts: event.per_model_verdicts.map((v) => ({\n model: v.model,\n model_id: v.model,\n provider: v.provider,\n verdict: v.verdict,\n confidence: v.confidence,\n duration_ms: v.duration_ms ?? 0,\n raw_response_excerpt: sanitizeExcerpt(v.raw_response_excerpt),\n raw_response_hash: sha256Hex(v.raw_response_excerpt),\n reasoning_summary:\n v.reasoning_excerpt !== undefined && v.reasoning_excerpt.length > 0\n ? sanitizeExcerpt(v.reasoning_excerpt, 500)\n : null,\n cited_sources: [],\n error: v.error ?? null,\n })),\n synthesized_verdict:\n event.synthesized_verdict === null\n ? null\n : {\n verdict: event.synthesized_verdict.verdict,\n confidence: event.synthesized_verdict.confidence,\n reasoning_excerpt: sanitizeExcerpt(event.synthesized_verdict.reasoning_excerpt),\n },\n consensus_confidence: event.consensus_confidence,\n per_model_tokens_in: event.per_model_tokens_in ?? null,\n per_model_tokens_out: event.per_model_tokens_out ?? null,\n total_cost_usd: event.total_cost_usd ?? null,\n duration_ms: event.duration_ms,\n dev_override: null,\n downstream_outcome: null,\n vo_mcp_version: VO_MCP_VERSION,\n consensus_engine_version: event.engine_version,\n cache_hit: event.cache_hit,\n };\n writer.append(merged);\n },\n };\n}\n", "/**\n * Pure builders + output-mappers for the engine-client's option wiring.\n *\n * Extracted from `engine-client.ts` so the four consensus-engine features\n * (agreement-gate, calibrated-confidence, citation-grader, retrieval-confidence)\n * can be turned on / forwarded without growing the already-large client file.\n *\n * THE DEFAULTS \u2014 each documented inline (also see the dispatch report):\n * - Pure-additive OUTPUTS (calibrated_confidence + confidence_badge,\n * fan_out_diagnostics, retrieval-confidence ordinals) are forwarded WHENEVER\n * the engine produced them. They never change a verdict \u2014 only add fields \u2014\n * so they are effectively ON by default for VO gates.\n * - The agreement-gate EARLY-EXIT (behaviour-changing: skips part of the panel)\n * is OFF unless the operator opts in via `VO_CONSENSUS_AGREEMENT_GATE` (or a\n * client-config override). DEFAULT OFF.\n * - The citation-grader ESCALATION (behaviour-changing: raises an escalation\n * signal on a failing grade) is OFF unless the caller injects a\n * `ClaimClassifier` and sets `enabled: true`. DEFAULT OFF.\n *\n * Everything here is pure (no I/O) so it is trivially unit-testable.\n */\nimport type {\n ConsensusFanOutDiagnostics,\n ConsensusCitationGrade,\n ConsensusSourceGroundedConfig,\n ConsensusShadowSynthesis,\n ConsensusVerdict,\n} from './client.js';\n\n/** Env var (or `truthy` client flag) that opts INTO the agreement-gated early-exit fan-out. */\nexport const AGREEMENT_GATE_ENV_VAR = 'VO_CONSENSUS_AGREEMENT_GATE';\n\n/** Engine `AgreementGateOptions` shape \u2014 re-declared structurally (no closed import). */\nexport interface AgreementGateOptionsShape {\n readonly enabled: boolean;\n readonly probe_size?: number;\n readonly modal_agreement_threshold?: number;\n readonly min_confidence?: number;\n}\n\n/** Truthy-string check used for env-flag opt-ins (`1`, `true`, `yes`, `on`). */\nexport function isTruthyFlag(raw: string | undefined): boolean {\n if (raw === undefined) return false;\n const v = raw.trim().toLowerCase();\n return v === '1' || v === 'true' || v === 'yes' || v === 'on';\n}\n\n/**\n * Resolve whether the BEHAVIOUR-CHANGING agreement-gate early-exit is enabled.\n * Priority: explicit client config (boolean) > env var > OFF (default).\n *\n * Returns the engine `agreement_gate` options object when enabled, else\n * `undefined` (so the engine fans out the whole panel \u2014 unchanged behaviour).\n */\nexport function resolveAgreementGate(args: {\n readonly configEnabled?: boolean;\n readonly env?: Readonly<Record<string, string | undefined>>;\n readonly probeSize?: number;\n readonly modalAgreementThreshold?: number;\n readonly minConfidence?: number;\n}): AgreementGateOptionsShape | undefined {\n const env = args.env ?? (process.env as Readonly<Record<string, string | undefined>>);\n const enabled =\n args.configEnabled === true ||\n (args.configEnabled === undefined && isTruthyFlag(env[AGREEMENT_GATE_ENV_VAR]));\n if (!enabled) return undefined;\n return {\n enabled: true,\n ...(args.probeSize !== undefined ? { probe_size: args.probeSize } : {}),\n ...(args.modalAgreementThreshold !== undefined\n ? { modal_agreement_threshold: args.modalAgreementThreshold }\n : {}),\n ...(args.minConfidence !== undefined ? { min_confidence: args.minConfidence } : {}),\n };\n}\n\n/** The engine source-grounded `citation_grader` option shape (structural). */\nexport interface EngineCitationGraderOption {\n readonly enabled: boolean;\n /**\n * A CONCRETE classifier \u2014 never undefined at this layer. The open-shell\n * `ConsensusSourceGroundedConfig.citation_grader.classifier` is optional (a\n * caller may omit it to take the engine-client's default), but by the time the\n * grader option is BUILT (`buildSourceGroundedFields`) a classifier has always\n * been resolved, so `NonNullable` is the honest type the engine receives.\n */\n readonly classifier: NonNullable<\n ConsensusSourceGroundedConfig extends { citation_grader?: infer C }\n ? C extends { classifier?: infer K }\n ? K\n : never\n : never\n >;\n readonly escalate_below?: number;\n}\n\n/**\n * Translate the open-shell `source_grounded` config into the engine's\n * source-grounded option fields (`citation_grader`, `allow_url`).\n *\n * The grader is forwarded when `citation_grader.enabled === true` AND a\n * classifier is available. The classifier resolves in priority order:\n * 1. an explicitly INJECTED `config.citation_grader.classifier` (caller wins), else\n * 2. the `defaultClassifier` the engine-client builds from its own panel\n * adapter (a real single-model `ClaimClassifier` \u2014 see claim-classifier.ts).\n *\n * If grading is enabled but NEITHER a classifier is injected NOR a default is\n * supplied, the grader stays OFF (no fabricated always-'supported' stub). When\n * `enabled !== true` the grader is always OFF. Returns `{}` when nothing is\n * configured. This keeps the sourceless / grading-off paths byte-for-byte\n * unchanged while letting the default classifier light citation grading up when\n * a caller opts in.\n */\nexport function buildSourceGroundedFields(\n config: ConsensusSourceGroundedConfig | undefined,\n defaultClassifier?: EngineCitationGraderOption['classifier'],\n): {\n citation_grader?: EngineCitationGraderOption;\n allow_url?: (url: string) => boolean;\n} {\n if (config === undefined) return {};\n const out: { citation_grader?: EngineCitationGraderOption; allow_url?: (url: string) => boolean } = {};\n const grader = config.citation_grader;\n if (grader !== undefined && grader.enabled === true) {\n const classifier = (grader.classifier ?? defaultClassifier) as\n | EngineCitationGraderOption['classifier']\n | undefined;\n if (classifier !== undefined) {\n out.citation_grader = {\n enabled: true,\n classifier,\n ...(grader.escalate_below !== undefined ? { escalate_below: grader.escalate_below } : {}),\n };\n }\n }\n if (config.allow_url !== undefined) {\n out.allow_url = config.allow_url;\n }\n return out;\n}\n\n/** Engine `FanOutDiagnostics` shape (structural). */\nexport interface EngineFanOutDiagnosticsShape {\n readonly models_called: number;\n readonly panel_size: number;\n readonly early_exit: boolean;\n readonly refused: number;\n}\n\n/** Map the engine's fan-out diagnostics \u2192 the open-shell projection (additive). */\nexport function mapFanOutDiagnostics(\n fd: EngineFanOutDiagnosticsShape | undefined,\n): ConsensusFanOutDiagnostics | undefined {\n if (fd === undefined) return undefined;\n return {\n models_called: fd.models_called,\n panel_size: fd.panel_size,\n early_exit: fd.early_exit,\n refused: fd.refused,\n };\n}\n\n/** Env var that DISABLES the Stage A7-shadow comparison (it is ON by default for VO). */\nexport const SHADOW_SYNTHESIS_ENV_VAR = 'VO_CONSENSUS_SHADOW';\n\n/**\n * Shadow-synthesis is opt-OUT (ON by default): every VO consensus call also computes\n * the adaptive verdict for grading. Disable with `VO_CONSENSUS_SHADOW` in\n * {`0`,`false`,`no`,`off`,``}. Absent \u21D2 enabled.\n */\nexport function shadowEnabled(env: Readonly<Record<string, string | undefined>> | undefined): boolean {\n const raw = (env ?? {})[SHADOW_SYNTHESIS_ENV_VAR];\n if (raw === undefined) return true;\n const norm = raw.trim().toLowerCase();\n return !(norm === '0' || norm === 'false' || norm === 'no' || norm === 'off' || norm === '');\n}\n\n/** Engine `ShadowSynthesis` shape \u2014 re-declared structurally (no closed import). */\nexport interface EngineShadowSynthesisShape {\n readonly incumbent: { readonly verdict: ConsensusVerdict; readonly confidence: number; readonly synthesizer: string };\n readonly adaptive: { readonly verdict: ConsensusVerdict; readonly confidence: number; readonly calibrated_confidence?: number };\n readonly agree: boolean;\n}\n\n/**\n * Map the engine's Stage A7-shadow comparison \u2192 the open-shell projection (PII-free:\n * verdict labels + confidences only). Returns undefined when shadow mode was off.\n */\nexport function mapShadowSynthesis(\n s: EngineShadowSynthesisShape | undefined,\n): ConsensusShadowSynthesis | undefined {\n if (s === undefined) return undefined;\n return {\n incumbent: { verdict: s.incumbent.verdict, confidence: s.incumbent.confidence, synthesizer: s.incumbent.synthesizer },\n adaptive: {\n verdict: s.adaptive.verdict,\n confidence: s.adaptive.confidence,\n ...(s.adaptive.calibrated_confidence !== undefined ? { calibrated_confidence: s.adaptive.calibrated_confidence } : {}),\n },\n agree: s.agree,\n };\n}\n\n/** Engine `CitationGradeResult` shape (structural). */\nexport interface EngineCitationGradeShape {\n readonly ok: boolean;\n readonly unsupported_count: number;\n readonly uncertain_count: number;\n readonly graded_claims: ReadonlyArray<{\n readonly claim: string;\n readonly label: 'supported' | 'unsupported' | 'uncertain';\n readonly confidence: number;\n readonly cited_ids: readonly number[];\n }>;\n}\n\n/** Map the engine's citation grade \u2192 the open-shell projection (additive). */\nexport function mapCitationGrade(\n cg: EngineCitationGradeShape | undefined,\n): ConsensusCitationGrade | undefined {\n if (cg === undefined) return undefined;\n return {\n ok: cg.ok,\n unsupported_count: cg.unsupported_count,\n uncertain_count: cg.uncertain_count,\n graded_claims: cg.graded_claims.map((g) => ({\n claim: g.claim,\n label: g.label,\n confidence: g.confidence,\n cited_ids: [...g.cited_ids],\n })),\n };\n}\n", "/**\n * Real `ClaimClassifier` for the source-grounded citation grader (Tier-4).\n *\n * The engine's citation grader needs a `ClaimClassifierShape` \u2014 given a single\n * claim plus the concatenated text of the cited sources, it must answer \"is this\n * claim supported by these sources?\". Until now no real classifier existed: the\n * open shell forwarded one ONLY when a caller injected it, and there was no\n * default \u2014 so citation grading was dormant even on the source-grounded path.\n *\n * This module supplies the missing piece. It is a THIN wrapper over ONE model\n * call: it reuses the same `call({ system_prompt, user_prompt })` shape the\n * engine-client's `ConsensusModelAdapter` already exposes (so the engine-client\n * can hand it its own first panel adapter, built from real credentials), and it\n * maps the model's verdict back to the grader's\n * `{ label: 'supported'|'unsupported'|'uncertain', confidence }` contract.\n *\n * It deliberately does NOT fabricate an always-'supported' stub \u2014 an unsupported\n * claim must come back `unsupported` so a failing grade escalates. On any model\n * error / malformed output it fails SAFE to `{ label: 'uncertain', confidence: 0 }`\n * (per the `ClaimClassifierShape` contract: MUST NOT throw), which the engine\n * treats as a low-confidence claim rather than silently passing it.\n */\nimport type { ClaimClassifierShape, ConsensusVerdict } from './client.js';\n\n/**\n * Minimal model surface the classifier needs. A single-model caller that takes a\n * system + user prompt and returns a verdict-style result. The engine-client's\n * `ConsensusModelAdapter.call` is a structural superset of this, so the\n * engine-client can pass one of its own adapters directly. Tests inject a fake.\n */\nexport interface ClaimClassifierModel {\n call(args: {\n readonly system_prompt: string;\n readonly user_prompt: string;\n readonly abort_signal?: AbortSignal;\n }): Promise<{\n readonly verdict: ConsensusVerdict | 'error';\n readonly confidence: number;\n readonly error?: { readonly category: string; readonly message: string };\n }>;\n}\n\n/** Result label the engine's citation grader consumes. */\nexport type ClaimLabel = 'supported' | 'unsupported' | 'uncertain';\n\n// The model behind `classify` is the engine panel adapter, whose response\n// parser (`parseModelResponse`) only yields a real verdict when the model\n// concludes with the strict `VERDICT:` / `CONFIDENCE:` protocol lines. So the\n// classifier MUST instruct that exact format \u2014 otherwise every classification\n// falls through to `parse_failed \u2192 uncertain, confidence 0` and the grader can\n// never get a supported/unsupported signal (the gate then escalates on every\n// source-grounded call regardless of grounding).\nconst CLASSIFIER_SYSTEM_PROMPT = [\n 'You are a citation-grading judge. You are given a CLAIM and the full text of the SOURCES it cites.',\n 'Decide whether the SOURCES directly support the CLAIM. Judge ONLY against the supplied sources \u2014 do not use outside knowledge.',\n 'pass = the sources clearly support the claim; fail = the sources contradict or do not support it; uncertain = the sources are insufficient or ambiguous.',\n 'After a one-sentence justification, conclude with EXACTLY one of these lines:',\n ' VERDICT: pass',\n ' VERDICT: fail',\n ' VERDICT: uncertain',\n 'Then on a new line, exactly:',\n ' CONFIDENCE: <0..1>',\n 'Do not include any other VERDICT or CONFIDENCE lines.',\n].join('\\n');\n\n/**\n * Map a single-model verdict to the citation grader's claim label.\n * pass \u2192 supported\n * fail \u2192 unsupported\n * uncertain \u2192 uncertain\n * error \u2192 uncertain (fail-safe; the model errored, so we cannot vouch)\n *\n * Exported for direct unit testing of the mapping in isolation.\n */\nexport function verdictToLabel(verdict: ConsensusVerdict | 'error'): ClaimLabel {\n switch (verdict) {\n case 'pass':\n return 'supported';\n case 'fail':\n return 'unsupported';\n case 'uncertain':\n return 'uncertain';\n default:\n return 'uncertain';\n }\n}\n\n/** Clamp a model confidence into [0, 1]; coerce NaN / non-finite to 0. */\nfunction clampConfidence(raw: number): number {\n if (!Number.isFinite(raw)) return 0;\n if (raw < 0) return 0;\n if (raw > 1) return 1;\n return raw;\n}\n\n/**\n * Build the user prompt for one claim/sources pair. Kept pure + exported so the\n * exact wording is testable and the classifier stays a thin shell.\n */\nexport function buildClassifyPrompt(claim: string, sources: string): string {\n return [\n 'CLAIM:',\n claim,\n '',\n 'SOURCES:',\n sources,\n '',\n 'Does the cited SOURCES text support the CLAIM? Reply with your verdict ' +\n '(pass = supported, fail = unsupported, uncertain = insufficient).',\n ].join('\\n');\n}\n\n/**\n * Construct a real `ClaimClassifier` backed by a single model call.\n *\n * @param model The single-model caller (an engine adapter in prod, a fake in tests).\n * @param options.signal Optional abort signal forwarded to the model call.\n */\nexport function createClaimClassifier(\n model: ClaimClassifierModel,\n options: { readonly signal?: AbortSignal } = {},\n): ClaimClassifierShape {\n return {\n async classify(args: {\n readonly claim: string;\n readonly sources: string;\n }): Promise<{ readonly label: ClaimLabel; readonly confidence: number }> {\n try {\n const result = await model.call({\n system_prompt: CLASSIFIER_SYSTEM_PROMPT,\n user_prompt: buildClassifyPrompt(args.claim, args.sources),\n ...(options.signal !== undefined ? { abort_signal: options.signal } : {}),\n });\n if (result.verdict === 'error') {\n return { label: 'uncertain', confidence: 0 };\n }\n return {\n label: verdictToLabel(result.verdict),\n confidence: clampConfidence(result.confidence),\n };\n } catch {\n // Contract: MUST NOT throw. A thrown model call (network, abort, etc.)\n // degrades to a low-confidence uncertain claim, never a silent pass.\n return { label: 'uncertain', confidence: 0 };\n }\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["homedir", "join", "dirname", "existsSync", "mkdirSync", "readFileSync", "writeFileSync", "chmodSync", "randomUUID", "createHash", "readFileSync", "dirname", "join", "fileURLToPath", "readdirSync", "readFileSync", "statSync", "homedir", "dirname", "join", "z", "z", "existsSync", "readFileSync", "join", "join", "path", "existsSync", "readFileSync", "path", "MAX_EXCERPT", "join", "homedir", "statSync", "readFileSync", "dirname", "readdirSync", "dirname", "fileURLToPath", "readFileSync", "join", "createHash", "cached", "envelope", "TOOL_NAME", "GATE_TYPE", "MAX_SOURCE_BYTES", "inputSchema", "description", "isToolInput", "cached", "payload", "envelope", "TOOL_NAME", "inputSchema", "description", "isToolInput", "buildPrompt", "cached", "payload", "envelope", "TOOL_NAME", "inputSchema", "description", "isToolInput", "cached", "payload", "envelope", "TOOL_NAME", "GATE_TYPE", "inputSchema", "description", "isToolInput", "buildPrompt", "cached", "payload", "envelope", "z", "z", "readFile", "path", "RATCHET_ID", "ALLOW_MARKER_RE", "RATCHET_ID", "FILE_ALLOW_RE", "ANY_MATCHER_RE", "inspectFile", "RATCHET_ID", "basename", "FILE_ALLOW_RE", "CATCH_RETURN_TRUE_RE", "USER_EVENT_RE", "FIRE_EVENT_RE", "FILE_ALLOW_RE", "inspectFile", "CATCH_RETURN_TRUE_RE", "USER_EVENT_RE", "FIRE_EVENT_RE", "RATCHET_ID", "RATCHET_ID", "FILE_ALLOW_RE", "hasFileAllowMarker", "hasLineAllowMarkerNearby", "inspectFile", "TOOL_NAME", "GATE_TYPE", "inputSchema", "description", "isToolInput", "TOOL_NAME", "GATE_TYPE", "MAX_CONTEXT_BYTES", "inputSchema", "description", "isToolInput", "cached", "payload", "envelope", "path", "TOOL_NAME", "inputSchema", "description", "isToolInput", "TOOL_NAME", "inputSchema", "description", "isToolInput", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "TOOL_NAME", "inputSchema", "description", "isToolInput", "buildPrompt", "payload", "recommendation", "reason", "pr", "TOOL_NAME", "inputSchema", "description", "isStringArray", "isToolInput", "homedir", "join", "existsSync", "mkdirSync", "readFileSync", "readdirSync", "statSync", "TOOL_NAME", "inputSchema", "description", "isToolInput", "join", "homedir", "readdirSync", "statSync", "existsSync", "readFileSync", "mkdirSync", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "homedir", "join", "existsSync", "mkdirSync", "readFileSync", "writeFileSync", "readdirSync", "TOOL_NAME", "inputSchema", "description", "isToolInput", "join", "homedir", "mkdirSync", "writeFileSync", "existsSync", "readdirSync", "readFileSync", "createAuthTokenSourceFromEnv", "readStoredCredential", "result", "isToolInput", "TOOL_NAME", "TOOL_NAME", "description", "inputSchema", "randomUUID", "createHash", "chmodSync", "mkdirSync", "dirname", "mkdirSync", "dirname", "chmodSync", "createHash", "buildSummary", "randomUUID"]
|
|
7
7
|
}
|