@algosuite/vo-mcp 0.2.0-beta.46 → 0.2.0-beta.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ci/check-local-pr-overlap.js +31 -8
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +2 -2
- package/dist/install-cli.js +383 -94
- package/dist/install-cli.js.map +4 -4
- package/dist/runner-cli.js +1136 -157
- package/dist/runner-cli.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../vo-arch-defaults/src/schema/rule-v1.ts", "../../vo-arch-defaults/src/schema/override-v1.ts", "../../vo-arch-defaults/src/schema/index.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", "../../vo-arch-defaults/src/index.ts", "../src/logging/events-writer.ts", "../src/tools/common.ts", "../src/cloud/auth-token-source.ts", "../src/cloud/keychain.ts", "../src/cloud/credential-store.ts", "../src/tools/memory/safe-memory-file.ts", "../src/tools/memory/sync-lock-liveness.ts", "../src/tools/memory/sync-lock.ts", "../src/tools/memory/memory-index-merge.ts", "../src/tools/memory/bounded-sync.ts", "../src/tools/memory/memory-push-cache.ts", "../src/tools/memory/memory-sync-http.ts", "../src/tools/memory/sync-kill-switch.ts", "../src/tools/memory/memory-knowledge-bridge.ts", "../src/tools/memory/sync-config.ts", "../src/cli.ts", "../src/server.ts", "../src/modes/local.ts", "../src/tools/check-assertion-strength.ts", "../src/tools/check-hollow-test.ts", "../src/tools/architecture-review-kb-prefilter.ts", "../src/tools/kb-metadata-prefilter.ts", "../src/tools/verify-answer.ts", "../src/tools/consensus-judgment.ts", "../src/consensus/gate-types.ts", "../src/tools/consensus-judgment-source-grounded.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/tools/heal/trigger-heal.ts", "../src/cloud/admin-callable-client.ts", "../src/tools/cloud-call.ts", "../src/tools/heal/common-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/list-pending-prs.ts", "../src/tools/pr/common-pr.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/report-session-state.ts", "../src/tools/session/directive.ts", "../src/tools/session/spawn-successor.ts", "../src/swarm/tier-binding.ts", "../src/swarm/successor-launch.ts", "../src/swarm/spawn-ledger.ts", "../src/tools/concierge/dispatch.ts", "../src/tools/concierge/common-concierge.ts", "../src/tools/memory/private-knowledge.ts", "../src/tools/hq/whiteboard.ts", "../src/tools/skills/skill-corpus.ts", "../../skill-registry/src/loader.ts", "../src/cache/sqlite-cache.ts", "../src/cache/canonicalize.ts", "../src/ratchets/stub-client.ts", "../src/consensus/engine-client.ts", "../src/consensus/null-client.ts", "../src/consensus/meta-model-caller.ts", "../src/consensus/consensus-panel.ts", "../src/consensus/engine-options.ts", "../src/consensus/claim-classifier.ts", "../src/consensus/moat-client.ts", "../src/consensus/client.ts", "../src/consensus/fallback-client.ts", "../src/consensus/local-credential-env.ts", "../src/cloud/login.ts", "../src/cloud/vo-credential-exchange.ts"],
|
|
4
|
-
"sourcesContent": ["/**\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 * Schema-version registry. Re-export v1 today; future v2/v3 dispatch by\n * `schema_version` field on the parsed input.\n */\nexport {\n ArchitecturalDefaultRuleSchema,\n parseRule,\n safeParseRule,\n type ArchitecturalDefaultRuleParsed,\n} from './rule-v1.js';\nexport {\n TenantOverrideSchema,\n parseOverride,\n safeParseOverride,\n type TenantOverrideParsed,\n} from './override-v1.js';\n\nexport const CURRENT_SCHEMA_VERSION = 1 as const;\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 * Public API for @algosuite/vo-arch-defaults.\n *\n * Stable surface \u2014 what external consumers (the vo-mcp `vo_architecture_review`\n * tool, the `vo-arch-check` CLI, future cloud control plane) import.\n */\n\n// Types\nexport type {\n ArchitecturalDefaultRule,\n AppliesReason,\n AppliesWhen,\n Category,\n ChangeType,\n EvidenceHit,\n EvidenceMatcher,\n EvidenceMatcherKind,\n QueryHit,\n QueryInput,\n QueryResult,\n Reference,\n ReferenceType,\n Severity,\n Stack,\n StaleRuleInfo,\n TenantOverride,\n} from './types.js';\n\n// Schema\nexport {\n ArchitecturalDefaultRuleSchema,\n TenantOverrideSchema,\n parseRule,\n safeParseRule,\n parseOverride,\n safeParseOverride,\n CURRENT_SCHEMA_VERSION,\n} from './schema/index.js';\n\n// Storage\nexport {\n loadBundledCorpus,\n resolveBundledCorpusDir,\n type LoadBundledOptions,\n type LoadBundledResult,\n} from './storage/load-bundled.js';\nexport {\n loadTenantOverride,\n defaultOverridePath,\n type LoadOverrideOptions,\n type LoadOverrideResult,\n} from './storage/load-override.js';\nexport {\n mergeCorpusWithOverride,\n type MergeResult,\n} from './storage/merge.js';\n\n// Query\nexport { appliesToStack } from './query/applies-to-stack.js';\nexport { appliesToChangeType } from './query/applies-to-change.js';\nexport { appliesToFiles } from './query/applies-to-files.js';\nexport {\n findApplicableRules,\n type FindApplicableRulesOptions,\n} from './query/run-query.js';\nexport { globToRegExp, matchesAnyGlob } from './query/glob.js';\nexport { computeCorpusVersionHash } from './query/corpus-version.js';\nexport { resolveStack } from './query/resolve-stack.js';\nexport {\n computeStaleRules,\n DEFAULT_STALENESS_THRESHOLD_DAYS,\n type StaleRule,\n type ComputeStaleRulesOptions,\n} from './query/staleness.js';\n\n// Analyze\nexport {\n parseUnifiedDiff,\n type ParsedDiff,\n type ParsedFile,\n type ParsedChangeType,\n} from './analyze/diff-parser.js';\nexport { runEvidenceMatcher } from './analyze/evidence-matchers/index.js';\n\n// PII / secret scrubber (PR-U 2026-05-25 \u2014 moved here from vo-mcp so KB\n// consumers don't need vo-mcp as a dependency to sanitize evidence excerpts).\nexport {\n sanitizeExcerpt,\n SANITIZE_DEFAULT_MAX_LEN,\n} from './pii-sanitize.js';\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 * 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/**\n * Aggregate per-call inference cost into the telemetry event's top-level shape\n * (Base44 rec #4 \u2014 \"instrument per-task cost NOW so the corpus can later justify\n * a learned router / cheaper tier\").\n *\n * PREFERS the engine's cumulative `token_usage` when present: `per_model_verdicts`\n * carries only the FINAL round, so summing it under-reports any call that\n * deliberated \u2014 and deliberation is what the priciest gate (`final-deep-verify`)\n * does. The engine accumulates every fan-out round, so its total is the honest one.\n * Falls back to summing the verdicts when the engine didn't report usage (older\n * engine build, or a path that never fanned out).\n *\n * - `per_model_tokens_in` / `per_model_tokens_out`: keyed by model id. Null when NO\n * usage was reported \u2014 never a map of fabricated zeros, so \"we don't know\" is\n * distinguishable from \"zero tokens\".\n * - `total_cost_usd`: summed ONLY from attested `cost_micro_usd` receipts, converted\n * micro-USD \u2192 USD. Null when nothing attested \u2014 never estimated from a price table.\n */\nexport function aggregateEventTokenUsage(\n src: ConsensusSuccess['per_model_verdicts'],\n engineUsage?: ConsensusSuccess['token_usage'],\n): Pick<ConsensusCallEvent, 'per_model_tokens_in' | 'per_model_tokens_out' | 'total_cost_usd'> {\n if (engineUsage !== undefined) {\n const hasIn = Object.keys(engineUsage.per_model_tokens_in).length > 0;\n const hasOut = Object.keys(engineUsage.per_model_tokens_out).length > 0;\n return {\n per_model_tokens_in: hasIn ? engineUsage.per_model_tokens_in : null,\n per_model_tokens_out: hasOut ? engineUsage.per_model_tokens_out : null,\n total_cost_usd:\n engineUsage.cost_micro_usd === null ? null : engineUsage.cost_micro_usd / 1_000_000,\n };\n }\n const tokensIn: Record<string, number> = {};\n const tokensOut: Record<string, number> = {};\n let anyTokensIn = false;\n let anyTokensOut = false;\n let costMicroUsd = 0;\n let anyCost = false;\n\n for (const v of src) {\n if (typeof v.input_tokens === 'number') {\n tokensIn[v.model] = (tokensIn[v.model] ?? 0) + v.input_tokens;\n anyTokensIn = true;\n }\n if (typeof v.output_tokens === 'number') {\n tokensOut[v.model] = (tokensOut[v.model] ?? 0) + v.output_tokens;\n anyTokensOut = true;\n }\n if (typeof v.cost_micro_usd === 'number') {\n costMicroUsd += v.cost_micro_usd;\n anyCost = true;\n }\n }\n\n return {\n per_model_tokens_in: anyTokensIn ? tokensIn : null,\n per_model_tokens_out: anyTokensOut ? tokensOut : null,\n total_cost_usd: anyCost ? costMicroUsd / 1_000_000 : null,\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 * 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\n/** Read only the OS keychain; never fall back to env-selected plaintext files. */\nexport function readStoredCredentialKeychainOnly(\n env: Readonly<Record<string, string | undefined>> = process.env,\n keychain: KeychainBackend = realKeychain,\n): StoredCredential | null {\n if (!keychainEnabled(env, keychain)) return null;\n const raw = keychain.get();\n return raw ? deserialize(raw) : null;\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", "import { resolve, sep } from 'node:path';\n\nconst SAFE_MEMORY_FILE_RE = /^[A-Za-z0-9][A-Za-z0-9._ -]*\\.md$/i;\n\nexport function isSafeMemoryFileName(fileName: string): boolean {\n return fileName.length <= 200\n && fileName.trim() === fileName\n && !fileName.includes('/')\n && !fileName.includes('\\\\')\n && !fileName.includes(':')\n && SAFE_MEMORY_FILE_RE.test(fileName);\n}\n\nexport function resolveMemoryFilePath(memoryDir: string, fileName: string): string {\n if (!isSafeMemoryFileName(fileName)) {\n throw new Error(`unsafe memory file_name: ${fileName.slice(0, 80)}`);\n }\n const root = resolve(memoryDir);\n const filePath = resolve(root, fileName);\n const rootPrefix = root.endsWith(sep) ? root : `${root}${sep}`;\n if (filePath !== root && !filePath.startsWith(rootPrefix)) {\n throw new Error(`memory file path escapes memory directory: ${fileName.slice(0, 80)}`);\n }\n return filePath;\n}\n", "/**\n * Who holds the memory-sync lock, and is that holder still alive?\n *\n * Split out of `sync-lock.ts` (400-line cap) because the abandonment decision is\n * the part that has to be reasoned about \u2014 and tested \u2014 quadrant by quadrant.\n *\n * ## The rule, and the defect it closes\n *\n * PR #9327 evaluated the TTL branch FIRST and independently of the liveness\n * evidence:\n *\n * ```ts\n * if (ageMs !== null && ageMs > ttlMs) return true; // \u2190 wins outright\n * \u2026then the same-host dead-pid check\n * ```\n *\n * So a sync that legitimately ran past the 15-minute TTL was declared abandoned\n * and STOLEN **while it was still writing** \u2014 producing exactly the two\n * concurrent writers the lock exists to prevent. Bigger inputs made it more\n * likely, which is backwards: the slower a sync is, the more certain it was to\n * be robbed.\n *\n * The rule here inverts that precedence. **Live-holder evidence DOMINATES.**\n *\n * | holder liveness | age \u2264 TTL | age > TTL |\n * | -------------------------- | --------- | --------- |\n * | `alive` (same host, pid up) | keep | **keep** |\n * | `dead` (same host, pid gone)| steal | steal |\n * | `unknown` (foreign/unreadable) | keep | steal |\n *\n * The TTL is now what it should always have been: the fallback for the one case\n * where liveness is genuinely unknowable, not an override for the case where it\n * is known.\n *\n * ## The residue this creates, stated rather than discovered later\n *\n * A REUSED pid reads as `alive`, so a lock whose holder died and whose pid was\n * recycled is never reclaimed by this host. That is the safe direction \u2014 the\n * contending sync fails closed with an error naming the lock path, and the\n * operator deletes one file \u2014 where the alternative is silent corruption. It is\n * also much rarer than it was: a sync can no longer run for minutes (see\n * `SYNC_DEADLINE_MS` in `memory-sync-http.ts`), so the window in which a lock is\n * held at all is now seconds.\n */\nimport { statSync, readFileSync } from 'node:fs';\n\n/** What a lock file contains. Pretty-printed on disk so a human can read it. */\nexport interface MemorySyncLockPayload {\n readonly pid: number;\n readonly hostname: string;\n readonly sessionId: string | null;\n /** Unique per acquire \u2014 the ownership proof `release()` checks. */\n readonly token: string;\n readonly acquiredAt: string;\n readonly acquiredAtMs: number;\n}\n\nexport interface LockRecord {\n readonly raw: string;\n readonly payload: MemorySyncLockPayload | null;\n}\n\n/**\n * Does `pid` exist on this host? Signal 0 performs the permission/existence\n * check without delivering anything. EPERM means the process EXISTS but belongs\n * to another user \u2014 alive, and therefore not reclaimable.\n */\nexport function defaultIsProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\nexport function toPayload(parsed: unknown): MemorySyncLockPayload | null {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const record = parsed as Record<string, unknown>;\n const token = record['token'];\n const host = record['hostname'];\n if (typeof token !== 'string' || token.length === 0) return null;\n const pid = record['pid'];\n const acquiredAtMs = record['acquiredAtMs'];\n return {\n pid: typeof pid === 'number' && Number.isInteger(pid) && pid > 0 ? pid : 0,\n hostname: typeof host === 'string' ? host : '',\n sessionId: typeof record['sessionId'] === 'string' ? record['sessionId'] : null,\n token,\n acquiredAt: typeof record['acquiredAt'] === 'string' ? record['acquiredAt'] : '',\n acquiredAtMs: typeof acquiredAtMs === 'number' && Number.isFinite(acquiredAtMs) ? acquiredAtMs : Number.NaN,\n };\n}\n\nexport function readLockRecord(path: string): LockRecord | null {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return null;\n }\n try {\n return { raw, payload: toPayload(JSON.parse(raw)) };\n } catch {\n return { raw, payload: null };\n }\n}\n\n/**\n * Age of a held lock in ms, or null when it cannot be established. Null is the\n * fail-closed value: an age we cannot compute must never read as expired.\n */\nexport function lockAgeMs(record: LockRecord, path: string, nowMs: number): number | null {\n let startedMs = Number.NaN;\n if (record.payload) {\n if (Number.isFinite(record.payload.acquiredAtMs)) {\n startedMs = record.payload.acquiredAtMs;\n } else if (record.payload.acquiredAt) {\n startedMs = Date.parse(record.payload.acquiredAt);\n }\n }\n if (!Number.isFinite(startedMs)) {\n try {\n startedMs = statSync(path).mtimeMs;\n } catch {\n return null;\n }\n }\n const age = nowMs - startedMs;\n // A negative age means the writer's clock is ahead of ours (or ours moved\n // back). Unknown, not expired.\n return Number.isFinite(age) && age >= 0 ? age : null;\n}\n\n/**\n * `alive` and `dead` are POSITIVE findings about the holder process. `unknown`\n * is everything we cannot interrogate: a foreign hostname (pid numbers mean\n * nothing across hosts), an unparseable payload (we do not know whose it is), a\n * missing pid, or a host we cannot even name.\n */\nexport type HolderLiveness = 'alive' | 'dead' | 'unknown';\n\nexport function classifyHolderLiveness(\n record: LockRecord,\n isProcessAlive: (pid: number) => boolean,\n thisHost: string,\n): HolderLiveness {\n const payload = record.payload;\n if (payload === null) return 'unknown';\n if (payload.pid <= 0) return 'unknown';\n if (thisHost.length === 0) return 'unknown';\n if (payload.hostname !== thisHost) return 'unknown';\n return isProcessAlive(payload.pid) ? 'alive' : 'dead';\n}\n\n/**\n * Is this lock abandoned? See the table in the module header \u2014 liveness\n * dominates, and the TTL applies ONLY to an unknowable holder.\n */\nexport function isLockAbandoned(\n record: LockRecord,\n ageMs: number | null,\n ttlMs: number,\n isProcessAlive: (pid: number) => boolean,\n thisHost: string,\n): boolean {\n const liveness = classifyHolderLiveness(record, isProcessAlive, thisHost);\n // A provably-running writer is NEVER stolen from, at any age. This is the\n // whole point of the module; do not reorder it below the TTL check again.\n if (liveness === 'alive') return false;\n // A same-host pid that no longer exists cannot be mid-write. Reclaim at once\n // rather than making every other session wait out the TTL.\n if (liveness === 'dead') return true;\n // Unknowable holder: the age is the only evidence there is, and an age we\n // could not compute (null) must never read as expired.\n return ageMs !== null && ageMs > ttlMs;\n}\n", "/**\n * memory sync LOCK \u2014 one writer at a time over `~/.claude/projects/<slug>/memory/`.\n *\n * ## The defect this closes\n *\n * Up to seven agent sessions run against the SAME memory directory on this\n * machine, and nothing serialized them. `vo_sync_config pull` rewrites every\n * local memory file while another session's `push` is mid-`readdirSync`/\n * `readFileSync`, so a push can ship a file that is half of the old content and\n * half of the new one; two concurrent `push` runs interleave their PUTs against\n * the same `memory_id`. Both are silent \u2014 the loser gets an HTTP 200.\n *\n * ## Why `openSync(path, 'wx')`\n *\n * O_EXCL create is the same race-free primitive `../../swarm/spawn-ledger.ts`\n * uses for slot claims: the kernel guarantees exactly one concurrent caller\n * creates the file, so the winner is decided without a second lock. This module\n * deliberately follows that file's shape rather than introducing a second\n * locking style into the package.\n *\n * ## Stale takeover, and why it is REQUIRED\n *\n * A killed process runs no `finally`, so an abandoned lock must be reclaimable\n * or one kill would deadlock memory sync on the host forever. The decision \u2014\n * which is where PR #9327's second MAJOR lived \u2014 is in `sync-lock-liveness.ts`:\n * live-holder evidence DOMINATES the TTL, so a sync that legitimately runs long\n * is never stolen from mid-write. Read that module's header table before\n * touching {@link DEFAULT_LOCK_TTL_MS}.\n *\n * ## Fail-closed rules (absent/unknown never resolves toward more concurrency)\n *\n * - Contention waits with backoff to a bounded deadline; on expiry the caller\n * gets an ERROR, never an unlocked sync.\n * - An unreadable lock file is NOT stolen on the pid signal (we cannot tell\n * whose it is) \u2014 only the mtime-derived TTL can reclaim it.\n * - A negative age (a clock that moved backwards) reads as UNKNOWN, not as\n * expired.\n * - `release()` removes the lock ONLY when the file still carries this handle's\n * token, so a caller that was legitimately taken over cannot delete its\n * successor's live lock.\n * - Acquiring does NOT create the memory directory unless the caller asks\n * ({@link MemorySyncLockOptions.createDir}). A push over a slug that has no\n * memory has nothing to serialize, and PR #9327's unconditional `mkdirSync`\n * littered an empty `memory/` into every project dir on the machine.\n *\n * RESIDUE, stated rather than discovered later: takeover is not atomic on a\n * POSIX/Win32 filesystem. Two callers can both judge the same lock abandoned;\n * the second re-reads the file and aborts its `unlinkSync` if the bytes changed,\n * with no `await` between that read and the unlink, so the window is a single\n * syscall gap rather than a scheduling quantum. It is not zero. The alternative\n * \u2014 never reclaiming \u2014 is a permanent deadlock, which is strictly worse.\n *\n * RESIDUE 2, the one this lock does NOT solve: mutual exclusion here is a\n * HOST-LOCAL file, while the resource it protects \u2014 the operator's cloud memory\n * index \u2014 is operator-GLOBAL. Two machines still race. See the fragment\n * `docs/vo/roadmap-log/2026-08-02-memory-sync-completable.md`.\n */\nimport { closeSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { hostname } from 'node:os';\nimport { join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport {\n defaultIsProcessAlive,\n isLockAbandoned,\n lockAgeMs,\n readLockRecord,\n toPayload,\n type LockRecord,\n type MemorySyncLockPayload,\n} from './sync-lock-liveness.js';\n\nexport {\n classifyHolderLiveness,\n defaultIsProcessAlive,\n isLockAbandoned,\n lockAgeMs,\n type HolderLiveness,\n type LockRecord,\n type MemorySyncLockPayload,\n} from './sync-lock-liveness.js';\n\n/**\n * Lock file name, inside the memory directory.\n *\n * It is NOT a memory file and must never be pushed. Two independent filters\n * already exclude it \u2014 `pushMemory` keeps only `*.md`, and\n * `isSafeMemoryFileName` rejects a leading `.` \u2014 and `memory-sync-http.ts`\n * excludes it by name on top of that. `sync-config-lock.test.ts` asserts the\n * exclusion against the real push path.\n */\nexport const MEMORY_SYNC_LOCK_FILE = '.memory-sync.lock';\n\n/**\n * 15 minutes. Applies ONLY to a holder whose liveness is unknowable (a foreign\n * hostname or an unreadable lock file) \u2014 a live same-host holder is never\n * stolen from at any age, and a dead one is reclaimed immediately without\n * waiting this out.\n *\n * Derivation, so the next reader can re-check it rather than trust it: a sync\n * is now hard-bounded at `SYNC_DEADLINE_MS` (120s) plus one in-flight request\n * (`REQUEST_TIMEOUT_MS`, 15s) = ~135s worst case \u2014 see `memory-sync-http.ts`.\n * 15 minutes is ~6.7x that. The old derivation (\"140 serial round trips \u2248 5\n * minutes\") described a push that could not finish inside any hook budget at\n * all; that push is gone.\n */\nexport const DEFAULT_LOCK_TTL_MS = 15 * 60_000;\n\n/**\n * How long a contending caller waits before failing closed \u2014 10 seconds.\n *\n * This number MUST fit inside the budget of whatever is calling. PR #9327 set\n * it to 60_000 while its only production callers were hooks that killed the\n * subprocess after 2-3 SECONDS, so the contention budget could never elapse and\n * the fail-closed error it guarded could never surface \u2014 dead code by\n * construction. 10s is under the SessionStart pull hook's own 20s ceiling; the\n * detached Stop push, which has no session-blocking budget, passes a larger\n * value explicitly (`--lock-wait-ms`) rather than inheriting a guess.\n */\nexport const DEFAULT_LOCK_WAIT_MS = 10_000;\n\nconst INITIAL_BACKOFF_MS = 25;\nconst MAX_BACKOFF_MS = 500;\nconst BACKOFF_FACTOR = 1.6;\n\nexport interface MemorySyncLockOptions {\n /** Directory the lock lives in. NOT created unless {@link createDir}. */\n readonly memoryDir: string;\n readonly sessionId?: string | undefined;\n /** Contention budget, ms. Default {@link DEFAULT_LOCK_WAIT_MS}. */\n readonly waitMs?: number | undefined;\n /** Abandonment threshold for an UNKNOWABLE holder, ms. Default {@link DEFAULT_LOCK_TTL_MS}. */\n readonly ttlMs?: number | undefined;\n /**\n * Create `memoryDir` if absent. Only a caller that is about to WRITE files\n * into it (i.e. `pull`) should pass true. A `push` must not conjure a memory\n * directory for a project slug that legitimately has none.\n */\n readonly createDir?: boolean | undefined;\n readonly now?: (() => number) | undefined;\n readonly sleep?: ((ms: number) => Promise<void>) | undefined;\n /** Injected for tests; production uses signal-0 against the real process table. */\n readonly isProcessAlive?: ((pid: number) => boolean) | undefined;\n}\n\nexport interface MemorySyncLockHandle {\n readonly path: string;\n readonly payload: MemorySyncLockPayload;\n /** The abandoned holder this acquire reclaimed from, else null. */\n readonly tookOverFrom: MemorySyncLockPayload | null;\n /** Idempotent. NEVER throws. Removes the file only if this handle still owns it. */\n release(): void;\n}\n\nfunction positiveOr(value: number | undefined, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\n\ntype CreateOutcome = { readonly ok: true } | { readonly ok: false; readonly exists: boolean; readonly message: string };\n\n/** Create `path` with O_EXCL, writing `contents`. Never leaves a partial file. */\nfunction createExclusive(path: string, contents: string): CreateOutcome {\n let fd: number;\n try {\n fd = openSync(path, 'wx');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n return { ok: false, exists: code === 'EEXIST', message: err instanceof Error ? err.message : String(err) };\n }\n try {\n writeFileSync(fd, contents, 'utf8');\n } catch (err) {\n closeSync(fd);\n // The empty file we just created would otherwise be a lock nobody holds.\n try {\n unlinkSync(path);\n } catch {\n /* best effort \u2014 the TTL reclaims it */\n }\n return { ok: false, exists: false, message: err instanceof Error ? err.message : String(err) };\n }\n // Closed BEFORE any later unlink: Windows refuses to unlink a file that is\n // still held open (EPERM), which would make every release on Windows leak.\n closeSync(fd);\n return { ok: true };\n}\n\n/**\n * Remove a lock we judged abandoned, but only if it is still byte-identical to\n * what we judged.\n *\n * This is the compare-and-delete guard that stops two simultaneous takeovers\n * from deleting a live SUCCESSOR's lock: A and B both judge lock L abandoned, A\n * unlinks it and immediately re-creates its own lock A'; B must then NOT unlink\n * A'. B's confirming read sees A' bytes, which differ from L, so it aborts.\n * There is deliberately no `await` between the read and the unlink \u2014 see the\n * RESIDUE note in the header for the syscall-gap window that remains.\n * Covered by `sync-lock-takeover-guard.test.ts`, including a mutation that\n * proves removing the byte-comparison lets B destroy A'.\n */\nexport function removeAbandoned(path: string, expectedRaw: string): void {\n let current: string;\n try {\n current = readFileSync(path, 'utf8');\n } catch {\n return; // already gone; the acquire loop retries the create\n }\n if (current !== expectedRaw) return; // replaced since we judged it \u2014 not ours to remove\n try {\n unlinkSync(path);\n } catch {\n /* raced or unwritable; the acquire loop re-evaluates against the deadline */\n }\n}\n\n/** Release is defined here so it never captures the acquire loop's locals. */\nfunction makeRelease(path: string, token: string): () => void {\n let released = false;\n return () => {\n if (released) return;\n released = true;\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n // ENOENT \u2014 someone reclaimed a lock we had let go stale. Tolerated by\n // contract: release must never throw.\n return;\n }\n let stillOurs: boolean;\n try {\n stillOurs = toPayload(JSON.parse(raw))?.token === token;\n } catch {\n stillOurs = false;\n }\n if (!stillOurs) return; // a successor owns it; deleting theirs would un-serialize them\n try {\n unlinkSync(path);\n } catch {\n /* never throw from release */\n }\n };\n}\n\nfunction describeHolder(record: LockRecord | null): string {\n const payload = record?.payload;\n if (!payload) return 'an unreadable lock file';\n return `pid ${payload.pid} on ${payload.hostname || '(unknown host)'} (session ${payload.sessionId ?? 'unknown'}, held since ${payload.acquiredAt || 'unknown'})`;\n}\n\n/**\n * Take the memory-sync lock, waiting out contention up to `waitMs`.\n *\n * @throws when the deadline expires or the lock file is unusable \u2014 a caller\n * that cannot hold the lock must NOT sync. Callers should prefer\n * {@link withMemorySyncLock}, which releases in `finally`.\n */\nexport async function acquireMemorySyncLock(options: MemorySyncLockOptions): Promise<MemorySyncLockHandle> {\n const waitMs = positiveOr(options.waitMs, DEFAULT_LOCK_WAIT_MS);\n const ttlMs = positiveOr(options.ttlMs, DEFAULT_LOCK_TTL_MS);\n const now = options.now ?? Date.now;\n const sleep = options.sleep ?? ((ms: number) => new Promise<void>((resolve) => { setTimeout(resolve, ms); }));\n const isProcessAlive = options.isProcessAlive ?? defaultIsProcessAlive;\n const thisHost = hostname();\n const path = join(options.memoryDir, MEMORY_SYNC_LOCK_FILE);\n\n // Only when the caller is about to write into it. See MemorySyncLockOptions.\n if (options.createDir === true) mkdirSync(options.memoryDir, { recursive: true });\n\n const deadline = now() + waitMs;\n let backoffMs = INITIAL_BACKOFF_MS;\n let tookOverFrom: MemorySyncLockPayload | null = null;\n let holderDescription = 'another session';\n\n for (;;) {\n const acquiredAtMs = now();\n const payload: MemorySyncLockPayload = {\n pid: process.pid,\n hostname: thisHost,\n sessionId: options.sessionId ?? null,\n token: randomUUID(),\n acquiredAt: new Date(acquiredAtMs).toISOString(),\n acquiredAtMs,\n };\n const created = createExclusive(path, `${JSON.stringify(payload, null, 2)}\\n`);\n if (created.ok) {\n return { path, payload, tookOverFrom, release: makeRelease(path, payload.token) };\n }\n if (!created.exists) {\n throw new Error(\n `memory sync lock ${path} could not be created (${created.message}) \u2014 refusing to sync without exclusion`,\n );\n }\n\n const record = readLockRecord(path);\n let reclaimed = false;\n if (record) {\n holderDescription = describeHolder(record);\n const age = lockAgeMs(record, path, now());\n if (isLockAbandoned(record, age, ttlMs, isProcessAlive, thisHost)) {\n tookOverFrom = record.payload;\n removeAbandoned(path, record.raw);\n reclaimed = true;\n }\n }\n\n // Checked on EVERY path, including after a takeover, so an unremovable lock\n // cannot spin here forever.\n if (now() >= deadline) {\n throw new Error(\n `memory sync lock ${path} is held by ${holderDescription}; waited ${waitMs}ms \u2014 ` +\n 'refusing to sync unlocked (concurrent memory writes corrupt the shared index). ' +\n 'If that holder is provably gone, delete the lock file.',\n );\n }\n // ALWAYS yield, including straight after a takeover. Retrying a reclaim\n // immediately looks free, but a lock that cannot actually be removed (EPERM,\n // or one another session keeps re-creating) then busy-spins for the whole\n // wait budget \u2014 a hot loop on a host that already runs seven sessions. A\n // reclaim resets the backoff instead, so the retry is still fast (25ms).\n if (reclaimed) backoffMs = INITIAL_BACKOFF_MS;\n await sleep(Math.max(1, Math.min(backoffMs, deadline - now())));\n if (!reclaimed) backoffMs = Math.min(MAX_BACKOFF_MS, Math.ceil(backoffMs * BACKOFF_FACTOR));\n }\n}\n\n/** Run `fn` holding the memory-sync lock. Releases in `finally`, always. */\nexport async function withMemorySyncLock<T>(\n options: MemorySyncLockOptions,\n fn: (handle: MemorySyncLockHandle) => Promise<T>,\n): Promise<T> {\n const handle = await acquireMemorySyncLock(options);\n try {\n return await fn(handle);\n } finally {\n handle.release();\n }\n}\n", "/**\n * MEMORY.md index MERGE \u2014 stop the shared index being last-writer-wins.\n *\n * ## The defect this closes\n *\n * Every session appends its own lines to ONE ~19KB flat index,\n * `~/.claude/projects/<slug>/memory/MEMORY.md`. `pushMemory` maps\n * `file_name -> memory_id` and PUTs the local bytes, so session B's push\n * REPLACES the document session A pushed minutes earlier. A's topic files\n * survive (they are separate entries); A's index LINES are gone. With up to\n * seven sessions the index is whichever one pushed last.\n *\n * A lock serializes the writes and does not fix this: B still PUTs a document\n * that never contained A's lines. The write has to be a MERGE.\n *\n * ## What is merged\n *\n * The index is one line per memory: `- [Title](file.md) \u2014 hook`. The union is\n * keyed on the LINK TARGET (`file.md`), case-insensitively, because that is the\n * memory's identity \u2014 a re-worded title or hook for the same file must UPDATE\n * the row in place, not add a second one.\n *\n * - Local order is preserved exactly; the local wording wins for any file both\n * sides carry (the pushing session is the one that just edited it).\n * - Cloud-only rows are inserted after the LAST local row, in cloud order, so\n * they join the list rather than landing after whatever prose follows it.\n * - Only local NON-row lines (headings, prose, blanks) survive. Merging the\n * cloud's prose too would duplicate the heading on every push.\n *\n * ## The trade-off, chosen deliberately\n *\n * A union cannot distinguish \"session A DELETED this row\" from \"session A never\n * had it\": a row A removed locally is still in the cloud, so the next push\n * RESURRECTS it. That is a real cost and it is the one we accept. The\n * alternative \u2014 last-writer-wins \u2014 silently destroys up to six other sessions'\n * index entries on every push, and a lost row means a memory that recall can no\n * longer find. A resurrected row is visible, wrong-looking, and one edit away\n * from gone; a lost row is invisible. Deleting a memory for real therefore means\n * deleting the topic FILE (which drops out of the push set) and then removing\n * the row \u2014 the row alone will come back.\n *\n * Wording is still last-writer-wins PER ROW: if A re-words a hook and B pushes\n * afterwards with the old wording, B's wording lands. No row is ever lost, which\n * is the property this module is responsible for.\n */\n\n/** The one index file. Everything else in the memory dir is a topic. */\nexport const MEMORY_INDEX_FILE = 'MEMORY.md';\n\n/** Case-insensitive, matching `memory-knowledge-bridge.ts`'s own comparison. */\nexport function isMemoryIndexFile(fileName: string): boolean {\n return fileName.toUpperCase() === MEMORY_INDEX_FILE.toUpperCase();\n}\n\n/** `- [Title](target) \u2014 hook`, with `*` bullets and leading indent tolerated. */\nconst INDEX_ROW_RE = /^\\s*[-*]\\s+\\[[^\\]]*\\]\\(([^)]+)\\)/;\n\n/**\n * The link target of an index row, NORMALIZED for keying, or null.\n *\n * Normalization is the point: the raw target is whatever the writing session\n * happened to type, and Markdown accepts several spellings of the same file.\n * PR #9327 keyed on the raw string, so `- [X](./foo.md)` and `- [X](foo.md)`\n * were two different memories and the union emitted BOTH \u2014 a duplicate row per\n * spelling, forever, in the index every session reads. Reproduced before the\n * fix: merging those two documents returned `addedFromCloud.length === 1`.\n *\n * Handled, in order: surrounding `<\u2026>` (Markdown's escape for targets with\n * spaces), a trailing `\"Title\"` / `'Title'` link title, Windows `\\` separators,\n * any number of leading `./` segments, and case. Deliberately NOT handled:\n * percent-decoding and `../` resolution \u2014 a memory index is a flat list of\n * sibling files, so those spellings do not occur, and guessing at them would\n * risk collapsing two genuinely different targets into one row.\n */\nexport function indexRowKey(line: string): string | null {\n const match = INDEX_ROW_RE.exec(line);\n if (!match) return null;\n let target = match[1].trim();\n if (target.startsWith('<') && target.endsWith('>')) target = target.slice(1, -1).trim();\n target = target.replace(/\\s+([\"'])[\\s\\S]*\\1$/, '').trim(); // drop a link title\n // A URL is not a memory file; leave it exactly as written rather than\n // mangling its scheme's `//` while collapsing path separators below.\n if (!/^[a-z][a-z0-9+.-]*:\\/\\//i.test(target)) {\n target = target.replace(/\\\\/g, '/').replace(/\\/{2,}/g, '/');\n target = target.replace(/^(?:\\.\\/)+/, '');\n }\n return target.length > 0 ? target.toLowerCase() : null;\n}\n\nexport interface MemoryIndexMergeResult {\n /** The document to push. */\n readonly content: string;\n /** Rows present only in the cloud copy that this merge preserved. */\n readonly addedFromCloud: readonly string[];\n}\n\n/**\n * Union the local index with the cloud index, keyed on each row's link target.\n *\n * Returns the local content untouched when there is no cloud document to merge\n * (first push, or a cloud read that returned nothing) \u2014 an absent remote must\n * never subtract from what the caller was about to push.\n */\nexport function mergeMemoryIndex(localContent: string, cloudContent: string | null | undefined): MemoryIndexMergeResult {\n if (typeof cloudContent !== 'string' || cloudContent.trim().length === 0) {\n return { content: localContent, addedFromCloud: [] };\n }\n\n const eol = localContent.includes('\\r\\n') ? '\\r\\n' : '\\n';\n const localLines = localContent.split(/\\r?\\n/);\n\n const localKeys = new Set<string>();\n let lastLocalRowIndex = -1;\n for (let i = 0; i < localLines.length; i++) {\n const key = indexRowKey(localLines[i]);\n if (key === null) continue;\n localKeys.add(key);\n lastLocalRowIndex = i;\n }\n\n const addedFromCloud: string[] = [];\n const seenCloudKeys = new Set<string>();\n for (const rawLine of cloudContent.split(/\\r?\\n/)) {\n const key = indexRowKey(rawLine);\n if (key === null) continue;\n if (localKeys.has(key) || seenCloudKeys.has(key)) continue;\n seenCloudKeys.add(key);\n addedFromCloud.push(rawLine.replace(/\\r$/, ''));\n }\n\n if (addedFromCloud.length === 0) {\n return { content: localContent, addedFromCloud: [] };\n }\n\n const merged =\n lastLocalRowIndex >= 0\n ? [...localLines.slice(0, lastLocalRowIndex + 1), ...addedFromCloud, ...localLines.slice(lastLocalRowIndex + 1)]\n : [...localLines, ...addedFromCloud];\n\n return { content: merged.join(eol), addedFromCloud };\n}\n", "/**\n * The two budget primitives every memory round trip runs under, plus the bounded\n * fan-out that makes a first-run push finish in seconds instead of minutes.\n *\n * These exist because \"how long can a sync take?\" had NO answer before: the push\n * was an unbounded serial loop over ~140 round trips with no per-request\n * timeout, so a single hung socket held the memory lock indefinitely and every\n * budget downstream (hook deadline, lock TTL, contention wait) was a guess about\n * an unbounded quantity. Bound the parts and the whole becomes statable:\n *\n * worst case = SYNC_DEADLINE_MS + REQUEST_TIMEOUT_MS \u2248 135s\n *\n * That single number is what `DEFAULT_LOCK_TTL_MS`, the Stop hook's\n * `--lock-wait-ms`, and the PR body's duration claim are all derived from. If\n * you change a constant here, change those with it.\n */\n\n/**\n * Ceiling on ONE control-plane round trip. A warm call is ~0.2-0.5s; a Cloud Run\n * cold start is ~5-10s. 15s covers the cold start with headroom and still fails\n * a genuinely hung socket fast enough that the overall deadline means something.\n */\nexport const REQUEST_TIMEOUT_MS = 15_000;\n\n/**\n * Ceiling on a whole push or pull, checked cooperatively BETWEEN round trips.\n * Two minutes is ~2x the measured worst-case first-run push (~60s: 140 trips at\n * concurrency 6 over ~24 waves), so it only ever fires on a genuinely sick\n * control plane.\n */\nexport const SYNC_DEADLINE_MS = 120_000;\n\n/**\n * Simultaneous in-flight round trips. The per-file PUT/POSTs are independent \u2014\n * different `memory_id`s, no ordering between them \u2014 so serializing them bought\n * nothing but latency. 6 keeps a first run to ~24 waves without hammering the\n * control plane from seven sessions at once.\n */\nexport const PUSH_CONCURRENCY = 6;\n\n/** Thrown when the overall sync deadline expires. Distinct so callers can name it. */\nexport class SyncDeadlineExceededError extends Error {\n constructor(elapsedMs: number, budgetMs: number) {\n super(\n `memory sync exceeded its ${budgetMs}ms deadline after ${elapsedMs}ms \u2014 ` +\n 'aborting so the lock is released instead of held indefinitely',\n );\n this.name = 'SyncDeadlineExceededError';\n }\n}\n\n/** Thrown when a single round trip exceeds {@link REQUEST_TIMEOUT_MS}. */\nexport class RequestTimeoutError extends Error {\n constructor(url: string, budgetMs: number) {\n super(`memory sync request to ${url} exceeded ${budgetMs}ms`);\n this.name = 'RequestTimeoutError';\n }\n}\n\n/**\n * A cooperative deadline. `check()` throws once the budget is spent; callers\n * invoke it before starting each round trip, so a sync that is already over\n * budget stops issuing work rather than discovering it at the end.\n */\nexport interface SyncDeadline {\n check(): void;\n remainingMs(): number;\n}\n\nexport function createSyncDeadline(budgetMs: number = SYNC_DEADLINE_MS, now: () => number = Date.now): SyncDeadline {\n const startedAt = now();\n return {\n check() {\n const elapsed = now() - startedAt;\n if (elapsed > budgetMs) throw new SyncDeadlineExceededError(elapsed, budgetMs);\n },\n remainingMs() {\n return Math.max(0, budgetMs - (now() - startedAt));\n },\n };\n}\n\n/**\n * Race a round trip against {@link REQUEST_TIMEOUT_MS}.\n *\n * The abandoned request keeps running \u2014 `FetchLike` has no abort surface \u2014 but\n * the SYNC stops waiting on it, which is the property that matters: the caller\n * unwinds, `withMemorySyncLock`'s `finally` runs, and the lock is released\n * rather than pinned to a hung socket for the life of the process.\n */\nexport async function withRequestTimeout<T>(\n url: string,\n run: () => Promise<T>,\n budgetMs: number = REQUEST_TIMEOUT_MS,\n): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n return await Promise.race([\n run(),\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new RequestTimeoutError(url, budgetMs)), budgetMs);\n // Do not keep the process alive just for this timer.\n timer.unref?.();\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/**\n * Map `items` through `fn` with at most `limit` in flight, ALWAYS awaiting every\n * started task before returning.\n *\n * Never rejects: each result is settled individually. A rejecting task must not\n * abandon its siblings mid-flight \u2014 that is how you get a half-finished push\n * whose successful uploads were never recorded in the state cache, which then\n * re-sends them forever.\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<Array<{ ok: true; value: R } | { ok: false; error: unknown }>> {\n const results: Array<{ ok: true; value: R } | { ok: false; error: unknown }> = new Array(items.length);\n const width = Math.max(1, Math.min(limit, items.length));\n let next = 0;\n\n async function worker(): Promise<void> {\n for (;;) {\n const index = next++;\n if (index >= items.length) return;\n try {\n results[index] = { ok: true, value: await fn(items[index] as T, index) };\n } catch (error) {\n results[index] = { ok: false, error };\n }\n }\n }\n\n await Promise.all(Array.from({ length: width }, () => worker()));\n return results;\n}\n", "/**\n * INCREMENTAL PUSH state \u2014 what this host has already uploaded, so a push does\n * not re-send ~70 unchanged files on every session end.\n *\n * ## The defect this closes\n *\n * `pushMemory` sent one PUT/POST per memory file plus one knowledge-bridge POST\n * per topic file on EVERY run: ~140 serial round trips at this operator's ~70\n * files. Measured on origin/main with a deliberately generous 20ms-per-call\n * stub, that is 140 calls / 4,623ms; at a realistic warm control-plane latency\n * (~0.2-0.5s) it is 30-70s, and against a cold Cloud Run instance it is minutes.\n * The Stop hook that drives it killed the subprocess after 3 SECONDS. The push\n * could therefore never complete, the lock's contention budget could never\n * elapse, and \u2014 because a killed process runs no `finally` \u2014 every run left a\n * lock behind.\n *\n * ## The key, and why it is the SENT bytes and not the file bytes\n *\n * Each entry records the sha256 of the payload this host last successfully\n * uploaded for that `file_name`. Not the local file's hash \u2014 the payload's.\n * That distinction is what makes `MEMORY.md` work: its pushed content is the\n * MERGE of local and cloud (`memory-index-merge.ts`), so the cloud side can\n * change the bytes to send while the local file is untouched. Hashing the\n * merged result means the index is skipped exactly when the merge is a no-op,\n * with no special case anywhere in the push loop.\n *\n * ## Fail-closed by construction\n *\n * Every uncertainty resolves toward SENDING, never toward skipping:\n *\n * - no state file, unreadable, bad JSON, wrong `version` \u2192 empty cache \u2192 push\n * everything (first run).\n * - a different `controlPlaneUrl` than the one recorded \u2192 empty cache. Pointing\n * at a new control plane must not \"skip\" files that plane has never seen.\n * - the server's GET does not list an entry for the file \u2192 push it even on a\n * hash hit. A cache hit only ever means \"the bytes I sent are the bytes I\n * would send\"; it is never evidence the server still holds them.\n * - a failed upload does NOT record a hash, so the next run retries it.\n *\n * The state file is a dotfile inside the memory dir, which three independent\n * filters already exclude from the push set (`*.md` only, `isSafeMemoryFileName`\n * rejects a leading `.`, and the bridge filters `*.md` too).\n */\nimport { createHash } from 'node:crypto';\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** Not a memory file; never pushed. Sibling of `MEMORY_SYNC_LOCK_FILE`. */\nexport const MEMORY_SYNC_STATE_FILE = '.memory-sync-state.json';\n\nconst STATE_VERSION = 1;\n\nexport interface PushCacheEntry {\n /** sha256 of the payload last successfully PUT/POSTed to the memory route. */\n readonly memoryHash?: string;\n /** sha256 of the content last successfully upserted into the knowledge corpus. */\n readonly knowledgeHash?: string;\n}\n\nexport interface PushCache {\n readonly controlPlaneUrl: string;\n readonly entries: Map<string, PushCacheEntry>;\n /**\n * When this host last upserted EVERY memory file into the knowledge corpus,\n * epoch ms, or null if never. See {@link KNOWLEDGE_FULL_SWEEP_MS}.\n */\n knowledgeSweptAtMs: number | null;\n}\n\n/**\n * How stale the knowledge corpus is allowed to get before the next push\n * re-upserts every file regardless of hash \u2014 24 hours.\n *\n * NEIGHBOUR CHECK, and the reason this exists. `needsMemoryPush` has TWO\n * independent conditions: the hash AND `serverHasEntry`, because a local cache\n * hit is never evidence the server still holds the bytes. The knowledge bridge\n * one branch over has no equivalent \u2014 it only POSTs, never GETs, so there is no\n * server-side evidence to consult. Skipping on the hash alone would therefore\n * fail OPEN: if the corpus is wiped or a route drifts, an unchanged memory file\n * is never re-sent and `vo_private_knowledge_context` recall stays empty\n * forever. That is precisely the reservoir-empty bug the bridge was built to fix\n * (live-fire 2026-07-24), reintroduced by its own optimisation.\n *\n * A periodic full sweep bounds the divergence at 24h while keeping the\n * steady-state win for every other run of the day.\n */\nexport const KNOWLEDGE_FULL_SWEEP_MS = 24 * 60 * 60_000;\n\nexport function sha256(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\nfunction statePath(memoryDir: string): string {\n return join(memoryDir, MEMORY_SYNC_STATE_FILE);\n}\n\n/**\n * Load the cache for `controlPlaneUrl`. Returns an EMPTY cache \u2014 meaning \"push\n * everything\" \u2014 for every failure mode, including a mismatched control plane.\n */\nexport function readPushCache(memoryDir: string, controlPlaneUrl: string): PushCache {\n const empty: PushCache = { controlPlaneUrl, entries: new Map(), knowledgeSweptAtMs: null };\n let raw: string;\n try {\n raw = readFileSync(statePath(memoryDir), 'utf8');\n } catch {\n return empty;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return empty;\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return empty;\n const obj = parsed as Record<string, unknown>;\n if (obj['version'] !== STATE_VERSION) return empty;\n if (obj['controlPlaneUrl'] !== controlPlaneUrl) return empty;\n const files = obj['entries'];\n if (typeof files !== 'object' || files === null || Array.isArray(files)) return empty;\n\n const entries = new Map<string, PushCacheEntry>();\n for (const [name, value] of Object.entries(files as Record<string, unknown>)) {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) continue;\n const row = value as Record<string, unknown>;\n const memoryHash = typeof row['memoryHash'] === 'string' ? row['memoryHash'] : undefined;\n const knowledgeHash = typeof row['knowledgeHash'] === 'string' ? row['knowledgeHash'] : undefined;\n if (memoryHash === undefined && knowledgeHash === undefined) continue;\n entries.set(name, {\n ...(memoryHash !== undefined ? { memoryHash } : {}),\n ...(knowledgeHash !== undefined ? { knowledgeHash } : {}),\n });\n }\n const sweptAt = obj['knowledgeSweptAtMs'];\n return {\n controlPlaneUrl,\n entries,\n // An unreadable/absent sweep stamp reads as NEVER SWEPT, which forces a full\n // sweep \u2014 the fail-closed direction (more upserts, never fewer).\n knowledgeSweptAtMs: typeof sweptAt === 'number' && Number.isFinite(sweptAt) ? sweptAt : null,\n };\n}\n\n/**\n * Persist the cache. Best-effort by contract: a state file we cannot write\n * costs the NEXT run a full re-push, which is correct-but-slow. It must never\n * turn a completed upload into a reported failure.\n */\nexport function writePushCache(memoryDir: string, cache: PushCache): void {\n const entries: Record<string, PushCacheEntry> = {};\n for (const [name, value] of cache.entries) entries[name] = value;\n try {\n writeFileSync(\n statePath(memoryDir),\n `${JSON.stringify(\n {\n version: STATE_VERSION,\n controlPlaneUrl: cache.controlPlaneUrl,\n knowledgeSweptAtMs: cache.knowledgeSweptAtMs,\n entries,\n },\n null,\n 2,\n )}\\n`,\n 'utf8',\n );\n } catch {\n /* see the doc comment \u2014 never fail a sync over its own bookkeeping */\n }\n}\n\n/** Record a successful memory upload. Mutates `cache` in place. */\nexport function recordMemoryPush(cache: PushCache, fileName: string, payloadHash: string): void {\n cache.entries.set(fileName, { ...cache.entries.get(fileName), memoryHash: payloadHash });\n}\n\n/** Record a successful knowledge upsert. Mutates `cache` in place. */\nexport function recordKnowledgePush(cache: PushCache, fileName: string, contentHash: string): void {\n cache.entries.set(fileName, { ...cache.entries.get(fileName), knowledgeHash: contentHash });\n}\n\n/**\n * Drop rows for files that no longer exist locally, so the state file cannot\n * grow without bound as memories are renamed or deleted.\n *\n * Deleting the row is deliberately NOT a cloud delete: neither push nor pull\n * has ever removed a cloud entry, and a local delete is not evidence the\n * operator wants the backup destroyed. It only means a file re-created later\n * with identical bytes is re-sent once \u2014 the safe direction.\n */\nexport function pruneMissing(cache: PushCache, presentFileNames: readonly string[]): void {\n const present = new Set(presentFileNames);\n for (const name of [...cache.entries.keys()]) {\n if (!present.has(name)) cache.entries.delete(name);\n }\n}\n\n/**\n * Should this payload be uploaded to the memory route?\n *\n * `serverHasEntry` is the second, independent condition: a cache hit alone is\n * never proof the server still holds the bytes.\n */\nexport function needsMemoryPush(cache: PushCache, fileName: string, payloadHash: string, serverHasEntry: boolean): boolean {\n if (!serverHasEntry) return true;\n return cache.entries.get(fileName)?.memoryHash !== payloadHash;\n}\n\n/**\n * Is a FULL knowledge sweep due? True when this host has never swept, or the\n * last sweep is older than {@link KNOWLEDGE_FULL_SWEEP_MS}. Unknown always\n * resolves toward sweeping.\n */\nexport function knowledgeSweepDue(cache: PushCache, nowMs: number = Date.now()): boolean {\n const swept = cache.knowledgeSweptAtMs;\n if (swept === null || !Number.isFinite(swept)) return true;\n const age = nowMs - swept;\n // A negative age (clock moved) is unknown, not fresh.\n return !(age >= 0 && age < KNOWLEDGE_FULL_SWEEP_MS);\n}\n\n/**\n * Should this content be re-upserted into the knowledge corpus?\n *\n * `sweepDue` bypasses the hash entirely \u2014 see {@link KNOWLEDGE_FULL_SWEEP_MS}\n * for why an upsert-only route cannot be skipped on local evidence alone.\n */\nexport function needsKnowledgePush(\n cache: PushCache,\n fileName: string,\n contentHash: string,\n sweepDue = false,\n): boolean {\n if (sweepDue) return true;\n return cache.entries.get(fileName)?.knowledgeHash !== contentHash;\n}\n", "/**\n * The control-plane half of `vo_sync_config` \u2014 the pull and push round trips\n * against `/api/v1/agent-config/memory/*`.\n *\n * Callers MUST hold the memory-sync lock (see `sync-lock.ts`) \u2014 neither function\n * takes it itself, because a pull and a push have to be serialized against EACH\n * OTHER, which only the orchestrating caller can arrange.\n *\n * ## Push is INCREMENTAL and BOUNDED (the 2026-08-02 rework)\n *\n * It used to be an unbounded serial loop: one PUT/POST per memory file, every\n * run, no per-request timeout. At this operator's ~70 files that is 71 memory\n * trips plus 69 knowledge trips = ~140 serial round trips \u2014 measured on\n * origin/main at 140 calls / 4,623ms against a 20ms/call stub, i.e. 30-70s\n * warm and minutes cold. The Stop hook driving it killed the child at 3,000ms,\n * so the push could never finish, the lock's contention budget could never\n * elapse, and each killed run leaked a lock (no `finally` on a kill).\n *\n * Three changes make it completable, and the numbers now agree end to end:\n *\n * 1. **Incremental** \u2014 `memory-push-cache.ts` remembers the sha256 of the\n * payload last successfully uploaded per file. Steady state is 1 GET and\n * zero writes.\n * 2. **Bounded fan-out** \u2014 `PUSH_CONCURRENCY` (6) independent round trips in\n * flight, so even a first run is ~24 waves rather than 140 hops.\n * 3. **Bounded time** \u2014 `REQUEST_TIMEOUT_MS` per trip and a cooperative\n * `SYNC_DEADLINE_MS` overall, so \"how long can this hold the lock?\" has an\n * answer (~135s) instead of being unbounded.\n *\n * Measured costs, first run vs steady state, are asserted in\n * `memory-push-incremental.test.ts` \u2014 not estimated here.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';\nimport { resolveMemoryFilePath } from './safe-memory-file.js';\nimport { MEMORY_SYNC_LOCK_FILE } from './sync-lock.js';\nimport { isMemoryIndexFile, mergeMemoryIndex } from './memory-index-merge.js';\nimport {\n createSyncDeadline,\n mapWithConcurrency,\n PUSH_CONCURRENCY,\n withRequestTimeout,\n type SyncDeadline,\n} from './bounded-sync.js';\nimport {\n needsMemoryPush,\n pruneMissing,\n readPushCache,\n recordMemoryPush,\n sha256,\n writePushCache,\n type PushCache,\n} from './memory-push-cache.js';\n\nexport interface 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/** Options threaded in by `sync-config.ts`; defaults keep the tests' 5-arg form working. */\nexport interface PushOptions {\n readonly cache?: PushCache | undefined;\n readonly deadline?: SyncDeadline | undefined;\n readonly concurrency?: number | undefined;\n /**\n * Write the state file here. Defaults to true ONLY when this function owned\n * the cache: a caller that passes `cache` in (sync-config.ts) also runs the\n * knowledge bridge against the same object afterwards and persists once, at\n * the end \u2014 persisting here would drop every `knowledgeHash` the bridge is\n * about to record.\n */\n readonly persistCache?: boolean | undefined;\n}\n\n/**\n * Pull cloud memory entries to local files.\n *\n * One GET plus local writes \u2014 it was never the expensive direction, but it now\n * runs under the same request timeout so a hung socket cannot pin the lock.\n */\nexport async 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 withRequestTimeout(url, () =>\n fetchFn(url, {\n method: 'GET',\n headers: {\n authorization: `Bearer ${token}`,\n },\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 // Every path is resolved BEFORE any write, so one unsafe file_name aborts the\n // whole pull rather than leaving a half-applied one.\n const writes = data.entries.map((entry) => ({\n entry,\n filePath: resolveMemoryFilePath(memoryDir, entry.file_name),\n }));\n mkdirSync(memoryDir, { recursive: true });\n const files: string[] = [];\n\n for (const { entry, filePath } of writes) {\n writeFileSync(filePath, entry.content, 'utf8');\n files.push(entry.file_name);\n }\n\n return { pulled: data.entries.length, files };\n}\n\nexport interface PushMemoryResult {\n /** Files actually uploaded this run \u2014 always `created + updated`. */\n readonly pushed: number;\n readonly created: number;\n readonly updated: number;\n /**\n * Files whose payload was byte-identical to what this host last uploaded AND\n * which the server still lists. The whole point of the rework: in steady\n * state this is every file and `pushed` is 0.\n */\n readonly skipped: number;\n /**\n * Rows that existed ONLY in the cloud index and were carried into a pushed\n * document. Non-zero means this push preserved another session's entries that\n * the pre-merge code would have destroyed. Counted only for the index\n * documents actually uploaded \u2014 a skipped index preserved nothing NEW.\n */\n readonly indexRowsPreserved: number;\n}\n\n/** Local `.md` files to push. The lock file is not memory and never ships. */\nfunction listPushableFiles(memoryDir: string): string[] {\n return readdirSync(memoryDir).filter((f) => f.endsWith('.md') && f !== MEMORY_SYNC_LOCK_FILE);\n}\n\ninterface PushItem {\n readonly fileName: string;\n readonly entryType: 'index' | 'topic';\n readonly content: string;\n readonly payloadHash: string;\n readonly memoryId: string | null;\n readonly rowsPreserved: number;\n}\n\nasync function uploadOne(\n item: PushItem,\n controlPlaneUrl: string,\n token: string,\n sessionId: string,\n fetchFn: FetchLike,\n deadline: SyncDeadline,\n): Promise<'created' | 'updated'> {\n // Cooperative: an already-over-budget push stops ISSUING work rather than\n // discovering the overrun after another 20 round trips.\n deadline.check();\n\n if (item.memoryId !== null) {\n const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${item.memoryId}`;\n const updateBody: UpdateMemoryBody = { content: item.content, session_id: sessionId };\n const updateResponse = await withRequestTimeout(updateUrl, () =>\n fetchFn(updateUrl, {\n method: 'PUT',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\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/${item.memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`,\n );\n }\n const updateData = JSON.parse(await updateResponse.text()) as UpdateMemoryResponse;\n if (!updateData.ok) throw new Error(`PUT /api/v1/agent-config/memory/${item.memoryId} returned ok=false`);\n return 'updated';\n }\n\n const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\n const createBody: CreateMemoryBody = {\n entry_type: item.entryType,\n file_name: item.fileName,\n content: item.content,\n session_id: sessionId,\n };\n const createResponse = await withRequestTimeout(createUrl, () =>\n fetchFn(createUrl, {\n method: 'POST',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\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 const createData = JSON.parse(await createResponse.text()) as CreateMemoryResponse;\n if (!createData.ok) throw new Error('POST /api/v1/agent-config/memory/me returned ok=false');\n return 'created';\n}\n\n/**\n * Push local memory files to cloud.\n *\n * `MEMORY.md` is merged with the cloud copy first \u2014 see `memory-index-merge.ts`\n * for why a straight PUT loses every other session's index rows.\n */\nexport async function pushMemory(\n controlPlaneUrl: string,\n token: string,\n memoryDir: string,\n sessionId: string,\n fetchFn: FetchLike,\n options: PushOptions = {},\n): Promise<PushMemoryResult> {\n const empty: PushMemoryResult = { pushed: 0, created: 0, updated: 0, skipped: 0, indexRowsPreserved: 0 };\n if (!existsSync(memoryDir)) {\n return empty;\n }\n\n const localFiles = listPushableFiles(memoryDir).map((f) => ({\n file_name: f,\n content: readFileSync(resolveMemoryFilePath(memoryDir, f), 'utf8'),\n entry_type: (isMemoryIndexFile(f) ? 'index' : 'topic') as 'index' | 'topic',\n }));\n\n if (localFiles.length === 0) {\n return empty;\n }\n\n const deadline = options.deadline ?? createSyncDeadline();\n const cache = options.cache ?? readPushCache(memoryDir, controlPlaneUrl);\n\n // GET existing entries to map file_name \u2192 { memory_id, content }. The CONTENT\n // is what makes the index union possible: it is the other sessions' rows.\n deadline.check();\n const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\n const getResponse = await withRequestTimeout(getUrl, () =>\n fetchFn(getUrl, { method: 'GET', headers: { authorization: `Bearer ${token}` } }),\n );\n\n const existingMap = new Map<string, { memoryId: string; content: 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, {\n memoryId: entry.memory_id,\n content: typeof entry.content === 'string' ? entry.content : '',\n });\n }\n }\n }\n\n // Decide, per file, whether the bytes we would send differ from the bytes we\n // last sent. Hashing the MERGED payload (not the local file) is what makes the\n // index participate correctly \u2014 see memory-push-cache.ts.\n const toUpload: PushItem[] = [];\n let skipped = 0;\n for (const localFile of localFiles) {\n const existing = existingMap.get(localFile.file_name);\n let content = localFile.content;\n let rowsPreserved = 0;\n if (localFile.entry_type === 'index') {\n const merged = mergeMemoryIndex(localFile.content, existing?.content);\n content = merged.content;\n rowsPreserved = merged.addedFromCloud.length;\n }\n const payloadHash = sha256(content);\n if (!needsMemoryPush(cache, localFile.file_name, payloadHash, existing !== undefined)) {\n skipped++;\n continue;\n }\n toUpload.push({\n fileName: localFile.file_name,\n entryType: localFile.entry_type,\n content,\n payloadHash,\n memoryId: existing?.memoryId ?? null,\n rowsPreserved,\n });\n }\n\n const outcomes = await mapWithConcurrency(toUpload, options.concurrency ?? PUSH_CONCURRENCY, (item) =>\n uploadOne(item, controlPlaneUrl, token, sessionId, fetchFn, deadline),\n );\n\n let created = 0;\n let updated = 0;\n let indexRowsPreserved = 0;\n let firstError: unknown;\n for (let i = 0; i < outcomes.length; i++) {\n const outcome = outcomes[i] as (typeof outcomes)[number];\n const item = toUpload[i] as PushItem;\n if (outcome.ok) {\n if (outcome.value === 'created') created++;\n else updated++;\n indexRowsPreserved += item.rowsPreserved;\n // Recorded ONLY on success, so a failed upload is retried next run.\n recordMemoryPush(cache, item.fileName, item.payloadHash);\n } else if (firstError === undefined) {\n firstError = outcome.error;\n }\n }\n\n pruneMissing(cache, localFiles.map((f) => f.file_name));\n if (options.persistCache ?? options.cache === undefined) writePushCache(memoryDir, cache);\n\n // Every started upload has settled before we surface the failure \u2014 a partial\n // push whose successes were never recorded would re-send them forever.\n if (firstError !== undefined) throw firstError;\n\n return { pushed: created + updated, created, updated, skipped, indexRowsPreserved };\n}\n", "/**\n * The operator's kill switch for the DETACHED memory push.\n *\n * ## Why this exists\n *\n * Two Stop hooks push the operator's memory directory to the cloud, both\n * detached, both unref'd, both with stdio ignored, on EVERY session Stop across\n * up to seven parallel sessions:\n *\n * A) `~/.claude/hooks/memory-corpus-sync.mjs` (machine-installed, wired at\n * `~/.claude/settings.json`) \u2192 spawns `packages/vo-mcp/dist/cli.js sync push`\n * B) `vo-claude-plugin/hooks/write-back-cloud-memory.mjs` \u2192 `startMemorySync()`\n * in `vo-claude-plugin/lib/vo-mcp-launch.mjs` \u2192 the same `vo-mcp sync push`\n *\n * Neither read ANY environment variable, so an operator watching a misbehaving\n * background push had no lever short of editing hook source or uninstalling.\n * The anti-swarm doctrine requires a kill switch on any unattended spawn path.\n *\n * ## The two mechanisms \u2014 EITHER one alone disables\n *\n * 1. ENV VAR `VO_MCP_DISABLE_MEMORY_SYNC` \u2014 for anything the operator launches\n * after setting it.\n * 2. SENTINEL FILE `~/.claude/vo-memory-sync-disabled` \u2014 because an env var\n * cannot reach shells that are ALREADY RUNNING, which is exactly the\n * situation an operator is in when they want the thing off NOW. A single\n * `New-Item ~/.claude/vo-memory-sync-disabled` stops the next Stop-hook push\n * in every already-open session. Delete the file to re-enable.\n *\n * They are ORed, never ANDed: an explicit `=0` on one mechanism does NOT veto the\n * other. A veto would mean an operator who once exported `...=0` could create the\n * sentinel and watch it be ignored.\n *\n * ## Value handling is DELIBERATELY forgiving (and deliberately unlike isTruthyFlag)\n *\n * `isTruthyFlag()` in `../../consensus/engine-options.ts` is an ALLOW-LIST\n * (`1|true|yes|on`). That is right for an opt-IN: an unrecognised value falls back\n * to the safe default of \"feature off\". For an OFF-switch the same shape is a\n * trap \u2014 an operator typing `DISABLE=please` or `DISABLE=y` under pressure would\n * get a push that keeps running. So this is a DENY-LIST: any non-empty value\n * disables EXCEPT an explicit `0` / `false` / `no`.\n *\n * `off` is NOT in the negation list. In a variable named `..._DISABLE` the string\n * `off` is genuinely ambiguous (\"disabling is off\" vs \"sync off\"), and an\n * ambiguous off-switch must break toward honouring the operator, not ignoring it.\n *\n * ## Blast radius \u2014 deliberately the WHOLE of memory sync, not just the push\n *\n * The gate lives at the top of `runMemorySync`, so it also covers:\n * - `sync pull` (the SessionStart hook `fetch-cloud-memory.mjs`), which writes\n * cloud state OVER local memory files. An off switch that stopped only the\n * upload would leave the more destructive half running.\n * - the `vo_sync_config` MCP tool, so an agent cannot drive a cloud write\n * through a hole in the operator's switch.\n * Consequence, stated so it is not a surprise: with the switch thrown, tests that\n * drive a real `runMemorySync` report `DISABLED by \u2026` rather than syncing. That\n * message says exactly what happened.\n *\n * ## What DISABLED looks like (this is the point of the whole slice)\n *\n * Disabled is VISIBLE, never silent: no spawn, no network, no lock acquired, and\n * one line naming the mechanism appended to `~/.claude/vo-mcp-sync.log`. An\n * operator must be able to tell \"I turned it off\" from \"it silently broke\".\n *\n * A plugin-side twin of this module lives at\n * `vo-claude-plugin/lib/memory-sync-kill-switch.mjs` (the plugin ships as plain\n * scripts and cannot import from the published vo-mcp tarball). The two are held\n * in lockstep BEHAVIOURALLY by\n * `packages/vo-mcp/test/memory/kill-switch-twin-lockstep.test.ts`, which drives\n * both over one shared value table.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** Env var that disables the memory push. Matches the `VO_MCP_DISABLE_*` shape of `VO_MCP_DISABLE_KEYCHAIN`. */\nexport const MEMORY_SYNC_DISABLE_ENV = 'VO_MCP_DISABLE_MEMORY_SYNC';\n\n/** Sentinel filename under `~/.claude`. Its EXISTENCE is the signal; see `isKillSwitchValueOn` for contents. */\nexport const MEMORY_SYNC_DISABLE_SENTINEL = 'vo-memory-sync-disabled';\n\n/** Values that mean \"do NOT disable\". Everything else non-empty disables. */\nconst NEGATIONS = new Set(['0', 'false', 'no']);\n\n/** Longest operator value echoed into the log line (a flag, not a payload). */\nconst MAX_LOGGED_VALUE = 32;\n\nexport interface MemorySyncKillSwitch {\n /** True when the memory push must not run. */\n readonly disabled: boolean;\n /**\n * One-line, log-ready explanation naming the mechanism(s) that fired, or null\n * when nothing did. Always starts `memory sync DISABLED by ` so it is greppable\n * and unmistakably distinct from a failure line.\n */\n readonly reason: string | null;\n}\n\n/** Absolute path of the sentinel file for a given home directory. */\nexport function memorySyncSentinelPath(home: string): string {\n return join(home, '.claude', MEMORY_SYNC_DISABLE_SENTINEL);\n}\n\n/**\n * Deny-list truthiness for an off-switch value.\n *\n * `undefined` / `null` / empty / whitespace-only \u2192 NOT on (an unset or\n * shell-blanked variable must not disable anything). Any other value is on\n * unless it trims-and-lowercases to `0`, `false`, or `no`.\n */\nexport function isKillSwitchValueOn(raw: string | undefined | null): boolean {\n if (raw === undefined || raw === null) return false;\n const v = raw.trim().toLowerCase();\n if (v === '') return false;\n return !NEGATIONS.has(v);\n}\n\nfunction clip(raw: string): string {\n const v = raw.trim();\n return v.length > MAX_LOGGED_VALUE ? `${v.slice(0, MAX_LOGGED_VALUE)}\u2026` : v;\n}\n\n/**\n * Decide whether the memory push is disabled, and by what.\n *\n * Reads at most one env var and one small file \u2014 no network, no spawn, no lock.\n * Callers MUST invoke this before acquiring the memory-sync lock, so that turning\n * the switch on can never leave a stale lock behind to block a later enabled run.\n *\n * Never throws: an unreadable-but-present sentinel counts as DISABLED (the\n * operator created a file; a permission error is not a reason to overrule them).\n */\nexport function evaluateMemorySyncKillSwitch(\n deps: {\n readonly env?: Readonly<Record<string, string | undefined>>;\n readonly home?: string;\n readonly fileExists?: (p: string) => boolean;\n readonly readFile?: (p: string) => string;\n } = {},\n): MemorySyncKillSwitch {\n const env = deps.env ?? process.env;\n const home = deps.home ?? homedir();\n const fileExists = deps.fileExists ?? existsSync;\n const readFile = deps.readFile ?? ((p: string) => readFileSync(p, 'utf8'));\n\n const fired: string[] = [];\n\n const rawEnv = env[MEMORY_SYNC_DISABLE_ENV];\n if (isKillSwitchValueOn(rawEnv)) {\n fired.push(`env ${MEMORY_SYNC_DISABLE_ENV}=${clip(rawEnv as string)}`);\n }\n\n // Evaluated INDEPENDENTLY of the env var \u2014 either mechanism alone must disable.\n const sentinel = memorySyncSentinelPath(home);\n let sentinelPresent: boolean;\n try {\n sentinelPresent = fileExists(sentinel);\n } catch {\n // A stat that throws is not an operator decision \u2014 leave the push enabled.\n sentinelPresent = false;\n }\n if (sentinelPresent) {\n let contents = '';\n let readable = true;\n try {\n contents = readFile(sentinel);\n } catch {\n readable = false;\n }\n // An empty file is the `touch` case and MUST disable; only explicit negation\n // in the contents spares it. Unreadable \u2192 honour the file's existence.\n if (!readable || isKillSwitchValueOn(contents) || contents.trim() === '') {\n fired.push(`sentinel file ${sentinel}`);\n }\n }\n\n if (fired.length === 0) return { disabled: false, reason: null };\n return { disabled: true, reason: `memory sync DISABLED by ${fired.join(' + ')}` };\n}\n", "/**\n * memory-knowledge-bridge \u2014 feeds operator memory files into the private\n * knowledge corpus so `vo_private_knowledge_context` recall works across\n * Claude/Codex/Cursor/cowork clients.\n *\n * Why this exists (live-fire finding, 2026-07-24): the recall pipe was fully\n * built \u2014 tools, auth, vendor wiring \u2014 but the corpus behind it was EMPTY.\n * `vo-mcp sync push` backed memory files up to AgentConfigMemory (raw file\n * backup for restore), while `vo_private_knowledge_context` searches private\n * knowledge entries that only `vo_private_knowledge_upsert` writes \u2014 and\n * nothing ever upserted. Cross-vendor recall was a working pipe to an empty\n * reservoir. This bridge closes the loop: every memory push also upserts each\n * file as a knowledge entry (class 'memory', idempotent on source_path).\n *\n * Fail-soft contract: knowledge upserts must NEVER fail the memory backup \u2014\n * per-file failures are counted and reported, not thrown.\n *\n * ## Incremental and bounded, for the same reason the push is (2026-08-02)\n *\n * This loop was the OTHER half of the ~140 serial round trips that made a sync\n * impossible to finish inside any hook budget: one POST per topic file, every\n * run, unconditionally, serially, with no per-request timeout. It now shares the\n * push's state cache (skip a file whose content is byte-identical to the last\n * successful upsert) and its bounded fan-out. Steady state is ZERO requests.\n */\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport type { FetchLike } from './sync-config.js';\nimport { resolveMemoryFilePath } from './safe-memory-file.js';\nimport { mapWithConcurrency, PUSH_CONCURRENCY, withRequestTimeout, type SyncDeadline } from './bounded-sync.js';\nimport {\n knowledgeSweepDue,\n needsKnowledgePush,\n recordKnowledgePush,\n sha256,\n type PushCache,\n} from './memory-push-cache.js';\n\nexport interface BridgeResult {\n readonly attempted: number;\n readonly upserted: number;\n readonly failed: number;\n /** Files whose content was byte-identical to the last successful upsert. */\n readonly skipped: number;\n /** First few failure reasons, for the sync result payload (bounded). */\n readonly failures: readonly string[];\n}\n\n/** Hard schema limit server-side (private-knowledge-v1 content max); anything\n * larger is a guaranteed 400, so skip it with a named failure instead. */\nconst CONTENT_HARD_LIMIT = 500_000;\n\n/**\n * Retrieval-friendly title for a memory file: frontmatter `description:` \u2192\n * first `# ` heading \u2192 the file name. Retrieval matches on titles, so the\n * one-line description memories carry is exactly the right surface. The\n * description lookup is anchored INSIDE the leading frontmatter fence \u2014 a body\n * line starting \"description:\" must not outrank a real heading (red-team F5).\n */\nexport function extractMemoryTitle(fileName: string, content: string): string {\n const frontmatter = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/);\n if (frontmatter) {\n const description = frontmatter[1].match(/^description:\\s*(.+)$/m);\n if (description && description[1].trim()) return description[1].trim().slice(0, 200);\n }\n const heading = content.match(/^#\\s+(.+)$/m);\n if (heading && heading[1].trim()) return heading[1].trim().slice(0, 200);\n return fileName;\n}\n\n/**\n * Upsert every local memory .md file as a private-knowledge entry.\n * `source_path` is `memory/<file_name>` \u2014 the schema's idempotent upsert key \u2014\n * so repeated pushes refresh rather than duplicate.\n */\nexport async function upsertMemoryFilesAsKnowledge(options: {\n controlPlaneUrl: string;\n token: string;\n memoryDir: string;\n fetchFn: FetchLike;\n /** Shared with `pushMemory`; omit to upsert every file (no incrementality). */\n cache?: PushCache | undefined;\n deadline?: SyncDeadline | undefined;\n concurrency?: number | undefined;\n}): Promise<BridgeResult> {\n const { controlPlaneUrl, token, memoryDir, fetchFn, cache, deadline } = options;\n let files: string[];\n try {\n if (!existsSync(memoryDir)) {\n return { attempted: 0, upserted: 0, failed: 0, skipped: 0, failures: [] };\n }\n // MEMORY.md excluded: the index duplicates every topic's keywords, so it\n // out-scores real entries on nearly any query and burns a retrieval slot\n // on a near-random snippet (red-team F5). Topics carry the knowledge.\n files = readdirSync(memoryDir).filter(\n (f) => f.endsWith('.md') && f.toUpperCase() !== 'MEMORY.MD',\n );\n } catch (err) {\n // Fail-soft even on directory-level errors (red-team F2): the memory\n // BACKUP already succeeded when this runs \u2014 never let the bridge convert\n // that success into a reported sync failure.\n return {\n attempted: 0, upserted: 0, failed: 1, skipped: 0,\n failures: [`memory dir scan: ${err instanceof Error ? err.message : String(err)}`],\n };\n }\n\n // Read + hash first so the skip decision costs no network at all.\n //\n // `sweepDue` is the fail-closed half: this route only ever POSTs, so unlike\n // `needsMemoryPush` there is no server-side evidence to pair the hash with.\n // Once a day we re-upsert everything, which bounds how long a wiped corpus can\n // stay wiped. See KNOWLEDGE_FULL_SWEEP_MS.\n const sweepDue = cache ? knowledgeSweepDue(cache) : true;\n const candidates: Array<{ fileName: string; content: string; hash: string }> = [];\n const failures: string[] = [];\n let skipped = 0;\n for (const fileName of files) {\n try {\n const content = readFileSync(resolveMemoryFilePath(memoryDir, fileName), 'utf8');\n if (content.length > CONTENT_HARD_LIMIT) {\n failures.push(`${fileName}: ${content.length} chars exceeds the ${CONTENT_HARD_LIMIT} server limit \u2014 split the memory file`);\n continue;\n }\n const hash = sha256(content);\n if (cache && !needsKnowledgePush(cache, fileName, hash, sweepDue)) {\n skipped += 1;\n continue;\n }\n candidates.push({ fileName, content, hash });\n } catch (err) {\n failures.push(`${fileName}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n const url = `${controlPlaneUrl}/api/v1/knowledge/private`;\n const outcomes = await mapWithConcurrency(candidates, options.concurrency ?? PUSH_CONCURRENCY, async (candidate) => {\n deadline?.check();\n const base = {\n knowledge_class: 'memory',\n source_path: `memory/${candidate.fileName}`,\n title: extractMemoryTitle(candidate.fileName, candidate.content),\n content: candidate.content,\n };\n const post = (body: unknown) => withRequestTimeout(url, () => fetchFn(url, {\n method: 'POST',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n }));\n let response = await post({\n ...base,\n provenance: { written_by: 'memory-bridge', source_kind: 'operator_memory' },\n });\n if (response.status === 400) {\n // Pre-provenance control-plane (strict schema rejects the new key).\n // Retry without it so client/server deploy order can't strand the\n // corpus; the next server deploy starts recording provenance.\n response = await post(base);\n }\n if (response.status >= 200 && response.status < 300) return true;\n const text = await response.text();\n throw new Error(`HTTP ${response.status} ${text.slice(0, 80)}`);\n });\n\n let upserted = 0;\n for (let i = 0; i < outcomes.length; i++) {\n const outcome = outcomes[i] as (typeof outcomes)[number];\n const candidate = candidates[i] as { fileName: string; hash: string };\n if (outcome.ok) {\n upserted += 1;\n // Only a SUCCESSFUL upsert is remembered, so a 403 retries next run\n // instead of being skipped forever with an empty corpus behind it.\n if (cache) recordKnowledgePush(cache, candidate.fileName, candidate.hash);\n } else {\n const error = outcome.error;\n failures.push(`${candidate.fileName}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // The sweep stamp advances ONLY when a due sweep actually covered every file\n // without a single failure. A sweep that 403'd half the corpus must not mark\n // the corpus as freshly swept \u2014 that would be the fail-open version of this\n // whole guard.\n if (cache && sweepDue && failures.length === 0) {\n cache.knowledgeSweptAtMs = Date.now();\n }\n\n return {\n // Every memory file this run considered. `attempted === upserted + skipped\n // + failed` holds, so a caller can tell \"nothing to do\" from \"nothing ran\".\n attempted: files.length,\n upserted,\n failed: failures.length,\n skipped,\n failures: failures.slice(0, 5),\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 *\n * The round trips live in `memory-sync-http.ts`. Both actions run under the\n * memory-sync LOCK (`sync-lock.ts`): up to seven sessions share this one\n * directory, so an unserialized pull can rewrite the files a concurrent push is\n * midway through reading. Failing to take the lock FAILS the sync \u2014 it never\n * degrades to an unlocked one.\n */\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ToolDeps } from '../../types.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\nimport { pullMemory, pushMemory, type FetchLike } from './memory-sync-http.js';\nimport { createSyncDeadline } from './bounded-sync.js';\nimport { readPushCache, writePushCache } from './memory-push-cache.js';\nimport { withMemorySyncLock, type MemorySyncLockOptions } from './sync-lock.js';\nimport { evaluateMemorySyncKillSwitch } from './sync-kill-switch.js';\n\nexport const TOOL_NAME = 'vo_sync_config';\n\n/** Re-exported so `memory-knowledge-bridge.ts` / `private-knowledge.ts` keep their import path. */\nexport type { FetchLike } from './memory-sync-http.js';\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. Serialized across concurrent sessions by an exclusive lock in the memory dir.';\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 // MUST mirror Claude Code's own project-dir slug: it maps a cwd to\n // ~/.claude/projects/<slug>/ by replacing EVERY non-alphanumeric char with a\n // dash, preserving case, with NO trailing-separator strip and NO collapsing\n // of consecutive separators. Verified against real on-disk project dirs\n // (2026-07-24): 'D:\\\\' -> 'D--', 'D:\\\\nexus-backup\\\\Nexus' ->\n // 'D--nexus-backup-Nexus', and 'C:\\\\Users\\\\greyl\\\\OneDrive\\\\Documents\\\\New\n // project\\\\Nexus' -> 'C--Users-greyl-OneDrive-Documents-New-project-Nexus'\n // (note the space becomes a dash too).\n //\n // The earlier trailing-separator strip diverged from Claude Code for the ONE\n // real cwd that ends in a separator \u2014 a drive root. `process.cwd()` returns\n // 'D:\\\\' for a drive-root session, which Claude Code slugs 'D--' but the\n // stripped form produced 'D-', so memory-sync silently read/wrote an EMPTY\n // 'D-/memory' instead of the operator's real 'D--/memory'. Removing the strip\n // realigns them; deep-path cwds (no trailing separator) are unaffected.\n //\n // Non-root trailing separators are the inverse hazard: `process.cwd()` never\n // produces 'D:\\\\foo\\\\', but a hand-passed --cwd / MCP cwd can, and Claude Code\n // itself would only ever see the canonical 'D:\\foo' \u2014 so the trailing form\n // must slug identically ('D--foo', not 'D--foo-'). Strip separators ONLY when\n // they don't terminate a bare root ('D:\\\\', '/'), keeping the fix above.\n return cwd\n .replace(/([^:\\\\/])[\\\\/]+$/, '$1') // canonicalize non-root trailing separators\n .replace(/\\\\/g, '/') // normalize Windows separators (redundant with the final map, kept for the drive regex)\n .replace(/^([a-zA-Z]):/, (_m, drive: string) => `${drive.toUpperCase()}:`) // Windows cwds arrive upper-cased; match Claude's dir\n .replace(/[^a-zA-Z0-9]/g, '-'); // every 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\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 /** Push only: files whose bytes were already uploaded \u2014 the incremental win. */\n readonly skipped?: number;\n readonly memory_dir?: string;\n /** Private-knowledge bridge (push only, additive-optional \u2014 schema stays v1):\n * how many memory files were upserted into the recall corpus. */\n readonly knowledge_upserted?: number;\n readonly knowledge_failed?: number;\n readonly knowledge_skipped?: number;\n readonly knowledge_failures?: readonly string[];\n /** Push only: cloud-only MEMORY.md rows this push preserved instead of overwriting. */\n readonly index_rows_preserved?: number;\n /** Set when this run reclaimed an abandoned lock \u2014 names the dead holder's pid. */\n readonly lock_taken_over_from_pid?: number;\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.\n *\n * A lock failure is deliberately NOT in this set: \"another session holds the\n * memory lock\" is a real, retryable failure and must surface as one.\n *\n * `DISABLED by` IS in the set: an operator throwing the kill switch is a\n * deliberate no-op, not an error, so the CLI exits 0 and the durable log records\n * it as `skip \u2026 memory sync DISABLED by <mechanism>` rather than `FAIL`. That\n * wording is the whole point \u2014 the operator must be able to read \"I turned it\n * off\" and never mistake it for \"it silently broke\". */\nexport function isNoopSyncReason(reason: string | undefined): boolean {\n if (!reason) return false;\n return /not set|No auth configured|Failed to obtain auth token|DISABLED by/.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/` UNDER THE MEMORY-SYNC LOCK.\n * Shared by the `vo_sync_config` MCP tool AND the `vo-mcp sync` CLI used by the\n * enforcement hooks. Fail-soft: returns `{ synced:false, reason }` when cloud\n * mode is off or auth is missing rather than throwing, so callers can no-op\n * cleanly.\n *\n * `lockOptions` overrides the lock's timing for tests; production passes none\n * and gets the documented defaults.\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 lockOptions: Omit<MemorySyncLockOptions, 'memoryDir' | 'sessionId'> = {},\n): Promise<MemorySyncResult> {\n // OPERATOR KILL SWITCH \u2014 first statement in the function, deliberately.\n // This is the shared entrypoint BOTH detached Stop-hook push paths reach, so\n // it is the one place that can stop a push that is already spawned. It runs\n // BEFORE the auth token fetch (a network call) and BEFORE withMemorySyncLock,\n // so a disabled run makes no request and \u2014 critically \u2014 never acquires the\n // lock it would then have to release. Turning the switch on can therefore\n // never strand a lock that blocks a later enabled run.\n const killSwitch = evaluateMemorySyncKillSwitch();\n if (killSwitch.disabled) {\n return { synced: false, reason: killSwitch.reason as string };\n }\n\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\n // A push over a slug with no memory directory has nothing to read and nothing\n // to serialize. Returning here \u2014 rather than inside pushMemory, after the lock\n // has already run mkdirSync \u2014 is what stops memory sync creating an empty\n // `memory/` in EVERY project dir on the machine (PR #9327 regression: the lock\n // moved directory creation ahead of pushMemory's not-exists early return).\n if (action === 'push' && !existsSync(memoryDir)) {\n return {\n synced: true, action: 'push', pushed: 0, created: 0, updated: 0, skipped: 0,\n index_rows_preserved: 0, knowledge_upserted: 0, knowledge_failed: 0, memory_dir: memoryDir,\n };\n }\n\n try {\n // `createDir` ONLY for pull, which is the action that writes files into it.\n return await withMemorySyncLock({ ...lockOptions, memoryDir, sessionId, createDir: action === 'pull' }, async (lock) => {\n const takeover = lock.tookOverFrom ? { lock_taken_over_from_pid: lock.tookOverFrom.pid } : {};\n if (action === 'pull') {\n const result = await pullMemory(baseUrl, token, memoryDir, fetchFn);\n return {\n synced: true,\n action: 'pull',\n pulled: result.pulled,\n files: result.files,\n memory_dir: memoryDir,\n ...takeover,\n };\n }\n // ONE cache object for both halves of the push, persisted ONCE at the end\n // so the bridge's knowledgeHash rows survive alongside the memoryHash rows.\n const deadline = createSyncDeadline();\n const cache = readPushCache(memoryDir, baseUrl);\n let result;\n try {\n result = await pushMemory(baseUrl, token, memoryDir, sessionId, fetchFn, { cache, deadline });\n } catch (err) {\n // A partial push still uploaded real files. Persisting what LANDED is\n // what makes the retry cheap \u2014 otherwise one 500 costs a full 140-trip\n // re-push on the next run, which is how the old behaviour never\n // converged. Only successes were recorded, so the failure retries.\n writePushCache(memoryDir, cache);\n throw err;\n }\n // Bridge: also upsert each memory file into the private-knowledge corpus so\n // vo_private_knowledge_context recall works cross-vendor. Fail-soft by\n // contract \u2014 the backup above is the critical path, so bridge failures are\n // reported in the result, never thrown (live-fire 2026-07-24: the recall\n // corpus was empty because nothing fed it). The import itself is inside the\n // guard: a mispackaged bundle failing to resolve the bridge module must\n // degrade to a reported bridge failure, not convert the completed backup\n // into synced:false (red-team F2; beta.18 bundling incident class).\n let bridge: { upserted: number; failed: number; skipped: number; failures: readonly string[] };\n try {\n const { upsertMemoryFilesAsKnowledge } = await import('./memory-knowledge-bridge.js');\n bridge = await upsertMemoryFilesAsKnowledge({\n controlPlaneUrl: baseUrl, token, memoryDir, fetchFn, cache, deadline,\n });\n } catch (err) {\n bridge = {\n upserted: 0, failed: 1, skipped: 0,\n failures: [`bridge unavailable: ${err instanceof Error ? err.message : String(err)}`],\n };\n }\n // Persist AFTER both halves. Best-effort by contract (see the module) \u2014\n // a state file we cannot write costs the next run a full re-push, never\n // this run's result.\n writePushCache(memoryDir, cache);\n return {\n synced: true,\n action: 'push',\n pushed: result.pushed,\n created: result.created,\n updated: result.updated,\n skipped: result.skipped,\n index_rows_preserved: result.indexRowsPreserved,\n memory_dir: memoryDir,\n knowledge_upserted: bridge.upserted,\n knowledge_failed: bridge.failed,\n knowledge_skipped: bridge.skipped,\n ...(bridge.failed > 0 ? { knowledge_failures: bridge.failures } : {}),\n ...takeover,\n };\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", "#!/usr/bin/env node\n/**\n * vo-mcp stdio entrypoint.\n *\n * Usage:\n * node dist/cli.js # starts on stdio with default paths\n * VO_MCP_EVENTS_PATH=... node dist/cli.js\n * VO_MCP_DB_PATH=... node dist/cli.js\n *\n * Defaults:\n * events file \u2192 $VO_MCP_EVENTS_PATH or ~/.claude/vo-mcp-events.jsonl\n * cache db \u2192 $VO_MCP_DB_PATH or ~/.claude/vo-mcp-cache.db\n *\n * The MCP protocol speaks JSON-RPC on stdio. Nothing else may write to\n * stdout \u2014 keep all diagnostics on stderr. We use console.error for\n * any boot messages.\n */\nimport { homedir, hostname } from 'node:os';\nimport { randomUUID } from 'node:crypto';\nimport { join } from 'node:path';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createServer } from './server.js';\nimport { createSqliteCache } from './cache/sqlite-cache.js';\nimport { buildAdminCallableClientFromEnv } from './cloud/admin-callable-client.js';\nimport { createFileEventsWriter, defaultEventsPath } from './logging/events-writer.js';\nimport { createStubRatchetClient } from './ratchets/stub-client.js';\nimport { probeProviders, tryCreateEngineConsensusClientFromEnv } from './consensus/engine-client.js';\nimport { tryCreateMoatConsensusClientFromEnv } from './consensus/moat-client.js';\nimport { createConsensusFallbackClient } from './consensus/fallback-client.js';\nimport { withLocalConsensusCredentials } from './consensus/local-credential-env.js';\nimport { runLogin } from './cloud/login.js';\nimport { exchangeForVoCredential } from './cloud/vo-credential-exchange.js';\nimport { VO_MCP_VERSION } from './tools/common.js';\n\nfunction defaultCacheDbPath(): string {\n const env = process.env['VO_MCP_DB_PATH'];\n if (env && env.length > 0) return env;\n return join(homedir(), '.claude', 'vo-mcp-cache.db');\n}\n\n/**\n * Probe the installed `@algosuite/consensus-engine` package's semver from\n * its package.json. Returns null when the engine package isn't installed\n * (the production state today \u2014 the null-client takes over in that case).\n *\n * The version becomes part of the cache-key namespace (audit BLOCKER #3):\n * upgrading the engine package flips the namespace and invalidates the\n * cache, so cached verdicts from the previous engine version are not\n * silently returned by newer logic.\n */\nasync function probeEngineVersion(): Promise<string | null> {\n try {\n // Split string keeps the import opaque to bundle-analyzers \u2014 same\n // trick as engine-client.ts uses for the runtime engine import.\n const moduleName = ['@algosuite', 'consensus-engine'].join('/');\n const mod = (await import(`${moduleName}/package.json`, { with: { type: 'json' } })) as {\n default?: { version?: string };\n };\n const v = mod.default?.version;\n return typeof v === 'string' && v.length > 0 ? v : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Probe the vo-arch-defaults bundled corpus and compute a stable version\n * hash. Returns null when the KB package isn't installed.\n *\n * Loads the corpus synchronously inside an async wrapper. The corpus is\n * small (~30 rules at v1) so this is fast \u2014 ~10ms at startup.\n */\nasync function probeCorpusVersionHash(): Promise<string | null> {\n try {\n const moduleName = ['@algosuite', 'vo-arch-defaults'].join('/');\n const mod = (await import(moduleName)) as {\n loadBundledCorpus?: () => { rules: ReadonlyArray<{ rule_id: string; rule_version: number }> };\n computeCorpusVersionHash?: (\n rules: ReadonlyArray<{ rule_id: string; rule_version: number }>,\n ) => string;\n };\n if (typeof mod.loadBundledCorpus !== 'function') return null;\n if (typeof mod.computeCorpusVersionHash !== 'function') return null;\n const { rules } = mod.loadBundledCorpus();\n return mod.computeCorpusVersionHash(rules);\n } catch {\n return null;\n }\n}\n\n/**\n * Probe the installed `@algosuite/vo-arch-defaults` package's semver from\n * its package.json. Returns null when the KB package isn't installed.\n *\n * Added PR-Q 2026-05-25 to close a cache-correctness gap exposed by PR-P:\n * `corpusHash` (above) is computed from rule CONTENT (rule_id, rule_version).\n * It does NOT change when the EVALUATION LOGIC changes \u2014 e.g. PR-P's\n * `applies-to-files.ts` semantic flip (wholesale-drop \u2192 per-file include\n * AND not-exclude), or PR-R's `glob.ts` brace expansion. Both materially\n * change which rules apply to a given diff without touching corpus content.\n *\n * Mixing the KB package version into the cache namespace as a distinct\n * `kb_logic@<v>` component flips the namespace on every vo-arch-defaults\n * release \u2014 invalidating cached `vo_architecture_review` / `vo_check_*` /\n * `vo_verify_answer` envelopes that were produced under older evaluation\n * logic. Operators on a new vo-arch-defaults version get fresh verdicts\n * instead of silently-stale cached ones.\n */\nasync function probeArchDefaultsPkgVersion(): Promise<string | null> {\n try {\n const moduleName = ['@algosuite', 'vo-arch-defaults'].join('/');\n const mod = (await import(`${moduleName}/package.json`, { with: { type: 'json' } })) as {\n default?: { version?: string };\n };\n const v = mod.default?.version;\n return typeof v === 'string' && v.length > 0 ? v : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Assemble the cache-key version namespace. Components default to 'none'\n * when their source is unavailable. Format pinned by tests so cloud-side\n * downstream consumers can parse it if they need to.\n *\n * Format (PR-Q 2026-05-25 \u2014 4 components, was 3):\n * `vo_mcp@<v>|engine@<v|none>|corpus@<h|none>|kb_logic@<v|none>`\n *\n * Forward-compatible: parsers that `split('|')` continue to work; older\n * 3-component caches simply miss under the new 4-component key shape.\n */\nexport function buildCacheVersionNamespace(\n voMcpVersion: string,\n engineVersion: string | null,\n corpusHash: string | null,\n archDefaultsPkgVersion: string | null,\n): string {\n return (\n `vo_mcp@${voMcpVersion}` +\n `|engine@${engineVersion ?? 'none'}` +\n `|corpus@${corpusHash ?? 'none'}` +\n `|kb_logic@${archDefaultsPkgVersion ?? 'none'}`\n );\n}\n\nasync function loadTestEngineClient(modulePath: string): Promise<ReturnType<typeof tryCreateEngineConsensusClientFromEnv> | null> {\n // Phase 2 Lane C \u2014 test-only opt-in escape hatch for the stdio-spawned\n // integration test. The pointed-at module exports a default async\n // factory `createTestConsensusEngineClient(): ConsensusEngineClient`.\n // Production code paths (real credentials \u2192 real adapters) are\n // unaffected; the env var is undocumented and intended for tests only.\n try {\n const url = modulePath.startsWith('file:')\n ? modulePath\n : `file:///${modulePath.replace(/\\\\/g, '/')}`;\n const mod = (await import(url)) as { createTestConsensusEngineClient?: unknown };\n if (typeof mod.createTestConsensusEngineClient === 'function') {\n const client = (mod.createTestConsensusEngineClient as () => unknown)();\n if (client !== null && typeof client === 'object' && 'run' in client) {\n return client as ReturnType<typeof tryCreateEngineConsensusClientFromEnv>;\n }\n }\n console.error(`[vo-mcp] test-engine-module loaded but missing createTestConsensusEngineClient export: ${modulePath}`);\n return null;\n } catch (err) {\n console.error(`[vo-mcp] failed to load VO_MCP_TEST_ENGINE_MODULE=${modulePath}:`, err);\n return null;\n }\n}\n\nasync function main(): Promise<void> {\n // Probe version components for the cache-key namespace BEFORE constructing\n // the cache. All three probes are graceful: null when the package isn't\n // installed (the current production state for engine; vo-arch-defaults\n // is workspace-resident so its probes typically succeed).\n const [engineVersion, corpusHash, archDefaultsPkgVersion] = await Promise.all([\n probeEngineVersion(),\n probeCorpusVersionHash(),\n probeArchDefaultsPkgVersion(),\n ]);\n const cacheVersionNamespace = buildCacheVersionNamespace(\n VO_MCP_VERSION,\n engineVersion,\n corpusHash,\n archDefaultsPkgVersion,\n );\n console.error(`[vo-mcp] cache version namespace: ${cacheVersionNamespace}`);\n\n const cache = createSqliteCache({\n dbPath: defaultCacheDbPath(),\n cacheVersionNamespace,\n });\n const events = createFileEventsWriter();\n const ratchets = createStubRatchetClient();\n // Phase 2 Lane C \u2014 test-only escape hatch (undocumented for production).\n const testModule = process.env['VO_MCP_TEST_ENGINE_MODULE'];\n const testClient =\n testModule !== undefined && testModule.length > 0\n ? await loadTestEngineClient(testModule)\n : null;\n // Consensus client selection (Increment 2 \u2014 AlgoHQ Command Center unification \u00A75):\n // 1. an injected test client always wins;\n // 2. else LOCAL consensus uses \u22652 credentials from env / the OS keychain;\n // 3. the paid cloud moat is fallback-only when an entitlement is configured.\n // `tryCreateMoatConsensusClientFromEnv` returns null when no entitlement token\n // is configured, so existing local-key installs are unaffected (backward-compat).\n // 2026-05-24 audit MEDIUM \u2014 `tryCreateEngineConsensusClientFromEnv` ALWAYS\n // returns a non-null client; on the failure path it surfaces a specific reason\n // (`consensus-credentials-missing`, etc.), so it is the correct final fallback.\n const localEnv = withLocalConsensusCredentials();\n const localProviders = probeProviders(localEnv);\n for (const key of ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_API_KEY', 'DEEPSEEK_API_KEY', 'MODEL_API_KEY'] as const) {\n if (!process.env[key] && localEnv[key]) process.env[key] = localEnv[key];\n }\n const localConsensus = tryCreateEngineConsensusClientFromEnv({ envSource: localEnv });\n // The moat entitlement may come from the OS keychain (`vo-mcp set-key\n // --provider moat`) as well as the env \u2014 read it from the hydrated view so an\n // npm-installed runner (no local engine package) gets a working consensus path.\n const cloudConsensus = testClient ? null : tryCreateMoatConsensusClientFromEnv(localEnv);\n let consensus = testClient ?? localConsensus;\n if (!testClient && cloudConsensus && localProviders.length >= 2) {\n console.error(`[vo-mcp] local-first consensus active (${localProviders.join(', ')}); cloud moat is fallback-only`);\n consensus = createConsensusFallbackClient(localConsensus, cloudConsensus, {\n onFallback: (reason) => console.error(`[vo-mcp] local consensus unavailable (${reason}); using cloud moat fallback`),\n });\n } else if (!testClient && cloudConsensus) {\n console.error('[vo-mcp] fewer than 2 linked local providers; cloud moat consensus active');\n consensus = cloudConsensus;\n } else if (!testClient) {\n // No entitlement anywhere: on an npm install the local engine can never load, so\n // say so ONCE at boot instead of letting every tool call discover it.\n console.error('[vo-mcp] no VO_ENTITLEMENT_TOKEN (env or keychain via `vo-mcp set-key --provider moat`); consensus tools depend on a locally installed engine and will report engine-unavailable on an npm-installed runner');\n }\n\n // PR 3 \u2014 wire vo-control-plane admin proxy when env vars are set.\n // Returns null when VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN\n // are unset (local mode \u2014 the default). Throws when exactly one is set.\n let adminCallables: ReturnType<typeof buildAdminCallableClientFromEnv> = null;\n try {\n adminCallables = buildAdminCallableClientFromEnv();\n } catch (err) {\n console.error(\n `[vo-mcp] admin-callable client config error \u2014 falling back to local mode: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (adminCallables) {\n console.error('[vo-mcp] cloud mode active \u2014 admin proxies forward to vo-control-plane');\n }\n\n const server = createServer({ cache, events, ratchets, consensus, adminCallables });\n const transport = new StdioServerTransport();\n\n process.on('SIGINT', () => {\n void shutdown();\n });\n process.on('SIGTERM', () => {\n void shutdown();\n });\n\n async function shutdown(): Promise<void> {\n try {\n await server.close();\n } finally {\n cache.close();\n events.close();\n process.exit(0);\n }\n }\n\n await server.connect(transport);\n // stderr only \u2014 stdout is reserved for JSON-RPC.\n console.error(\n `[vo-mcp] connected on stdio (events=${defaultEventsPath()}, db=${defaultCacheDbPath()})`,\n );\n}\n\nif (process.argv[2] === 'login') {\n // `vo-mcp login` \u2014 interactive per-user sign-in (Inc 3b). Retires the god-token\n // from the client config in favor of the user's own auto-refreshing credential.\n // Inc 3b.4b: when a control-plane URL is configured, exchange the captured\n // Firebase refresh token for a scoped, revocable vocred_ and store THAT instead.\n const controlPlaneUrl = process.env['VO_CONTROL_PLANE_URL']?.trim();\n // A host-derived label so the operator can identify + revoke THIS machine's\n // credential in the control-plane (`vo-mcp-cli@<hostname>`).\n const credentialLabel = `vo-mcp-cli@${hostname()}`.slice(0, 200);\n runLogin(\n controlPlaneUrl\n ? {\n exchange: (refreshToken, apiKey) =>\n exchangeForVoCredential({ refreshToken, apiKey, controlPlaneUrl, label: credentialLabel }),\n }\n : {},\n )\n // NO process.exit() here or in the sync path below: a hard exit tears down\n // the environment while @napi-rs/keyring's native async handle can still be\n // live, which on Windows trips libuv's `!(handle->flags & UV_HANDLE_CLOSING)`\n // assertion in src/win/async.c and kills the process with exit code 9 AFTER\n // the success line printed (observed 5/5 in non-interactive shells,\n // 2026-07-21) \u2014 violating the fail-soft exit-0 contract. Setting exitCode\n // and letting the loop drain exits cleanly, and measured drain time after\n // the work settles is <1ms (nothing else holds the loop).\n .then((r) => {\n console.error(`[vo-mcp] login successful${r.email ? ` as ${r.email}` : ''}. Credential stored at ${r.credentialPath}.`);\n console.error('[vo-mcp] You can now remove VO_CONTROL_PLANE_ADMIN_TOKEN (the god-token) from your MCP config.');\n console.error('[vo-mcp] NOTE: per-user auth requires VO_OPERATOR_ALLOWED_EMAILS (with your email) on the deployed control-plane.');\n })\n .catch((err: unknown) => {\n console.error('[vo-mcp] login failed:', err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n });\n} else if (process.argv[2] === 'sync') {\n // `vo-mcp sync <push|pull> [--cwd <path>]` \u2014 cloud memory sync from the shell,\n // so the SessionStart/PostToolUse enforcement hooks (and any non-Claude runtime)\n // can drive the SAME push/pull logic the `vo_sync_config` MCP tool uses. The\n // operator's #1 ask: a system that MAKES SURE local memory reaches the cloud.\n //\n // Fail-soft contract (so a hook NEVER blocks a session): exit 0 when sync\n // succeeds OR when cloud mode simply isn't configured here (no control-plane\n // URL / no stored credential). Exit 1 only on a real sync error (network/HTTP).\n const action = process.argv[3];\n if (action !== 'push' && action !== 'pull') {\n console.error('[vo-mcp] usage: vo-mcp sync <push|pull> [--cwd <path>] [--lock-wait-ms <n>]');\n // Hard exit is safe ONLY here: nothing async has started and the native\n // keychain module has not loaded yet (it loads lazily on credential read).\n process.exit(2);\n }\n const cwdFlag = process.argv.indexOf('--cwd');\n const cwd =\n cwdFlag >= 0 && typeof process.argv[cwdFlag + 1] === 'string'\n ? (process.argv[cwdFlag + 1] as string)\n : process.cwd();\n // The caller's contention budget, so the lock's wait and the caller's own\n // deadline are ONE number instead of two independent guesses. The Stop hook\n // runs this detached and passes a large value; the SessionStart pull hook\n // passes one that fits inside its own ceiling. Absent/garbage \u2192 the module\n // default (fail-closed: never widened by a bad argument).\n const waitFlag = process.argv.indexOf('--lock-wait-ms');\n const parsedWait = waitFlag >= 0 ? Number(process.argv[waitFlag + 1]) : Number.NaN;\n const lockOptions = Number.isFinite(parsedWait) && parsedWait >= 0 ? { waitMs: parsedWait } : {};\n // A fresh watermark per CLI run \u2014 V1 storage is last-write-wins; the watermark\n // records WHICH session last wrote each entry for future conflict detection.\n const sessionId = randomUUID();\n // Durable sync log (red-team F3): the SessionStart/Stop hook spawns this\n // detached with stdio:'ignore', so console output is discarded \u2014 a silently\n // failing knowledge bridge (403s, route drift) would leave the recall corpus\n // empty forever with NO visible signal, the exact reservoir-empty bug the\n // bridge exists to fix. Append every outcome (incl. knowledge_upserted /\n // knowledge_failed) to a file so it's inspectable regardless of stdio.\n const appendSyncLog = async (line: string) => {\n try {\n const { appendFileSync, mkdirSync } = await import('node:fs');\n const { join } = await import('node:path');\n const { homedir } = await import('node:os');\n const dir = join(homedir(), '.claude');\n mkdirSync(dir, { recursive: true });\n appendFileSync(join(dir, 'vo-mcp-sync.log'), `${line}\\n`, 'utf8');\n } catch { /* logging must never affect the sync outcome */ }\n };\n import('./tools/memory/sync-config.js')\n .then(async ({ runMemorySync, isNoopSyncReason }) => {\n const r = await runMemorySync(action, cwd, sessionId, undefined, lockOptions);\n const stamp = `${sessionId} ${action}`;\n // exitCode + natural drain, never process.exit() \u2014 see the login path\n // above for why (Windows @napi-rs/keyring teardown race \u2192 exit code 9).\n if (r.synced) {\n console.error(`[vo-mcp] sync ${action} ok: ${JSON.stringify(r)}`);\n await appendSyncLog(`ok ${stamp} ${JSON.stringify(r)}`);\n } else if (isNoopSyncReason(r.reason)) {\n // No-op (cloud off / no credential) \u2192 exit 0 so hooks treat it as a skip.\n console.error(`[vo-mcp] sync ${action} skipped: ${r.reason}`);\n await appendSyncLog(`skip ${stamp} ${r.reason ?? ''}`);\n } else {\n console.error(`[vo-mcp] sync ${action} failed: ${r.reason}`);\n await appendSyncLog(`FAIL ${stamp} ${r.reason ?? ''}`);\n process.exitCode = 1;\n }\n })\n .catch(async (err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error('[vo-mcp] sync fatal:', message);\n await appendSyncLog(`FATAL ${sessionId} ${action} ${message}`);\n process.exitCode = 1;\n });\n} else {\n main().catch((err: unknown) => {\n console.error('[vo-mcp] fatal:', err);\n process.exit(1);\n });\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';\nimport * as privateKnowledge from './tools/memory/private-knowledge.js';\nimport * as hqWhiteboard from './tools/hq/whiteboard.js';\nimport * as skillCorpus from './tools/skills/skill-corpus.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 [privateKnowledge.UPSERT_TOOL_NAME]: {\n definition: {\n name: privateKnowledge.UPSERT_TOOL_NAME,\n description: privateKnowledge.upsertDescription,\n inputSchema: privateKnowledge.upsertInputSchema,\n },\n handler: privateKnowledge.handlePrivateKnowledgeUpsert,\n },\n [privateKnowledge.CONTEXT_TOOL_NAME]: {\n definition: {\n name: privateKnowledge.CONTEXT_TOOL_NAME,\n description: privateKnowledge.contextDescription,\n inputSchema: privateKnowledge.contextInputSchema,\n },\n handler: privateKnowledge.handlePrivateKnowledgeContext,\n },\n [privateKnowledge.INVALIDATE_TOOL_NAME]: {\n definition: {\n name: privateKnowledge.INVALIDATE_TOOL_NAME,\n description: privateKnowledge.invalidateDescription,\n inputSchema: privateKnowledge.invalidateInputSchema,\n },\n handler: privateKnowledge.handlePrivateKnowledgeInvalidate,\n },\n [privateKnowledge.STALE_TOOL_NAME]: {\n definition: {\n name: privateKnowledge.STALE_TOOL_NAME,\n description: privateKnowledge.staleDescription,\n inputSchema: privateKnowledge.staleInputSchema,\n },\n handler: privateKnowledge.handlePrivateKnowledgeStale,\n },\n [hqWhiteboard.POST_TOOL_NAME]: {\n definition: {\n name: hqWhiteboard.POST_TOOL_NAME,\n description: hqWhiteboard.postDescription,\n inputSchema: hqWhiteboard.postInputSchema,\n },\n handler: hqWhiteboard.handleHqWhiteboardPost,\n },\n [hqWhiteboard.READ_TOOL_NAME]: {\n definition: {\n name: hqWhiteboard.READ_TOOL_NAME,\n description: hqWhiteboard.readDescription,\n inputSchema: hqWhiteboard.readInputSchema,\n },\n handler: hqWhiteboard.handleHqWhiteboardRead,\n },\n [skillCorpus.LIST_TOOL_NAME]: {\n definition: {\n name: skillCorpus.LIST_TOOL_NAME,\n description: skillCorpus.listDescription,\n inputSchema: skillCorpus.listInputSchema,\n },\n handler: skillCorpus.handleSkillList,\n },\n [skillCorpus.GET_TOOL_NAME]: {\n definition: {\n name: skillCorpus.GET_TOOL_NAME,\n description: skillCorpus.getDescription,\n inputSchema: skillCorpus.getInputSchema,\n },\n handler: skillCorpus.handleSkillGet,\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 * 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 * 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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 * 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_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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 ...(engineResult.quorum_failed === true ? { quorum_failed: true } : {}),\n // The verification receipt (moat decision_id) \u2014 the thing an agent pastes as `receipt id: <uuid>`.\n ...(engineResult.receipt_id ? { receipt_id: engineResult.receipt_id } : {}),\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 * 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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 ...(engineResult.quorum_failed === true ? { quorum_failed: true } : {}),\n // The verification receipt (moat decision_id) \u2014 the thing an agent pastes as `receipt id: <uuid>`.\n // NOTE: a content-hash cache hit replays the ORIGINAL call's receipt_id (same claim, same verdict, no new spend) \u2014\n // a receipt asserts the stage ran for this claim, not one-receipt-per-call.\n ...(engineResult.receipt_id ? { receipt_id: engineResult.receipt_id } : {}),\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 // Critique-uptake (2026-07-20 red-team fix) \u2014 the engine computes this\n // on every call; this spread closes the gap where the visibility report\n // was itself silently dropped at the payload boundary.\n ...(engineResult.critique_uptake !== undefined\n ? { critique_uptake: engineResult.critique_uptake }\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 * 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_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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 * 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 * Dispatches through `buildCloudOrStubResponse` and reaches the cloud\n * callable for real when cloud mode is configured; the `unimplemented`\n * envelope is the unconfigured fallback only. A live call additionally\n * requires a FOUNDING-operator credential \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_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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope with structured normalized_input when cloud mode is not configured.\";\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 * 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`.\n *\n * The cloud wiring SHIPPED. Every tool in this family now dispatches\n * through `buildCloudOrStubResponse`, which makes a real request to the\n * control plane's `/api/v1/admin/*` surface whenever cloud mode is\n * configured on the session. The \"unimplemented\" envelope is now only the\n * UNCONFIGURED fallback \u2014 it is not the permanent state, and the tool\n * descriptions must not advertise it as one.\n *\n * Two conditions therefore govern a live call, and both are real:\n * 1. Cloud mode must be configured (`vo-mcp login`). Without it the\n * dispatcher returns the stub envelope without touching the network.\n * 2. The caller must hold a FOUNDING-operator credential. The control\n * plane's scoped-operator gate allows exactly one `/api/v1/admin/*`\n * path (GET /api/v1/admin/receipts); every other admin path returns\n * HTTP 403 `scoped_operator_forbidden` for a tenant-scoped account.\n * Proven live: vo_list_pending_prs returns that 403 from\n * /api/v1/admin/pr/list \u2014 a response a stub cannot produce.\n *\n * Keep this header and the per-tool `description` strings in agreement\n * with both conditions. An agent reads the tool list BEFORE deciding to\n * call, so a description claiming \"unimplemented\" suppresses correct\n * calls and under-reports the shipped surface.\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 is not configured on this session, so the admin callable was not reached. The wiring exists (see buildCloudOrStubResponse); sign in with `vo-mcp login` to route this tool to the control plane. Note that /api/v1/admin/* additionally requires a founding-operator credential.';\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_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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Tool: `vo_list_pending_prs`\n *\n * Wraps `voListPendingPRs()` admin callable (no parameters). Returns\n * the list of open AlgoHQ-source pull requests with blocker / source /\n * tester / specialist-context metadata.\n *\n * Founder-scoped admin call \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 AlgoHQ-source pull requests with blocker / source / tester / specialist-context metadata. Read-only diagnostic for Command Center reads. Wraps `voListPendingPRs` admin Cloud Function. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * 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 * The cloud wiring SHIPPED \u2014 these tools dispatch through\n * `buildCloudOrStubResponse` and reach `/api/v1/admin/pr/*` for real when\n * cloud mode is configured. The stub envelope is the UNCONFIGURED\n * fallback only. A live call additionally requires a FOUNDING-operator\n * credential; a tenant-scoped account receives HTTP 403\n * `scoped_operator_forbidden`. See common-heal.ts header for the full\n * rationale and the live evidence.\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 is not configured on this session, so the admin callable was not reached. The wiring exists (see buildCloudOrStubResponse); sign in with `vo-mcp login` to route this tool to the control plane. Note that /api/v1/admin/* additionally requires a founding-operator credential.';\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_merge_pr`\n *\n * Wraps `voMergePR({ prNumber })` admin callable. Approves + merges a\n * single AlgoHQ-source PR by number (refuses non-AlgoHQ PRs server-side).\n *\n * Founder-scoped admin call \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 AlgoHQ-source pull request by number. Wraps `voMergePR` admin Cloud Function (server-side refuses non-AlgoHQ PRs with permission-denied). Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 AlgoHQ-source PRs and merges/auto-merges each. Returns a summary\n * of merged / accepted / total counts plus per-PR results.\n *\n * Founder-scoped admin call \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 AlgoHQ-source pull requests and merges (or arms auto-merge) on each. Returns counts of merged / accepted / total plus per-PR results. Wraps `voApproveAllFixes` admin Cloud Function. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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 an AlgoHQ pull request and dispatches a self-heal pass to retry the same focus page. Cloud callable refuses non-AlgoHQ PRs and respects the self-heal kill switch + per-PR retry block. Wraps `voRejectAndRetry` admin Cloud Function. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 aggregateEventTokenUsage,\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 AlgoHQ-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 AlgoHQ PRs (already merged/closed, or not an AlgoHQ-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 ...aggregateEventTokenUsage(result.per_model_verdicts, result.token_usage),\n });\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 plus a user/scoped VO\n * credential (or the legacy admin token) is available, the tool forwards reports\n * to the deployed vo-control-plane HTTP API. Auto-allocates the session on first\n * report (404 from report-state \u2192 allocate \u2192 retry). Interactive agents (Claude\n * Code, Cursor, Codex, Continue) now appear on 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';\nimport { createAuthTokenSourceFromEnv } from '../../cloud/auth-token-source.js';\nimport { readStoredCredential } from '../../cloud/credential-store.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 AlgoHQ and returns a directive: 'continue' (under 70%), 'prepare_handoff' (70-84%), or 'execute_handoff_now' (\u226585%). Implements V1 launch gate #9 (fleet context lifecycle management) per the official AlgoHQ roadmap. Cloud-control-plane mode when VO_CONTROL_PLANE_URL plus a user/scoped HQ credential (or legacy admin token) is available; auto-allocates the session on first report so interactive agents (Claude Code, Cursor, Codex, Continue) appear on the live fleet whiteboard. Stub-local fallback when cloud config is absent or fails. The response shape stays stable across modes (`backend_mode` field in the payload tells the caller which mode produced the verdict).\";\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. Uses the same auth precedence as other cloud-mode tools:\n * explicit user tokens, stored scoped `vo_credential`, then legacy admin token.\n * When VO_TENANT_ID is absent, `/auth/me` supplies the operator's own tenant.\n */\ninterface CloudConfig {\n readonly url: string;\n readonly token: string;\n readonly tenant_id: string;\n readonly operator_id?: string;\n}\n\ninterface AuthMeResponse {\n readonly ok?: boolean;\n readonly provisioned?: boolean;\n readonly operator_id?: string | null;\n readonly tenant_id?: string | null;\n}\n\ntype FetchLike = typeof fetch;\n\nasync function fetchCloudIdentity(\n url: string,\n token: string,\n fetchFn: FetchLike,\n): Promise<{ operator_id: string; tenant_id: string } | null> {\n try {\n const response = await fetchFn(`${url}/api/v1/auth/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n if (!response.ok) return null;\n const data = await response.json() as AuthMeResponse;\n if (!data.ok || !data.provisioned || !data.operator_id || !data.tenant_id) return null;\n return { operator_id: data.operator_id, tenant_id: data.tenant_id };\n } catch {\n return null;\n }\n}\n\nasync function getCloudConfig(fetchFn: FetchLike = fetch): Promise<CloudConfig | null> {\n const url = process.env['VO_CONTROL_PLANE_URL']?.trim();\n if (!url) return null;\n\n const tokenSource = createAuthTokenSourceFromEnv(\n process.env,\n fetchFn,\n () => readStoredCredential(process.env),\n );\n const token = await tokenSource?.getToken();\n if (!token) return null;\n\n const tenant_id = process.env['VO_TENANT_ID']?.trim();\n if (tenant_id) return { url, token, tenant_id };\n\n const identity = await fetchCloudIdentity(url, token, fetchFn);\n if (!identity) return null;\n return { url, token, tenant_id: identity.tenant_id, operator_id: identity.operator_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\ninterface CloudAllocateSessionResponse {\n readonly session?: {\n readonly session_id?: unknown;\n };\n}\n\nasync function tryCloudReportState(\n cloud: CloudConfig,\n input: ToolInput,\n fetchFn: FetchLike = fetch,\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 fetchFn(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: cloud.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 fetchFn(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 const allocateData: CloudAllocateSessionResponse = await allocateResponse.json() as CloudAllocateSessionResponse;\n const retrySessionId = typeof allocateData.session?.session_id === 'string'\n && allocateData.session.session_id.length > 0\n ? allocateData.session.session_id\n : input.session_id;\n const retryReportUrl = `${cloud.url}/api/v1/session/${retrySessionId}/report-state`;\n\n // Retry the report-state call\n response = await fetchFn(retryReportUrl, {\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 = await 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 * 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_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';\nimport {\n admitSubagentSpawn,\n agentBindingRefusal,\n childBindingEnvFragment,\n inheritSwarmTierBinding,\n SWARM_TIER_BINDING_ENV,\n} from '../../swarm/tier-binding.js';\nimport { resolveSuccessorLaunch } from '../../swarm/successor-launch.js';\nimport { claimSpawnSlot, resolveLedgerDir, type ClaimSpawnSlot } from '../../swarm/spawn-ledger.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 agent: {\n type: 'string',\n description:\n \"Which agent to spawn ('claude' | 'codex'). Normally omitted: the agent comes from the swarm tier binding inherited via \" +\n `${SWARM_TIER_BINDING_ENV}. When a binding IS inherited this may only RESTATE the bound agent \u2014 an agent that contradicts ` +\n 'the binding is REFUSED, because a different agent is a different payer and the payer was decided once, at admission.',\n },\n },\n required: [],\n additionalProperties: false,\n};\n\n/**\n * The retired counter input. Kept as a NAME so a stale caller is told what\n * happened, never as a value the gate reads.\n *\n * `spawns_so_far` was a declared tool input: the model being bounded supplied\n * its own spend counter, and an ABSENT one read as zero. Reproduced end-to-end,\n * one process performed 25 real spawns from a budget-3 binding by omitting it.\n * Ignoring the field silently would leave a caller believing it still bounds\n * something, so supplying it is an explicit error.\n */\nexport const RETIRED_COUNTER_INPUT = 'spawns_so_far';\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 readonly agent?: 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['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 if (o['agent'] !== undefined && typeof o['agent'] !== 'string') return false;\n return true;\n}\n\n/** Reason to reject a call that still carries the retired counter, else null. */\nexport function retiredCounterRefusal(v: unknown): string | null {\n if (typeof v !== 'object' || v === null) return null;\n if (!(RETIRED_COUNTER_INPUT in (v as Record<string, unknown>))) return null;\n return (\n `\\`${RETIRED_COUNTER_INPUT}\\` is no longer accepted: a spawn counter supplied by the process being ` +\n 'bounded bounds nothing, and an absent one read as zero. The fan-out ceiling is now enforced by the ' +\n 'durable per-swarm spawn ledger; remove the field.'\n );\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 an AlgoHQ 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 AlgoHQ 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\n/**\n * Decide WHAT to spawn, honouring an inherited swarm tier binding.\n *\n * Backward compatibility is deliberate and bounded. When NO binding is present\n * in the env \u2014 which is every caller today \u2014 this returns exactly the legacy\n * claude launch, so wiring the precedence in does not take a working tool\n * offline. The new refusal and the new codex path fire ONLY when a fan-out has\n * actually done admission and exported a binding. That is the full answer to\n * \"what newly executes\".\n *\n * When a binding IS present it is authoritative in both directions: it can bind\n * the spawn to codex, and it can REFUSE the spawn outright (tier `refused` /\n * `unresolved`, or a budget already spent). A refusal here is the point \u2014 it is\n * the mechanism that stops a fan-out spilling onto a second payer halfway\n * through.\n *\n * Exported for tests.\n */\nexport function resolveSpawnPlan(\n env: Record<string, string | undefined>,\n input: { readonly agent?: string | undefined; readonly max_turns?: number | undefined },\n nowIso: string,\n platform: string = process.platform,\n claim: ClaimSpawnSlot = claimSpawnSlot,\n):\n | {\n readonly ok: true;\n readonly bin: string;\n readonly args: readonly string[];\n readonly agent: string;\n readonly tier: string;\n readonly bound: boolean;\n readonly env: Record<string, string>;\n readonly slot: number | null;\n /** The spend the ledger DEBITED for this spawn, USD. null when uncapped. */\n readonly capUsd: number | null;\n /** Pool left after that debit, USD. null when the fan-out has no pool. */\n readonly capRemainingUsd: number | null;\n }\n | { readonly ok: false; readonly reason: string; readonly tier: string } {\n // Read once into a local so the narrowing survives, rather than re-indexing and\n // asserting non-null. A `!` here tripped @typescript-eslint/no-non-null-assertion,\n // and this repo's lint floor is 0 warnings \u2014 so it turned `Main health` red on main\n // and, while red, that required gate silently evicts every open PR.\n const rawBinding = env[SWARM_TIER_BINDING_ENV];\n const hasBinding = typeof rawBinding === 'string' && rawBinding.trim().length > 0;\n\n if (!hasBinding) {\n // Legacy path, unchanged: no fan-out admission happened, so there is no\n // tier to honour and nothing to refuse on behalf of.\n const explicit = input.agent?.trim();\n if (explicit) {\n const resolved = resolveSuccessorLaunch({ agent: explicit, maxTurns: input.max_turns, platform });\n if (!resolved.ok) return { ok: false, reason: resolved.reason, tier: 'unbound' };\n return {\n ok: true,\n bin: resolved.bin,\n args: resolved.args,\n agent: resolved.agent,\n tier: 'unbound',\n bound: false,\n env: {},\n slot: null,\n capUsd: null,\n capRemainingUsd: null,\n };\n }\n return {\n ok: true,\n bin: 'claude',\n args: buildSuccessorArgs(input.max_turns),\n agent: 'claude',\n tier: 'unbound',\n bound: false,\n env: {},\n slot: null,\n capUsd: null,\n capRemainingUsd: null,\n };\n }\n\n const binding = inheritSwarmTierBinding(env, nowIso);\n // No counter argument: `admitSubagentSpawn` here asks only \"does this binding\n // still carry allowance\". The number of spawns already performed is NOT the\n // caller's to assert \u2014 it comes from the ledger, below.\n const admission = admitSubagentSpawn(binding);\n if (!admission.allowed) {\n return { ok: false, reason: admission.reason, tier: binding.tier };\n }\n // A caller-supplied `agent` may RESTATE the bound agent; it may never\n // contradict it. `agent` is model-supplied, and a different agent is a\n // different vendor's subscription or key \u2014 a different PAYER. This used to be\n // `input.agent?.trim() || binding.agent` with no check, which meant the\n // comment above it (\"can never change the tier\") was documentation the code\n // did not implement, and admission-time binding was defeatable by its own\n // caller.\n const agentRefusal = agentBindingRefusal(binding, input.agent);\n if (agentRefusal !== null) return { ok: false, reason: agentRefusal, tier: binding.tier };\n const resolved = resolveSuccessorLaunch({\n agent: binding.agent,\n maxTurns: input.max_turns,\n platform,\n });\n if (!resolved.ok) return { ok: false, reason: resolved.reason, tier: binding.tier };\n // LAST, so a spawn refused for any other reason does not consume allowance.\n // This is the counter that replaces the model-supplied one: a durable slot in\n // a per-swarm directory, shared with the plugin spawner on the same host, and\n // fail-closed if it cannot be read or written.\n const slot = claim({\n swarmId: binding.swarm_id,\n proposedCeiling: binding.subagent_budget,\n // The spend-cap POOL, recorded once per swarm exactly like the ceiling. The\n // child's cap is DEBITED from it below, not recomputed from this binding.\n proposedCapUsd: binding.spend_cap_usd,\n dir: resolveLedgerDir(env),\n nowIso,\n });\n if (!slot.ok) return { ok: false, reason: slot.reason, tier: binding.tier };\n return {\n ok: true,\n bin: resolved.bin,\n args: resolved.args,\n agent: resolved.agent,\n tier: binding.tier,\n bound: true,\n // Re-export the same TIER with a DECREMENTED budget and the spend cap the\n // ledger just DEBITED. Exporting the binding verbatim (what this did before\n // #9312) meant the child re-read the full budget and every generation\n // restarted at zero. Recomputing the cap from THIS binding (what #9312 did)\n // bounded a chain but not a tree: three siblings each re-halved the parent's\n // untouched $50 and walked away with $75 between them.\n env: childBindingEnvFragment(binding, slot.capUsd),\n slot: slot.slot,\n capUsd: slot.capUsd,\n capRemainingUsd: slot.capRemainingUsd,\n };\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 const retired = retiredCounterRefusal(rawInput);\n if (retired !== null) throw invalidParams(TOOL_NAME, retired);\n if (!isToolInput(rawInput)) {\n throw invalidParams(TOOL_NAME, 'invalid input. Optional: { handoff_path, goal, cwd, max_turns, agent }.');\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 // Honour the fan-out's ONE tier decision before spending anything. Resolved\n // BEFORE the log file is opened so a refusal leaves no stray artifacts.\n const plan = resolveSpawnPlan(process.env, rawInput, new Date().toISOString());\n if (!plan.ok) {\n return jsonContent({\n tool: TOOL_NAME,\n schema_version: 1,\n payload: {\n spawned: false,\n reason: `swarm tier binding refused this spawn: ${plan.reason}`,\n tier: plan.tier,\n handoff_path: handoffPath,\n },\n });\n }\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(plan.bin, [...plan.args], {\n cwd: rawInput.cwd?.trim() || process.cwd(),\n detached: true,\n stdio: ['pipe', logFd, logFd],\n // Windows: the agent CLIs are .cmd shims \u2014 they need a shell to resolve.\n // The prompt goes via STDIN below, never argv, so the shell never sees it.\n shell: process.platform === 'win32',\n windowsHide: true,\n // Carry the SAME binding to the child. Without this the successor inherits\n // no tier and re-resolves its own \u2014 which is the split-payer defect one\n // generation down.\n ...(plan.bound ? { env: { ...process.env, ...plan.env } } : {}),\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}`, agent: plan.agent, tier: plan.tier, handoff_path: handoffPath }\n : {\n spawned: true,\n pid: child.pid ?? null,\n log_path: logPath,\n handoff_path: handoffPath,\n agent: plan.agent,\n tier: plan.tier,\n tier_bound: plan.bound,\n ledger_slot: plan.slot,\n // The debit, surfaced so an operator can reconcile a fan-out's spend\n // against the pool without reading the ledger directory by hand.\n ledger_cap_usd: plan.capUsd,\n ledger_cap_remaining_usd: plan.capRemainingUsd,\n },\n });\n}\n", "/**\n * Swarm tier binding \u2014 ONE billing-tier decision per fan-out, taken at\n * admission, recorded, and INHERITED by every subagent in that fan-out.\n *\n * ## The defect this closes\n *\n * The /goal precedence (connected subscription CLI or local model \u2192 the user's\n * OWN API keys \u2192 the platform owner's keys, last and catalogued) is resolved\n * PER SPAWN everywhere it is resolved at all. A fan-out has no tier concept, so\n * a 20-agent swarm can legitimately half-run on the operator's subscription and\n * half bill the platform: each spawn re-asks the question, and the answer can\n * legitimately change mid-swarm (a weekly window fills, a runner drops, a key\n * rotates). That is confusing to the user and unreconcilable for billing.\n *\n * Resolve ONCE, at admission, and hand every subagent the same answer.\n *\n * ## THE HARD RULE (inherited from the durable capability record)\n *\n * ABSENT, CORRUPT, OR STALE INPUT READS AS 'unresolved'. NEVER 'subscription'.\n *\n * `resolveOperatorCapability` in the control plane already enforces this on the\n * way in; this module must not undo it on the way out. Every failure path here\n * degrades to `unresolved`, and `unresolved` does not admit a spawn. Inferring\n * a flat-cost tier from silence would route paid work onto an assumption.\n *\n * ## EXHAUSTED IS NOT CONNECTED\n *\n * A connected subscription can be USED UP, not merely absent \u2014 #9283 measured\n * `seven_day_used_pct` 91 on a live account. An exhausted subscription that\n * still reports `authenticated: true` would otherwise bind a whole swarm to\n * tier 1 and then fail every spawn, or silently spill onto a paid fallback one\n * subagent at a time. Exhaustion is therefore a first-class disqualifier with\n * its own reason string, not a missing capability.\n *\n * ## What this record is NOT\n *\n * It is a CONSISTENCY mechanism, not an authorization one. It travels in the\n * process env alongside `VO_RUNNER_PREFER_KEY` and friends and carries exactly\n * their trust level. Billing authority stays server-side; a binding can name a\n * tier but it can never GRANT one. Consumers must treat it the way the\n * capability record treats `last_seen_runner_id`: diagnostics and coordination,\n * never an authz input.\n */\n\n/**\n * Tiers, in the operator's precedence order. `refused` and `unresolved` are\n * terminal states, not fallbacks \u2014 neither admits a spawn.\n *\n * `refused` is the operator's explicit decision for \"runner offline\": REFUSE by\n * default, with a paid fallback only via per-user opt-in plus a spend cap.\n * `unresolved` is the fail-closed answer when we could not decide at all.\n */\nexport const SWARM_TIERS = Object.freeze([\n 'tier1_subscription',\n 'tier1_local',\n 'tier2_user_key',\n 'tier3_platform_key',\n 'refused',\n 'unresolved',\n] as const);\nexport type SwarmTier = (typeof SWARM_TIERS)[number];\n\n/** The two tiers that spend the platform owner's money or none at all. */\nexport const TIER_ADMITS_SPAWN: ReadonlySet<SwarmTier> = new Set<SwarmTier>([\n 'tier1_subscription',\n 'tier1_local',\n 'tier2_user_key',\n 'tier3_platform_key',\n]);\n\n/**\n * The env var that carries a binding to every subagent.\n *\n * MUST be listed in SAFE_ENV_NAMES in\n * scripts/virtual-office/code-runner/agent-process-env.mjs. The daemon builds\n * every child env through `buildAgentProcessEnv`, which DROPS any name absent\n * from that set \u2014 the exact way #9242's `VO_RUNNER_PREFER_KEY` shipped inert\n * until #9247 added the line. A binding that cannot cross the process boundary\n * is a binding that binds nothing.\n */\nexport const SWARM_TIER_BINDING_ENV = 'VO_SWARM_TIER_BINDING';\n\n/**\n * At or above this percentage of a metered subscription window, the\n * subscription is treated as EXHAUSTED for admission purposes.\n *\n * Deliberately below 100: admission binds a WHOLE fan-out, so the question is\n * not \"is there one call left\" but \"will this subscription carry N subagents\".\n * Binding 20 agents to a window at 97% spends the operator's remaining headroom\n * on the first two and strands the rest. 95 leaves a margin that is honest\n * about the fan-out's shape without being so conservative that a normally-busy\n * account never qualifies. A null reading is NOT exhaustion \u2014 it is unknown,\n * and unknown is handled by the hard rule, not by this threshold.\n */\nexport const SUBSCRIPTION_EXHAUSTED_PCT = 95;\n\n/** Hard ceiling on subagents one binding may admit. Mirrors the coordinator's\n * platform limit (MAX_COORDINATOR_AGENTS in @algosuite/vo-resume-engine). */\nexport const MAX_BOUND_SUBAGENTS = 20;\n\n/** A usage reading for one agent, as the heartbeat carries it. */\nexport interface AgentUsageWindows {\n readonly seven_day_used_pct?: number | null;\n readonly five_hour_used_pct?: number | null;\n readonly monthly_used_pct?: number | null;\n}\n\n/**\n * One capability row, shaped to match `ResolvedCapabilityAgent` from\n * cloud-run/vo-control-plane/src/schema/operator-capability-v1.ts. Structural\n * rather than an import: vo-mcp ships to npm and cannot depend on the\n * control-plane package. `test/swarm/tier-binding-lockstep.test.ts` pins the\n * two shapes together the way runner-agent-registry-lockstep.test.ts does.\n */\nexport interface CapabilityAgentRow {\n readonly agent: string;\n readonly installed: boolean;\n readonly authenticated: boolean;\n /** 'subscription' | 'api_key' | 'local' | 'unknown'. */\n readonly auth_tier: string;\n readonly stale: boolean;\n readonly usage?: AgentUsageWindows | null;\n}\n\nexport interface ResolveSwarmTierInput {\n /** Stable id of the fan-out this binding governs. */\n readonly swarm_id: string;\n /** Rows from `resolveOperatorCapability`. Empty when the record is missing. */\n readonly agents: readonly CapabilityAgentRow[];\n /** Local models this operator has been observed serving. */\n readonly local_models?: readonly string[];\n /**\n * Is NO runner currently beating? REQUIRED, and fail-closed on read.\n *\n * This was optional until #9303's review: an ABSENT reading fell through the\n * `=== true` check and went on to BIND A TIER, which is the one input in this\n * function that must not do that. The operator's decision for runner-offline\n * is REFUSE by default, and the module's hard rule is that absent input never\n * resolves upward. Required in the type so a TS caller cannot omit it, and\n * checked against an explicit `false` at runtime so an untyped caller (JSON,\n * JS, a hand-built object) cannot omit it either.\n */\n readonly runner_offline: boolean;\n /** Per-user opt-in to a paid fallback. Required for tier3. */\n readonly paid_fallback_opt_in?: boolean;\n /** Spend cap accompanying that opt-in, USD. Required for tier3. */\n readonly paid_fallback_cap_usd?: number | null;\n /** True when the operator has their own provider key stored. */\n readonly has_user_api_key?: boolean;\n /** How many subagents this fan-out intends to run. */\n readonly requested_subagents: number;\n readonly now_iso: string;\n}\n\nexport interface SwarmTierBinding {\n readonly schema_version: 1;\n readonly swarm_id: string;\n readonly tier: SwarmTier;\n /** Which agent the fan-out is bound to, or null when no tier admits a spawn. */\n readonly agent: string | null;\n /** Why this tier, in one line. Always populated, including on refusal. */\n readonly reason: string;\n /** Agents disqualified specifically by exhaustion \u2014 never silently dropped. */\n readonly exhausted_agents: readonly string[];\n /** Max subagents this binding admits. 0 when the tier admits no spawn. */\n readonly subagent_budget: number;\n /** Spend cap for a tier3 binding, USD. null for every other tier. */\n readonly spend_cap_usd: number | null;\n readonly resolved_at: string;\n}\n\nfunction clampSubagents(requested: number): number {\n if (!Number.isFinite(requested) || requested < 1) return 0;\n return Math.min(Math.floor(requested), MAX_BOUND_SUBAGENTS);\n}\n\n/**\n * Is this a usable tier-3 spend cap?\n *\n * ONE predicate, used by the resolver, the parser, and the admission check, so\n * \"capped\" cannot mean three different things. A STRING cap ('99999') is NOT a\n * cap: nothing downstream compares it numerically, so honouring it would be\n * honouring a number nobody can enforce.\n */\nexport function isPositiveCap(cap: unknown): cap is number {\n return typeof cap === 'number' && Number.isFinite(cap) && cap > 0;\n}\n\n/**\n * Is this agent's subscription window used up?\n *\n * A null/absent reading is NOT exhaustion \u2014 it is unknown. Treating unknown as\n * exhausted would refuse a healthy operator whose usage collector is simply\n * older than the reader, which is the same \"silence becomes a decision\" error\n * as treating unknown as available, just pointed the other way. Unknown usage\n * on an authenticated subscription stays tier 1; the hard rule already governs\n * unknown TIER, which is the field that decides who pays.\n */\nexport function isSubscriptionExhausted(usage: AgentUsageWindows | null | undefined): boolean {\n if (!usage) return false;\n const readings = [usage.seven_day_used_pct, usage.five_hour_used_pct, usage.monthly_used_pct];\n return readings.some((v) => typeof v === 'number' && Number.isFinite(v) && v >= SUBSCRIPTION_EXHAUSTED_PCT);\n}\n\n/** A row is usable as tier 1 only if installed, authenticated, and fresh. */\nfunction isLiveSubscription(row: CapabilityAgentRow): boolean {\n return row.installed && row.authenticated && !row.stale && row.auth_tier === 'subscription';\n}\n\n/**\n * THE ONE DECISION. Call this EXACTLY ONCE per fan-out, at admission.\n *\n * Every subagent then inherits the result via `inheritSwarmTierBinding`; none\n * of them may call this again. Re-resolving per subagent is precisely the\n * defect \u2014 the inputs legitimately change mid-swarm, so a second call can\n * legitimately return a different tier and split one fan-out across two payers.\n */\nexport function resolveSwarmTierBinding(input: ResolveSwarmTierInput): SwarmTierBinding {\n const budget = clampSubagents(input.requested_subagents);\n const base = {\n schema_version: 1 as const,\n swarm_id: input.swarm_id,\n resolved_at: input.now_iso,\n };\n const rows = Array.isArray(input.agents) ? input.agents : [];\n const subscriptionRows = rows.filter(isLiveSubscription);\n const exhausted = subscriptionRows.filter((r) => isSubscriptionExhausted(r.usage)).map((r) => r.agent);\n\n const refuse = (reason: string): SwarmTierBinding => ({\n ...base,\n tier: 'refused',\n agent: null,\n reason,\n exhausted_agents: exhausted,\n subagent_budget: 0,\n spend_cap_usd: null,\n });\n\n // Operator decision: runner offline => REFUSE by default. Checked FIRST so a\n // stale-but-present capability row cannot bind a fan-out to a machine that is\n // not there to run it.\n //\n // ONLY an explicit `false` asserts a live runner. `undefined`, `null`, the\n // STRING 'false', or any other value is runner-liveness we were never told,\n // and unknown liveness reads as offline \u2014 the same direction every other\n // optional input in this function fails (`has_user_api_key === true`,\n // `paid_fallback_opt_in === true`). The previous `=== true` form let an absent\n // reading bind tier 1 for a whole fan-out on a machine that may not exist.\n if (input.runner_offline !== false) {\n return refuse(\n input.runner_offline === true\n ? 'runner offline \u2014 refusing by default (operator decision); paid fallback needs per-user opt-in'\n : `runner liveness was not asserted (runner_offline=${JSON.stringify(input.runner_offline) ?? 'undefined'}) \u2014 absent reads as OFFLINE; refusing by default`,\n );\n }\n\n // Tier 1a: a live, non-exhausted subscription CLI.\n const usableSubscription = subscriptionRows.find((r) => !isSubscriptionExhausted(r.usage));\n if (usableSubscription) {\n return {\n ...base,\n tier: 'tier1_subscription',\n agent: usableSubscription.agent,\n reason: `connected subscription CLI '${usableSubscription.agent}' with window headroom`,\n exhausted_agents: exhausted,\n subagent_budget: budget,\n spend_cap_usd: null,\n };\n }\n\n // Tier 1b: a local model. Reached when every subscription is exhausted, which\n // is why exhaustion must be its own state \u2014 as \"absent\" it would have looked\n // identical to never having connected one, and the reason string would lie.\n const localRow = rows.find((r) => r.installed && r.authenticated && !r.stale && r.auth_tier === 'local');\n const hasLocalModel = (input.local_models?.length ?? 0) > 0;\n if (localRow || hasLocalModel) {\n return {\n ...base,\n tier: 'tier1_local',\n agent: localRow?.agent ?? 'local',\n reason: exhausted.length > 0\n ? `subscription exhausted (${exhausted.join(', ')}) \u2014 falling back to a connected local model at zero marginal cost`\n : 'connected local model at zero marginal cost',\n exhausted_agents: exhausted,\n subagent_budget: budget,\n spend_cap_usd: null,\n };\n }\n\n // Tier 2: the operator's OWN key. Their money, so no opt-in is required \u2014\n // but the tier is still recorded so billing can tell it from tier 3.\n if (input.has_user_api_key === true) {\n const keyRow = rows.find((r) => r.installed && r.authenticated && !r.stale && r.auth_tier === 'api_key');\n return {\n ...base,\n tier: 'tier2_user_key',\n agent: keyRow?.agent ?? null,\n reason: exhausted.length > 0\n ? `subscription exhausted (${exhausted.join(', ')}) \u2014 using the operator's own API key`\n : \"no subscription or local model \u2014 using the operator's own API key\",\n exhausted_agents: exhausted,\n subagent_budget: budget,\n spend_cap_usd: null,\n };\n }\n\n // Tier 3: the platform owner's keys. LAST, and only with an explicit per-user\n // opt-in AND a spend cap. An opt-in without a cap is refused rather than\n // treated as unlimited \u2014 an uncapped fan-out on someone else's key is the\n // runaway the anti-swarm invariants exist to prevent.\n if (input.paid_fallback_opt_in === true) {\n const cap = input.paid_fallback_cap_usd;\n if (!isPositiveCap(cap)) {\n return refuse('paid fallback opted in but no positive spend cap supplied \u2014 refusing an uncapped platform-billed fan-out');\n }\n const keyRow = rows.find((r) => r.installed && !r.stale);\n return {\n ...base,\n tier: 'tier3_platform_key',\n agent: keyRow?.agent ?? null,\n reason: `platform-owner keys via explicit per-user opt-in, capped at $${cap}`,\n exhausted_agents: exhausted,\n subagent_budget: budget,\n spend_cap_usd: cap,\n };\n }\n\n if (exhausted.length > 0) {\n return refuse(`subscription exhausted (${exhausted.join(', ')}) and no local model, own key, or paid-fallback opt-in`);\n }\n return refuse('no connected subscription, local model, or own API key, and no paid-fallback opt-in');\n}\n\n/** An unresolved binding. The only thing a corrupt inheritance may become. */\nexport function unresolvedBinding(swarmId: string, nowIso: string, reason: string): SwarmTierBinding {\n return {\n schema_version: 1,\n swarm_id: swarmId,\n tier: 'unresolved',\n agent: null,\n reason,\n exhausted_agents: [],\n subagent_budget: 0,\n spend_cap_usd: null,\n resolved_at: nowIso,\n };\n}\n\n/** Serialize for the env channel. */\nexport function serializeSwarmTierBinding(binding: SwarmTierBinding): string {\n return JSON.stringify(binding);\n}\n\n/**\n * Parse a binding off the wire. Any failure \u2014 absent, malformed, unknown tier,\n * wrong schema version \u2014 yields `unresolved`, never a tier. This is the hard\n * rule's last line of defence: the env is the one input to this module that\n * arrives from outside the process.\n */\nexport function parseSwarmTierBinding(raw: string | undefined | null, nowIso: string): SwarmTierBinding {\n if (typeof raw !== 'string' || raw.trim().length === 0) {\n return unresolvedBinding('', nowIso, 'no swarm tier binding present in the environment');\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return unresolvedBinding('', nowIso, 'swarm tier binding is not valid JSON');\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return unresolvedBinding('', nowIso, 'swarm tier binding is not an object');\n }\n const o = parsed as Record<string, unknown>;\n const swarmId = typeof o['swarm_id'] === 'string' ? o['swarm_id'] : '';\n if (o['schema_version'] !== 1) {\n return unresolvedBinding(swarmId, nowIso, 'swarm tier binding has an unsupported schema_version');\n }\n const tier = o['tier'];\n if (typeof tier !== 'string' || !(SWARM_TIERS as readonly string[]).includes(tier)) {\n return unresolvedBinding(swarmId, nowIso, 'swarm tier binding names an unknown tier');\n }\n const budget = o['subagent_budget'];\n const cap = o['spend_cap_usd'];\n const capNum = isPositiveCap(cap) ? cap : null;\n // `resolveSwarmTierBinding` refuses an opted-in platform fallback with no\n // positive cap (see the tier-3 branch above), but the INHERITANCE path used to\n // accept one: `{tier:'tier3_platform_key'}` with the cap absent \u2014 or a STRING\n // '99999', which fails the numeric test and lands on null \u2014 parsed to a live\n // tier3 binding with `cap: null` and admitted spawns. That let the inherited\n // path spend the platform owner's money uncapped, which is exactly what the\n // resolver forbids. An uncapped tier3 is not a tier: it is unresolved.\n if (tier === 'tier3_platform_key' && capNum === null) {\n return unresolvedBinding(\n swarmId,\n nowIso,\n 'inherited tier3_platform_key binding carries no positive numeric spend cap \u2014 refusing an uncapped platform-billed fan-out',\n );\n }\n return {\n schema_version: 1,\n swarm_id: swarmId,\n tier: tier as SwarmTier,\n agent: typeof o['agent'] === 'string' ? o['agent'] : null,\n reason: typeof o['reason'] === 'string' ? o['reason'] : 'inherited binding carried no reason',\n exhausted_agents: Array.isArray(o['exhausted_agents'])\n ? o['exhausted_agents'].filter((v): v is string => typeof v === 'string')\n : [],\n subagent_budget: typeof budget === 'number' && Number.isFinite(budget) && budget > 0\n ? Math.min(Math.floor(budget), MAX_BOUND_SUBAGENTS)\n : 0,\n spend_cap_usd: capNum,\n resolved_at: typeof o['resolved_at'] === 'string' ? o['resolved_at'] : nowIso,\n };\n}\n\n/**\n * INHERIT a binding from the environment. A subagent calls this; it must never\n * call `resolveSwarmTierBinding`. There is deliberately no re-resolution path\n * here \u2014 that absence is the feature.\n */\nexport function inheritSwarmTierBinding(\n env: Record<string, string | undefined>,\n nowIso: string,\n): SwarmTierBinding {\n return parseSwarmTierBinding(env[SWARM_TIER_BINDING_ENV], nowIso);\n}\n\n/**\n * Env fragment to merge into a subagent's environment.\n *\n * Deliberately still ONE key: the remaining budget rides INSIDE the serialized\n * binding, so there is no second env name to add to SAFE_ENV_NAMES and no way\n * for a counter to be dropped at the process boundary while the tier survives.\n * Spawners must pass `childBinding(binding, allocatedCapUsd)`, not the parent's\n * binding \u2014 see `childBindingEnvFragment`.\n */\nexport function bindingEnvFragment(binding: SwarmTierBinding): Record<string, string> {\n return { [SWARM_TIER_BINDING_ENV]: serializeSwarmTierBinding(binding) };\n}\n\n/**\n * The env fragment a spawner hands its child: same tier, decremented budget,\n * and the spend cap the LEDGER debited for this spawn.\n *\n * `allocatedCapUsd` comes from `claimSpawnSlot`'s `capUsd` and nowhere else.\n * Omitting it yields a child with NO cap, which an inherited tier3 binding\n * refuses \u2014 deliberately, so that the only way to obtain a platform-billed\n * child cap is to have debited the durable pool for it.\n */\nexport function childBindingEnvFragment(\n binding: SwarmTierBinding,\n allocatedCapUsd: number | null = null,\n): Record<string, string> {\n return bindingEnvFragment(childBinding(binding, allocatedCapUsd));\n}\n\n/**\n * May this fan-out spawn its `spawnsSoFar + 1`-th subagent?\n *\n * Pure, so the caller owns the counter \u2014 the same shape as VerifierCostGuard's\n * `admitSpawn`, and for the same reason: a cost guard that hides its own state\n * is a cost guard nobody can test.\n *\n * `spawnsSoFar` DEFAULTS TO 0 on purpose and means only \"does this binding still\n * carry allowance\". A SPAWNER must never source that number from its own caller:\n * `spawns_so_far` was a declared MCP tool input, which made the bounded process\n * the author of its own bound. Spawners get their counter from the durable\n * ledger in `spawn-ledger.ts` instead.\n */\nexport function admitSubagentSpawn(\n binding: SwarmTierBinding,\n spawnsSoFar = 0,\n): { readonly allowed: boolean; readonly reason: string } {\n if (!TIER_ADMITS_SPAWN.has(binding.tier)) {\n return { allowed: false, reason: `tier '${binding.tier}' admits no spawn: ${binding.reason}` };\n }\n // Second, independent guard on the platform's money. The parser already\n // refuses an uncapped tier3 on the way in; this catches a binding built in\n // code that never went through the parser. A cost guard with one gate is a\n // cost guard with one bug between it and the credit card.\n if (binding.tier === 'tier3_platform_key' && !isPositiveCap(binding.spend_cap_usd)) {\n return {\n allowed: false,\n reason: `swarm ${binding.swarm_id} is tier3_platform_key with no positive spend cap \u2014 refusing to spend the platform owner's money uncapped`,\n };\n }\n if (!Number.isFinite(spawnsSoFar) || spawnsSoFar < 0) {\n return { allowed: false, reason: 'spawn counter is not a finite non-negative number' };\n }\n if (spawnsSoFar >= binding.subagent_budget) {\n return {\n allowed: false,\n reason: `swarm ${binding.swarm_id} exhausted its bound subagent budget (${binding.subagent_budget})`,\n };\n }\n return { allowed: true, reason: `admitted under tier '${binding.tier}'` };\n}\n\n/**\n * The binding a CHILD inherits \u2014 the same tier decision with the budget\n * DECREMENTED and the spend cap PARTITIONED.\n *\n * ## The defect this closes\n *\n * `bindingEnvFragment(binding)` re-exported the binding VERBATIM, and every\n * consumer of an inherited binding started its own counter at zero\n * (`input.spawns_so_far ?? 0`). So a budget of 3 meant \"3 spawns per\n * generation, forever\": generation 1 spawned 3, each of those re-read budget 3\n * and spawned 3 more, and nothing ever decreased. The bound number was a\n * per-process quota dressed up as a fan-out ceiling. In a repo with an\n * anti-swarm doctrine and a prior host-resource incident, that is the most\n * consequential shape a bug can have.\n *\n * ## Why this no longer takes a counter\n *\n * It used to take `spawnsSoFar`, and the only production call site passed the\n * number the MODEL supplied. A function whose decrement is chosen by the process\n * being bounded decrements nothing. The parameter is gone: a child sees\n * `budget - 1`, floored at 0, and the counter that decides WHETHER a spawn\n * happens at all now lives in `spawn-ledger.ts`, outside the bounded process.\n *\n * ## What this guarantees, precisely\n *\n * The budget that crosses the process boundary STRICTLY DECREASES with every\n * hop, so every chain terminates and its DEPTH is bounded by the admitted\n * budget.\n *\n * The spend cap is NOT derived from the parent's cap. It is the share the\n * durable ledger DEBITED for this spawn (`claimSpawnSlot(...).capUsd`), and it\n * is clamped never to exceed the parent's own cap. `allocatedCapUsd` omitted, or\n * non-positive, yields a child with NO cap \u2014 which an inherited tier3 binding\n * refuses. That refusal is the point: a platform-billed child cap must be\n * DEBITED into existence, never computed.\n *\n * ## What it does NOT guarantee (stated, not implied)\n *\n * Nothing here bounds width on its own \u2014 this function is still pure. The\n * conservation over a TREE comes entirely from the ledger, and only on ONE host.\n * Exact cross-host accounting still belongs to the control plane, not here.\n */\nexport function childBinding(\n binding: SwarmTierBinding,\n allocatedCapUsd: number | null = null,\n): SwarmTierBinding {\n const allocated = isPositiveCap(allocatedCapUsd) ? allocatedCapUsd : null;\n const parentCap = isPositiveCap(binding.spend_cap_usd) ? binding.spend_cap_usd : null;\n return {\n ...binding,\n subagent_budget: Math.max(0, binding.subagent_budget - 1),\n // A child never carries more than its parent, whatever the ledger says: a\n // forged or hand-edited pool cannot inflate a descendant above the binding\n // it descends from.\n spend_cap_usd: allocated === null || parentCap === null ? null : Math.min(allocated, parentCap),\n };\n}\n\n/**\n * Halve a tier-3 spend cap between the process that holds it and ONE child.\n *\n * ## CORRECTION \u2014 the invariant this function was documented with was FALSE\n *\n * #9312 shipped this function and asserted, in this comment, in the plugin twin,\n * in the PR body and in the merged roadmap fragment\n * `docs/vo/roadmap-log/2026-08-02-fanout-ceiling-untrusted-counter.md`, that\n * \"the caps carried by ALL descendants of a binding sum to at most the parent's\n * own cap\".\n *\n * That claim was tested over a CHAIN and it is TRUE over a chain: 12 generations\n * of a $50 cap sum to $49.95. A swarm is a TREE. This function is PURE and holds\n * no state, so a $50 parent that spawns THREE children calls it three times and\n * hands out $25 each = $75 > $50. Re-measured end-to-end against the shipped\n * twin, a $50 root fanned 3 wide by 3 deep distributed **$356.25 \u2014 7.1x the\n * cap** \u2014 on the one field that guards the platform owner's money.\n *\n * The sentence is corrected rather than deleted because an overclaim that\n * quietly disappears teaches the next author nothing. What held: CONSERVATION\n * OVER DEPTH. What did not: conservation over WIDTH, which is what a swarm\n * actually does.\n *\n * ## What replaced it\n *\n * `spawn-ledger.ts` records the cap as a durable per-swarm POOL and DEBITS one\n * equal share of it per exclusive slot claim. Because every spawn in the tree \u2014\n * every generation, every sibling, both twins on the host \u2014 must win a distinct\n * slot from that one ledger, the sum over the whole tree is bounded by the pool.\n * Conservation now comes from persisted state, not from a pure function that a\n * sibling can re-enter with an untouched input.\n *\n * ## Why this is still here\n *\n * It is the anchor for the correction above, and it is deliberately NOT on the\n * spawn path: `childBinding` takes the ledger's debit instead. Re-wiring it back\n * into `childBinding` is covered by a mutation in\n * `scripts/ci/mutation-test-fanout-ceiling.mjs`, so the regression cannot land\n * silently. What it still is, accurately: a conserving two-way split in integer\n * cents (`child + retained === cap`) \u2014 correct for ONE parent and ONE child, and\n * nothing more.\n *\n * A non-positive / non-numeric cap partitions to `{null, null}` \u2014 the same\n * answer `isPositiveCap` gives everywhere else, so an uncapped tier3 stays\n * unresolvable rather than becoming a zero-cap tier3.\n */\nexport function partitionSpendCap(\n cap: number | null | undefined,\n): { readonly child: number | null; readonly retained: number | null } {\n if (!isPositiveCap(cap)) return { child: null, retained: null };\n // Integer cents: float halving of 0.01 repeatedly would drift, and a cap that\n // does not add back up is not a partition.\n const cents = Math.round(cap * 100);\n const childCents = Math.floor(cents / 2);\n return { child: childCents / 100, retained: (cents - childCents) / 100 };\n}\n\n/**\n * Refuse a caller-supplied `agent` that CONTRADICTS the bound one.\n *\n * ## The defect this closes\n *\n * The spawner documented \"an explicit `agent` may pick WHICH agent inside the\n * bound tier, but it can never change the tier\" and then implemented\n * `input.agent?.trim() || binding.agent` with no check at all. `agent` is\n * model-supplied. Naming a different agent moves the fan-out onto a different\n * vendor's subscription or key \u2014 a different PAYER \u2014 which is the exact decision\n * admission exists to make once. Code that does not enforce the invariant it\n * documents defeats the whole mechanism.\n *\n * Restating the bound agent is a harmless no-op and stays allowed. Anything else\n * is refused, including any agent at all when the binding named none: a null\n * bound agent plus a model-supplied name is silence resolving upward.\n *\n * Returns a refusal reason, or `null` when the request is compatible.\n */\nexport function agentBindingRefusal(\n binding: Pick<SwarmTierBinding, 'agent' | 'tier' | 'swarm_id'>,\n requestedAgent: string | null | undefined,\n): string | null {\n const requested = typeof requestedAgent === 'string' ? requestedAgent.trim() : '';\n if (requested.length === 0) return null;\n if (binding.agent !== null && requested === binding.agent) return null;\n return (\n `swarm '${binding.swarm_id}' is bound to agent '${binding.agent ?? 'none'}' under tier '${binding.tier}'; ` +\n `a caller-supplied agent '${requested}' would move this fan-out onto a different payer \u2014 refusing ` +\n '(the tier is decided once, at admission, and an inherited binding cannot be renegotiated)'\n );\n}\n", "/**\n * Per-agent launch shapes for a successor / subagent spawn.\n *\n * ## Why this file exists\n *\n * Both successor spawners hardcode `claude` \u2014 the binary AND the argv. The\n * operator's clause is that the precedence holds for subagents from workflows\n * and swarms, so a fan-out bound to `codex` must spawn codex. But the naive fix\n * \u2014 swap the binary name and keep the args \u2014 SHIPS BROKEN:\n *\n * claude: claude -p --permission-mode acceptEdits [--max-turns N]\n * codex: codex exec --json -c approval_policy=\"never\" \\\n * --sandbox workspace-write --skip-git-repo-check -\n *\n * (see scripts/virtual-office/code-runner/claude-args.mjs:46-55 and\n * codex-runner.mjs:97-107). `codex -p --permission-mode acceptEdits` is not a\n * valid codex invocation. So the resolution has to carry the whole launch, not\n * just a name.\n *\n * ## THE REFUSAL RULES\n *\n * 1. An agent with no known headless launch shape is REFUSED, never\n * approximated. Guessing argv for `cursor` would produce a spawn that either\n * fails obscurely or \u2014 worse \u2014 succeeds with different semantics.\n *\n * 2. A requested `max_turns` that the resolved agent CANNOT ENFORCE is\n * REFUSED. `--max-turns` is claude-only; codex's arg builder emits no turn\n * cap at all. Honouring the resolved agent by silently dropping the cap\n * would convert a bounded spawn into an unbounded one \u2014 removing a spend\n * guard as a side effect of a billing-correctness change is precisely the\n * backfire this slice is supposed to prevent.\n *\n * Both refusals are LOUD (a named reason on a `spawned: false` result), never a\n * silent downgrade.\n */\n\n/** What one agent's headless launch looks like. */\nexport interface AgentLaunchShape {\n readonly bin: string;\n /** Args that do not depend on the request. */\n readonly baseArgs: readonly string[];\n /** Whether this CLI can bound the run to N turns. */\n readonly enforcesMaxTurns: boolean;\n /** Flag pair used to express a turn cap, when it can. */\n readonly maxTurnsFlag?: string;\n /** Trailing args appended after any request-dependent flags. */\n readonly trailingArgs?: readonly string[];\n /**\n * Whether this argv survives `spawn(..., { shell: true })` on Windows.\n *\n * The successor spawner sets `shell: true` on win32 because the agent CLIs\n * are `.cmd` shims that cannot otherwise be resolved. That routes the whole\n * command line through cmd.exe, which re-parses quoting. claude's argv is\n * plain tokens and survives; codex's carries embedded double quotes\n * (`-c approval_policy=\"never\"`) and its behaviour under cmd.exe re-parsing\n * is UNVERIFIED here. Marking it false makes the spawner refuse on win32\n * rather than emit a command line that may silently mean something else.\n */\n readonly windowsShellSafe: boolean;\n}\n\n/**\n * The agents this repo knows how to launch headlessly with a piped prompt.\n *\n * Kept deliberately small. An agent is added here only once its real argv is\n * verified against the runner that already spawns it \u2014 an entry here is a\n * promise that the spawn works, and a wrong promise is worse than an absence\n * because the absence refuses loudly.\n */\nexport const AGENT_LAUNCH_SHAPES: Readonly<Record<string, AgentLaunchShape>> = Object.freeze({\n claude: {\n bin: 'claude',\n baseArgs: ['-p', '--permission-mode', 'acceptEdits'],\n enforcesMaxTurns: true,\n maxTurnsFlag: '--max-turns',\n windowsShellSafe: true,\n },\n codex: {\n bin: 'codex',\n baseArgs: ['exec', '--json', '-c', 'approval_policy=\"never\"', '--sandbox', 'workspace-write', '--skip-git-repo-check'],\n enforcesMaxTurns: false,\n // `-` makes codex read the prompt from stdin (injection-safe), matching how\n // codex-runner.mjs already spawns it.\n trailingArgs: ['-'],\n // `approval_policy=\"never\"` carries embedded quotes; cmd.exe re-parsing is\n // unverified, so win32 refuses rather than risking a mangled sandbox flag.\n windowsShellSafe: false,\n },\n});\n\n/**\n * Can a successor ACTUALLY be launched as `agent` on `platform`?\n *\n * ## Why a predicate exists at all\n *\n * This registry is small on purpose, and every refusal above is correct. But a\n * refusal is only honest when the caller had a CHOICE. #9313 gave the swarm tier\n * binding a producer at code-task admission, and that producer bound the real\n * dispatch agent \u2014 `cursor`, `local`, `meta`, `oai` included. None of those has\n * a shape here, so `resolveSuccessorLaunch` refused every successor spawn those\n * tasks used to make. Before #9313 no binding existed, they took the unbound\n * legacy path, and they spawned. A billing-correctness change silently deleted a\n * working capability.\n *\n * The producer cannot fix that by keeping its own list of launchable agents \u2014\n * that is a second copy of this table, and a second copy drifts. So this is the\n * ONE question it asks, derived from `AGENT_LAUNCH_SHAPES` and nothing else:\n * bind only what this lane can actually start, and leave everything else on the\n * behaviour it has today.\n *\n * `platform` is part of the question, not a detail. `codex` HAS a shape and is\n * still unlaunchable on win32 (its argv carries embedded quotes that cmd.exe\n * re-parses), so on the operator's own Windows fleet a codex binding would\n * refuse exactly like `cursor`. Answering \"yes\" here on the strength of the\n * table alone would leave that half of the regression live.\n *\n * Deliberately does NOT consider `max_turns`: that is a property of one request,\n * not of whether the lane can launch the agent at all, and a binding is minted\n * once for a whole fan-out whose per-spawn turn caps are not yet known.\n */\nexport function canLaunchSuccessorAgent(\n agent: string | null | undefined,\n platform: string = process.platform,\n): boolean {\n const name = typeof agent === 'string' ? agent.trim() : '';\n if (!name) return false;\n const shape = AGENT_LAUNCH_SHAPES[name];\n if (!shape) return false;\n if (platform === 'win32' && !shape.windowsShellSafe) return false;\n return true;\n}\n\nexport type SuccessorLaunch =\n | { readonly ok: true; readonly agent: string; readonly bin: string; readonly args: readonly string[] }\n | { readonly ok: false; readonly reason: string };\n\nexport interface ResolveSuccessorLaunchInput {\n /** The agent the binding resolved to. null/absent means nothing was bound. */\n readonly agent: string | null | undefined;\n readonly maxTurns?: number | undefined;\n /** Defaults to the host platform; injected so the win32 rule is testable. */\n readonly platform?: string | undefined;\n}\n\n/**\n * Resolve the launch for a successor spawn, or refuse with a named reason.\n *\n * There is NO default-to-claude branch. A caller that has not resolved an agent\n * gets a refusal, because defaulting is how the fan-out ends up half on the\n * operator's subscription and half on the platform's key in the first place.\n */\nexport function resolveSuccessorLaunch(input: ResolveSuccessorLaunchInput): SuccessorLaunch {\n const agent = typeof input.agent === 'string' ? input.agent.trim() : '';\n if (!agent) {\n return { ok: false, reason: 'no agent bound for this spawn \u2014 refusing rather than defaulting to claude' };\n }\n const shape = AGENT_LAUNCH_SHAPES[agent];\n if (!shape) {\n const known = Object.keys(AGENT_LAUNCH_SHAPES).join(', ');\n return {\n ok: false,\n reason: `no known headless launch shape for agent '${agent}' (known: ${known}) \u2014 refusing rather than guessing its argv`,\n };\n }\n\n const wantsMaxTurns = Number.isInteger(input.maxTurns) && (input.maxTurns as number) > 0;\n if (wantsMaxTurns && !shape.enforcesMaxTurns) {\n return {\n ok: false,\n reason: `agent '${agent}' cannot enforce a max_turns cap \u2014 refusing rather than spawning it unbounded`,\n };\n }\n\n const platform = input.platform ?? process.platform;\n if (platform === 'win32' && !shape.windowsShellSafe) {\n return {\n ok: false,\n reason: `agent '${agent}' has an argv whose behaviour under Windows cmd.exe re-parsing is unverified \u2014 refusing rather than emitting a command line that may mean something else`,\n };\n }\n\n const args = [...shape.baseArgs];\n if (wantsMaxTurns && shape.maxTurnsFlag) {\n args.push(shape.maxTurnsFlag, String(input.maxTurns));\n }\n if (shape.trailingArgs) args.push(...shape.trailingArgs);\n\n return { ok: true, agent, bin: shape.bin, args };\n}\n", "/**\n * The swarm SPAWN LEDGER \u2014 a durable, host-shared counter for one fan-out.\n *\n * ## The defect this closes\n *\n * #9311 made the inherited budget DECREMENT, which bounds a chain's DEPTH. It\n * left the counter itself in the caller's hands: `vo_spawn_successor` declared\n * `spawns_so_far` as an MCP TOOL INPUT (`input.spawns_so_far ?? 0`) and the\n * plugin CLI took `--spawns-so-far`. Both are supplied by the very process being\n * bounded, and both default to ZERO when absent. Reproduced end-to-end through\n * `handleSpawnSuccessor` with the real `resolveSpawnPlan` and a fake spawn: ONE\n * process performed 25 real spawns from a budget-3 binding \u2014 and would have\n * performed any number \u2014 simply by omitting the field. Absent optional input\n * resolving toward MORE spending is the exact fail-open shape this PR series\n * already fixed for `runner_offline`.\n *\n * A pure predicate may take a counter (`admitSubagentSpawn` does, deliberately \u2014\n * a cost guard that hides its state is a cost guard nobody can test). A SPAWNER\n * may not take one from the model. So the spawners now get their counter from\n * here.\n *\n * ## Why the filesystem\n *\n * The bound this replaces is per-process, and both spawners are one-shot in the\n * lane that matters: `vo-claude-plugin/hooks/session-state-tracker-auto-spawn.mjs`\n * fires a FRESH `node vo-spawn-successor.mjs` per successor, so any counter held\n * in a variable, or written back into `process.env`, dies with the process and\n * the next one restarts at zero. A directory of exclusively-created slot files\n * is a counter that outlives the process, is shared by BOTH spawners on the\n * host, and is race-free without a lock: `openSync(path, 'wx')` is O_EXCL, so\n * exactly one concurrent claimant can win a given slot.\n *\n * The ceiling is written ONCE per swarm, by whoever claims first, and is read \u2014\n * never re-proposed \u2014 by everyone after. A descendant therefore cannot inflate\n * the fan-out it was admitted into.\n *\n * ## What this bounds, precisely (and what it does not)\n *\n * BOUNDS: the TOTAL number of spawns performed under one `swarm_id` on one host,\n * across every generation and every sibling, to the ceiling recorded at\n * admission. Combined with `childBinding`'s decrement (depth) that is a real\n * ceiling, not a per-process quota.\n *\n * DOES NOT BOUND: a fan-out spread across hosts (the ledger is host-local), or a\n * caller that FORGES `VO_SWARM_TIER_BINDING` in its own environment. The latter\n * is out of scope by design and always has been \u2014 this module is a CONSISTENCY\n * mechanism, not an authorization one (see tier-binding.ts's header). The defect\n * being closed is a far lower bar than forging env: a number the model typed\n * into a declared tool input.\n *\n * RESIDUE, stated rather than discovered later: the ledger is append-only and\n * has no reaper. One directory per swarm, at most `MAX_BOUND_SUBAGENTS + 1`\n * files of ~80 bytes inside it \u2014 so 10,000 fan-outs is ~17 MB. Deliberately no\n * cleanup path: a reaper is a deletion path, and a deletion path that races a\n * live claim would hand out a slot twice. Bounded residue beats an unbounded\n * fan-out.\n *\n * FAILS CLOSED: if the ledger cannot be read or written, the spawn is REFUSED.\n * An unbounded fan-out on the platform owner's key is the highest-consequence\n * defect in this programme, and this repo has a prior host-resource-exhaustion\n * incident behind its anti-swarm doctrine.\n *\n * ## The ledger also DEBITS the spend cap (slice W5A)\n *\n * #9312 shipped `partitionSpendCap`, a PURE function of the parent's cap\n * (`child = floor(cents/2)`), and asserted that \"the caps carried by ALL\n * descendants of a binding sum to at most the root cap\". That was measured over\n * a CHAIN \u2014 12 generations summing to $49.95 of $50 \u2014 and it is TRUE over depth.\n * A swarm is a TREE, and over WIDTH the claim is FALSE: the function holds no\n * state, so a $50 parent that calls it three times hands each child $25 = $75.\n * Re-measured against the shipped twin, a root cap of $50 fanned 3 wide by 3\n * deep distributed **$356.25 \u2014 7.1x the cap**, on the one field that guards the\n * platform owner's money.\n *\n * The fix is the reason this file exists: the remaining allowance must be\n * PERSISTED and DEBITED per spawn, never recomputed from the parent's untouched\n * cap. The pool (`cap_cents`) is recorded ONCE next to the ceiling, and each\n * exclusive slot claim draws exactly one share of it \u2014 so the debit is atomic\n * with the claim, and a sibling physically cannot re-read an undebited cap\n * because it must win a DIFFERENT slot.\n */\nimport { mkdirSync, openSync, closeSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { MAX_BOUND_SUBAGENTS, isPositiveCap } from './tier-binding.js';\n\n/**\n * Where the ledger lives. Overridable so tests never touch the real home dir.\n *\n * Listed in SAFE_ENV_NAMES in\n * scripts/virtual-office/code-runner/agent-process-env.mjs: if a parent's\n * override were stripped at the process boundary, the child would ledger into a\n * DIFFERENT directory and the shared ceiling would silently become per-process\n * again \u2014 the exact defect this file exists to close.\n */\nexport const SWARM_LEDGER_DIR_ENV = 'VO_SWARM_LEDGER_DIR';\n\n/** Default ledger root, alongside the handoff and successor-log directories. */\nexport function resolveLedgerDir(env: Record<string, string | undefined>): string {\n const override = env[SWARM_LEDGER_DIR_ENV];\n if (typeof override === 'string' && override.trim().length > 0) return override.trim();\n return join(homedir(), '.vo', 'swarm-ledger');\n}\n\n/**\n * A swarm id is safe as a directory name, or it is not usable as a ledger key.\n *\n * `swarm_id` arrives from the environment and is interpolated into a PATH. A\n * `..` or a separator would let a binding write outside the ledger root, so\n * anything outside `[A-Za-z0-9._-]` is refused rather than escaped: a spawn we\n * cannot account for is a spawn we do not perform.\n */\nexport function sanitizeSwarmId(raw: unknown): string | null {\n if (typeof raw !== 'string') return null;\n const id = raw.trim();\n if (id.length === 0 || id.length > 128) return null;\n if (!/^[A-Za-z0-9._-]+$/u.test(id)) return null;\n if (id === '.' || id === '..') return null;\n return id;\n}\n\nexport interface SpawnSlotInput {\n readonly swarmId: unknown;\n /** The ceiling this caller proposes, used ONLY if no ceiling is recorded yet. */\n readonly proposedCeiling: number;\n /**\n * The spend-cap POOL this caller proposes, USD \u2014 recorded ONLY if no pool is\n * recorded yet, exactly like the ceiling. Absent / null / non-positive means\n * \"this fan-out has no cap to account\" (tier1 and tier2 spend nobody else's\n * money), which is NOT the same as \"the pool is unknown\": an unknown pool\n * refuses a capped spawn, see `claimSpawnSlot`.\n */\n readonly proposedCapUsd?: number | null;\n readonly dir: string;\n readonly nowIso: string;\n}\n\nexport type SpawnSlotResult =\n | {\n readonly ok: true;\n readonly slot: number;\n readonly ceiling: number;\n readonly remaining: number;\n /**\n * The share of the pool this claim DEBITED, USD \u2014 the cap the child may\n * carry. null when the fan-out has no pool (tier1/tier2).\n */\n readonly capUsd: number | null;\n /** Pool left after this debit, USD. Strictly decreases with every claim. */\n readonly capRemainingUsd: number | null;\n }\n | { readonly ok: false; readonly reason: string };\n\nexport type ClaimSpawnSlot = (input: SpawnSlotInput) => SpawnSlotResult;\n\nconst CEILING_FILE = 'ceiling.json';\n\n/** The recorded head of one swarm's ledger: its ceiling and its spend pool. */\ninterface LedgerHead {\n readonly ceiling: number;\n /** Integer cents. 0 means \"no cap recorded\" \u2014 see the read rule below. */\n readonly capCents: number;\n}\n\n/** Create `path` only if it does not exist. Returns false on EEXIST. */\nfunction createExclusive(path: string, contents: string): boolean {\n let fd: number | undefined;\n try {\n fd = openSync(path, 'wx');\n } catch {\n return false;\n }\n try {\n writeFileSync(fd, contents, 'utf8');\n } finally {\n closeSync(fd);\n }\n return true;\n}\n\n/** A cap in USD as integer cents. Non-positive / non-numeric reads as 0. */\nexport function capToCents(cap: number | null | undefined): number {\n return isPositiveCap(cap) ? Math.round(cap * 100) : 0;\n}\n\n/**\n * The head of this swarm's ledger: the first claimant's ceiling AND spend pool,\n * recorded once, read \u2014 never re-proposed \u2014 by everyone after.\n *\n * Returns null when the ledger is unreadable or the recorded ceiling is not\n * usable; both refuse the spawn upstream rather than inventing one.\n *\n * An ABSENT `cap_cents` (a head written before slice W5A, or a hand-edited one)\n * reads as ZERO POOL, not as an unknown to be filled in from the caller. Zero\n * pool costs a tier1/tier2 fan-out nothing \u2014 those tiers carry no cap \u2014 and\n * REFUSES a tier3 one, which is the hard rule pointed the only direction it may\n * point: absent input never resolves toward more of someone else's money.\n */\nfunction readOrRecordLedgerHead(\n swarmDir: string,\n proposedCeiling: number,\n proposedCapCents: number,\n nowIso: string,\n): LedgerHead | null {\n const path = join(swarmDir, CEILING_FILE);\n const head = JSON.stringify({\n ceiling: proposedCeiling,\n cap_cents: proposedCapCents,\n recorded_at: nowIso,\n });\n if (createExclusive(path, head)) {\n return { ceiling: proposedCeiling, capCents: proposedCapCents };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'));\n } catch {\n return null;\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const record = parsed as Record<string, unknown>;\n const recorded = record['ceiling'];\n if (typeof recorded !== 'number' || !Number.isFinite(recorded) || recorded < 1) return null;\n const recordedCap = record['cap_cents'];\n const capCents =\n typeof recordedCap === 'number' && Number.isFinite(recordedCap) && recordedCap > 0\n ? Math.floor(recordedCap)\n : 0;\n // Clamped on READ too: a hand-edited ledger cannot raise the platform limit.\n return { ceiling: Math.min(Math.floor(recorded), MAX_BOUND_SUBAGENTS), capCents };\n}\n\n/**\n * Claim the next spawn slot for `swarmId`, or refuse with a named reason.\n *\n * The claim is the counter. It is taken AFTER every other refusal so a refused\n * spawn does not consume allowance, and it is NOT released if the spawn then\n * fails: a spawner that can retry indefinitely is the unbounded loop this file\n * exists to prevent, and consuming the slot is the fail-closed direction.\n *\n * ## The claim is also the spend-cap DEBIT\n *\n * Every spawn under this swarm \u2014 every generation, every sibling, both twins on\n * this host \u2014 wins a DISTINCT slot from this one directory, and each slot draws\n * exactly one equal share of the recorded pool:\n *\n * share = floor(cap_cents / ceiling) total = claims * share <= cap_cents\n *\n * That is the conservation the pure `partitionSpendCap` could not give: it is a\n * function of durable SHARED state, so three siblings get three different slots\n * and three separate draws, not three copies of the same halving. Conservation\n * therefore holds over the TREE, not merely over a chain.\n *\n * Two properties stated rather than discovered later:\n *\n * - Equal shares, not halving-from-remaining. Halving would make a child's cap\n * depend on the ORDER claims happen to arrive in, and would starve the tail\n * (slot 12 of a $50 pool gets 0 cents and is refused while the ceiling still\n * has allowance). Equal shares are deterministic and fund every admitted\n * spawn identically.\n * - Unclaimed allowance is NOT redistributed. A narrow fan-out under a wide\n * ceiling leaves headroom unspent. Redistribution needs a release path, and\n * a release path is a path that can hand the same allowance out twice \u2014\n * exactly the reason this file has no reaper. Under-allocating is the\n * fail-closed direction; the cap is a ceiling on exposure, not an\n * entitlement.\n *\n * The ACCOUNTING is the integer-cent fields written to each slot file; the USD\n * numbers returned here are a rendering of them. Stated because IEEE754 can make\n * a USD sum read a hair over its pool (3 x $0.21 is 0.6300000000000001, not\n * 0.63) while the cents it renders \u2014 3 x 21 <= 63 \u2014 are exact. Assert\n * conservation against `cap_cents_debited`, not against summed floats.\n *\n * ## What the pool does NOT bound (found by attacking this file, not later)\n *\n * A ledger head PRE-FORGED on disk before the first legitimate claim \u2014 a\n * `ceiling.json` carrying an inflated `cap_cents` \u2014 makes each share large, and\n * `childBinding` then clamps each child to its PARENT's cap. So no descendant\n * ever exceeds its parent, but the SUM across the fan-out is bounded by the\n * forged pool rather than by the root cap. This sits inside the threat model\n * this module has carried since #9312: a caller that can write the ledger can\n * equally forge `VO_SWARM_TIER_BINDING` itself, and this is a CONSISTENCY\n * mechanism, not an authorization one. It is not a regression either \u2014 before\n * this slice the cap ignored the ledger completely. Named here so the next\n * reader does not have to rediscover it.\n *\n * The tempting mitigation \u2014 also dividing the CALLER's own cap by the ceiling \u2014\n * was rejected: a legitimate descendant's cap is already one share, so dividing\n * it again shrinks allocations with depth and reintroduces exactly the\n * order-dependence the equal-share rule exists to remove.\n */\nexport const claimSpawnSlot: ClaimSpawnSlot = ({ swarmId, proposedCeiling, proposedCapUsd, dir, nowIso }) => {\n const id = sanitizeSwarmId(swarmId);\n if (id === null) {\n return {\n ok: false,\n reason:\n `swarm id ${JSON.stringify(swarmId)} is absent or unusable as a ledger key \u2014 ` +\n 'refusing a spawn that cannot be counted against a fan-out ceiling',\n };\n }\n const proposed = Number.isFinite(proposedCeiling) ? Math.floor(proposedCeiling) : 0;\n if (proposed < 1) {\n return { ok: false, reason: `swarm '${id}' proposes a ceiling of ${proposed} \u2014 no allowance to claim` };\n }\n const swarmDir = join(dir, id);\n try {\n mkdirSync(swarmDir, { recursive: true });\n } catch (err) {\n return {\n ok: false,\n reason: `swarm '${id}' ledger directory is unwritable (${err instanceof Error ? err.message : String(err)}) \u2014 refusing rather than spawning uncounted`,\n };\n }\n const wantedCents = capToCents(proposedCapUsd);\n const head = readOrRecordLedgerHead(swarmDir, Math.min(proposed, MAX_BOUND_SUBAGENTS), wantedCents, nowIso);\n if (head === null) {\n return { ok: false, reason: `swarm '${id}' ledger carries no readable ceiling \u2014 refusing rather than spawning uncounted` };\n }\n const { ceiling, capCents } = head;\n // One equal share of the RECORDED pool. Integer cents throughout: a pool that\n // does not add back up is not an accounting of anything.\n const shareCents = capCents > 0 ? Math.floor(capCents / ceiling) : 0;\n // A caller that needs a cap and cannot be given one is REFUSED. Handing it\n // `null` would degrade the child to an uncapped tier3, which the parser and\n // `admitSubagentSpawn` both already refuse \u2014 but refusing HERE names the real\n // reason instead of letting it surface one hop later as \"no spend cap\".\n if (wantedCents > 0 && shareCents < 1) {\n return {\n ok: false,\n reason:\n `swarm '${id}' has no spend allowance left to debit (recorded pool $${(capCents / 100).toFixed(2)} ` +\n `across a ceiling of ${ceiling} leaves under one cent per spawn) \u2014 refusing a platform-billed spawn it cannot fund`,\n };\n }\n for (let slot = 0; slot < ceiling; slot++) {\n const debitedCents = shareCents;\n const remainingCents = capCents > 0 ? capCents - (slot + 1) * shareCents : 0;\n const claimed = createExclusive(\n join(swarmDir, `slot-${slot}.json`),\n JSON.stringify({\n slot,\n ceiling,\n pid: process.pid,\n claimed_at: nowIso,\n // The debit record. Durable and atomic with the claim: this file is\n // created with O_EXCL, so exactly one claimant ever writes this line.\n cap_cents_pool: capCents,\n cap_cents_debited: debitedCents,\n cap_cents_remaining: remainingCents,\n }),\n );\n if (claimed) {\n return {\n ok: true,\n slot,\n ceiling,\n remaining: ceiling - slot - 1,\n capUsd: debitedCents > 0 ? debitedCents / 100 : null,\n capRemainingUsd: capCents > 0 ? remainingCents / 100 : null,\n };\n }\n }\n return {\n ok: false,\n reason: `swarm '${id}' has spent its whole fan-out ceiling (${ceiling} spawns across every generation) \u2014 refusing`,\n };\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 * 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 * Tools: private cloud knowledge for Claude/Codex/Cursor/cowork clients.\n *\n * Uses the same auth-token source as `vo_sync_config` (`vo-mcp login` preferred).\n * The control-plane binds all calls to the authenticated operator and returns\n * metadata/snippets/context only \u2014 no raw corpus download surface.\n */\nimport type { ToolDeps } from '../../types.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\nimport type { FetchLike } from './sync-config.js';\n\nexport const UPSERT_TOOL_NAME = 'vo_private_knowledge_upsert';\nexport const CONTEXT_TOOL_NAME = 'vo_private_knowledge_context';\nexport const INVALIDATE_TOOL_NAME = 'vo_private_knowledge_invalidate';\nexport const STALE_TOOL_NAME = 'vo_private_knowledge_stale';\n\nconst KNOWLEDGE_CLASSES = ['memory', 'skill', 'doctrine', 'hook', 'command'] as const;\ntype KnowledgeClass = (typeof KNOWLEDGE_CLASSES)[number];\n\n/**\n * ~3 pages of dense text. Advisory only \u2014 oversized entries still upsert, but\n * retrieval surfaces whole entries, so tight focused entries beat bulk dumps.\n * Verified against the knowledge-base precision findings of 2026-07-11\n * (small corpora load whole into context; precision beats volume).\n */\nexport const PRECISION_CHAR_BUDGET = 12_000;\n\nexport const upsertInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n knowledge_class: { type: 'string', enum: KNOWLEDGE_CLASSES },\n source_path: { type: 'string', description: 'Stable private source identifier; not exposed to other users.' },\n title: { type: 'string', description: 'Descriptive, retrieval-friendly title (e.g. \"AlgoTax OCR redaction architecture\", not \"notes\") \u2014 retrieval matches on it.' },\n content: { type: 'string', description: 'Private knowledge text to store server-side. Keep each entry tight and focused (~1-3 pages, under ~12k chars); split larger corpora into separate entries.' },\n },\n required: ['knowledge_class', 'source_path', 'title', 'content'],\n additionalProperties: false,\n};\n\nexport const contextInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n query: { type: 'string' },\n limit: { type: 'number', minimum: 1, maximum: 50 },\n knowledge_class: { type: 'string', enum: KNOWLEDGE_CLASSES },\n },\n required: ['query'],\n additionalProperties: false,\n};\n\nexport const invalidateInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n knowledge_class: { type: 'string', enum: KNOWLEDGE_CLASSES },\n source_path: { type: 'string', minLength: 1, maxLength: 400, description: 'Stable private source identifier of the entry to invalidate \u2014 must match the source_path used at upsert.' },\n },\n required: ['knowledge_class', 'source_path'],\n additionalProperties: false,\n};\n\nexport const staleInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n days: { type: 'number', minimum: 1, maximum: 3650, description: 'Window in days (default 90): entries at least this old that were never recalled into an agent context, or not within the window.' },\n limit: { type: 'number', minimum: 1, maximum: 500 },\n },\n additionalProperties: false,\n};\n\nexport const staleDescription =\n 'The FORGETTING REPORT: lists the authenticated operator\u2019s live private-knowledge entries that are at least N days old and have never been recalled into an agent context (or not within N days). Metadata only. ' +\n 'SURFACES ONLY \u2014 never auto-invalidates or auto-merges: two memories that disagree may both have been right in different contexts, so you decide. ' +\n `Act on a candidate deliberately with ${INVALIDATE_TOOL_NAME}; recall counts come from ${CONTEXT_TOOL_NAME} reads that actually placed the entry into returned context.`;\n\nexport const upsertDescription =\n 'Uploads or refreshes the authenticated operator\u2019s private cloud knowledge. Works for Claude, Codex, Cursor, and cowork clients via the same vo-mcp login credential. Returns metadata only, not raw stored content. ' +\n 'PRECISION DISCIPLINE: keep each entry tight and focused (~1-3 pages) with a descriptive retrieval-friendly title \u2014 retrieval surfaces whole entries, so small dense entries beat bulk dumps. Split large corpora into focused entries, then run a retrieval self-test via vo_private_knowledge_context before relying on the knowledge.';\n\nexport const contextDescription =\n 'Retrieves prompt-ready private knowledge context for the authenticated operator. Returns snippets/context only; no raw corpus download. ' +\n 'Also the retrieval self-test surface: after upserting critical knowledge, query for it here and confirm the entry surfaces before trusting it in downstream work.';\n\nexport const invalidateDescription =\n 'Soft-deletes one private-knowledge entry for the authenticated operator: closes the live entry\u2019s validity window (bi-temporal) so it stops surfacing in retrieval. Never destroys data \u2014 invalidated versions remain queryable server-side via include_invalidated. ' +\n 'Identify the entry by the same { knowledge_class, source_path } used at upsert; a not_found response means no live entry matches. ' +\n `After invalidating, self-test via ${CONTEXT_TOOL_NAME} to confirm the entry no longer surfaces.`;\n\ninterface UpsertInput {\n readonly knowledge_class: KnowledgeClass;\n readonly source_path: string;\n readonly title: string;\n readonly content: string;\n}\n\ninterface InvalidateInput {\n readonly knowledge_class: KnowledgeClass;\n readonly source_path: string;\n}\n\ninterface ContextInput {\n readonly query: string;\n readonly limit?: number;\n readonly knowledge_class?: KnowledgeClass;\n}\n\ninterface StaleInput {\n readonly days?: number;\n readonly limit?: number;\n}\n\nfunction isStaleInput(value: unknown): value is StaleInput {\n if (value === undefined || value === null) return true;\n if (typeof value !== 'object') return false;\n const input = value as Record<string, unknown>;\n if (input['days'] !== undefined && typeof input['days'] !== 'number') return false;\n if (input['limit'] !== undefined && typeof input['limit'] !== 'number') return false;\n return true;\n}\n\nfunction isKnowledgeClass(value: unknown): value is KnowledgeClass {\n return typeof value === 'string' && (KNOWLEDGE_CLASSES as readonly string[]).includes(value);\n}\n\nfunction isUpsertInput(value: unknown): value is UpsertInput {\n if (typeof value !== 'object' || value === null) return false;\n const input = value as Record<string, unknown>;\n return isKnowledgeClass(input['knowledge_class']) &&\n typeof input['source_path'] === 'string' &&\n typeof input['title'] === 'string' &&\n typeof input['content'] === 'string';\n}\n\nfunction isInvalidateInput(value: unknown): value is InvalidateInput {\n if (typeof value !== 'object' || value === null) return false;\n const input = value as Record<string, unknown>;\n return isKnowledgeClass(input['knowledge_class']) &&\n typeof input['source_path'] === 'string';\n}\n\nfunction isContextInput(value: unknown): value is ContextInput {\n if (typeof value !== 'object' || value === null) return false;\n const input = value as Record<string, unknown>;\n if (typeof input['query'] !== 'string') return false;\n if (input['limit'] !== undefined && typeof input['limit'] !== 'number') return false;\n if (input['knowledge_class'] !== undefined && !isKnowledgeClass(input['knowledge_class'])) return false;\n return true;\n}\n\nasync function getCloudAuth(fetchFn: FetchLike) {\n const controlPlaneUrl = process.env['VO_CONTROL_PLANE_URL']?.replace(/\\/+$/, '');\n if (!controlPlaneUrl) {\n return { ok: false as const, reason: 'VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled' };\n }\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) return { ok: false as const, reason: 'No auth configured. Run `vo-mcp login`.' };\n const token = await tokenSource.getToken();\n if (!token) return { ok: false as const, reason: 'Failed to obtain auth token. Run `vo-mcp login` again.' };\n return { ok: true as const, controlPlaneUrl, token };\n}\n\nasync function callPrivateKnowledge(\n path: string,\n body: unknown,\n fetchFn: FetchLike,\n) {\n const auth = await getCloudAuth(fetchFn);\n if (!auth.ok) return { ok: false, reason: auth.reason };\n const response = await fetchFn(`${auth.controlPlaneUrl}${path}`, {\n method: 'POST',\n headers: {\n authorization: `Bearer ${auth.token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n const text = await response.text();\n // A proxy 502/503 can carry an HTML body \u2014 surface it as a structured\n // failure rather than escaping as an uncaught SyntaxError.\n let parsed: unknown;\n try {\n parsed = text ? JSON.parse(text) as unknown : null;\n } catch {\n parsed = null;\n }\n if (response.status < 200 || response.status >= 300) {\n return { ok: false, status: response.status, response: parsed ?? text };\n }\n return parsed;\n}\n\nexport async function handlePrivateKnowledgeUpsert(\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 (!isUpsertInput(rawInput)) {\n throw invalidParams(UPSERT_TOOL_NAME, 'expected { knowledge_class, source_path, title, content }.');\n }\n const payload = await callPrivateKnowledge('/api/v1/knowledge/private', rawInput, fetchFn);\n const envelope: { tool: string; schema_version: number; payload: unknown; precision_note?: string } = {\n tool: UPSERT_TOOL_NAME,\n schema_version: 1,\n payload,\n };\n if (rawInput.content.length > PRECISION_CHAR_BUDGET) {\n // Additive optional field \u2014 schema_version stays 1 (adding optional fields\n // is allowed under the lane's schema-v1 lock; required shape unchanged).\n envelope.precision_note =\n `content is ${rawInput.content.length} chars (> ${PRECISION_CHAR_BUDGET}). ` +\n `Tight 1-3 page entries retrieve better \u2014 consider splitting into focused entries, ` +\n `then re-test retrieval via ${CONTEXT_TOOL_NAME}.`;\n }\n return jsonContent(envelope);\n}\n\nexport async function handlePrivateKnowledgeInvalidate(\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 (!isInvalidateInput(rawInput)) {\n throw invalidParams(INVALIDATE_TOOL_NAME, 'expected { knowledge_class, source_path }.');\n }\n const payload = await callPrivateKnowledge('/api/v1/knowledge/private/invalidate', rawInput, fetchFn);\n return jsonContent({ tool: INVALIDATE_TOOL_NAME, schema_version: 1, payload });\n}\n\nexport async function handlePrivateKnowledgeStale(\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 (!isStaleInput(rawInput)) {\n throw invalidParams(STALE_TOOL_NAME, 'expected { optional days, optional limit }.');\n }\n const auth = await getCloudAuth(fetchFn);\n if (!auth.ok) return jsonContent({ tool: STALE_TOOL_NAME, schema_version: 1, payload: { ok: false, reason: auth.reason } });\n const params = new URLSearchParams();\n if (rawInput?.days !== undefined) params.set('days', String(Math.trunc(rawInput.days)));\n if (rawInput?.limit !== undefined) params.set('limit', String(Math.trunc(rawInput.limit)));\n const qs = params.toString();\n const response = await fetchFn(`${auth.controlPlaneUrl}/api/v1/knowledge/private/stale${qs ? `?${qs}` : ''}`, {\n method: 'GET',\n headers: { authorization: `Bearer ${auth.token}` },\n });\n const text = await response.text();\n let parsed: unknown;\n try { parsed = text ? JSON.parse(text) as unknown : null; } catch { parsed = null; }\n const payload = response.status < 200 || response.status >= 300\n ? { ok: false, status: response.status, response: parsed ?? text }\n : parsed;\n return jsonContent({ tool: STALE_TOOL_NAME, schema_version: 1, payload });\n}\n\nexport async function handlePrivateKnowledgeContext(\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 (!isContextInput(rawInput)) {\n throw invalidParams(CONTEXT_TOOL_NAME, 'expected { query, optional limit, optional knowledge_class }.');\n }\n const payload = await callPrivateKnowledge('/api/v1/knowledge/private/context', rawInput, fetchFn);\n return jsonContent({ tool: CONTEXT_TOOL_NAME, schema_version: 1, payload });\n}\n", "import type { ToolDeps } from '../../types.js';\nimport { createAuthTokenSourceFromEnv } from '../../cloud/auth-token-source.js';\nimport { readStoredCredential } from '../../cloud/credential-store.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\n\nexport const POST_TOOL_NAME = 'hq_whiteboard_post';\nexport const READ_TOOL_NAME = 'hq_whiteboard_read';\n\nexport const postDescription =\n 'Post an append-only coordination note to the live AlgoHQ whiteboard. Uses the scoped credential from vo-mcp login; operator and tenant ownership are derived by the server.';\nexport const readDescription =\n 'Read recent coordination notes from the caller\\'s live AlgoHQ whiteboard. Uses the scoped credential from vo-mcp login and cannot widen tenant scope.';\n\nexport const postInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n from: { type: 'string', minLength: 1, maxLength: 100, description: 'Agent/session display name.' },\n type: { type: 'string', minLength: 1, maxLength: 64, description: 'Message kind, such as intent, worklog, blocker, or completion.' },\n content: { type: 'string', minLength: 1, maxLength: 500, description: 'Short coordination note.' },\n targetAgent: { type: 'string', maxLength: 100 },\n tester: { type: 'string', maxLength: 100 },\n tier: { type: 'string', maxLength: 32 },\n },\n required: ['from', 'type', 'content'],\n additionalProperties: false,\n};\n\nexport const readInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n limit: { type: 'integer', minimum: 1, maximum: 100, default: 25 },\n since: { type: 'string', description: 'Optional ISO-8601 lower bound.' },\n type: { type: 'string', minLength: 1, maxLength: 64 },\n },\n additionalProperties: false,\n};\n\ninterface PostInput {\n readonly from: string;\n readonly type: string;\n readonly content: string;\n readonly targetAgent?: string;\n readonly tester?: string;\n readonly tier?: string;\n}\n\ninterface ReadInput {\n readonly limit?: number;\n readonly since?: string;\n readonly type?: string;\n}\n\ntype FetchLike = typeof fetch;\n\nfunction resolveTimeoutMs(): number {\n const parsed = Number(process.env['HQ_WHITEBOARD_TIMEOUT_MS']);\n return Number.isFinite(parsed) && parsed >= 10 && parsed <= 120_000 ? parsed : 10_000;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction onlyKeys(value: Record<string, unknown>, allowed: readonly string[]): boolean {\n return Object.keys(value).every((key) => allowed.includes(key));\n}\n\nfunction isBoundedString(value: unknown, min: number, max: number): value is string {\n return typeof value === 'string' && value.trim().length >= min && value.trim().length <= max;\n}\n\nfunction parsePostInput(value: unknown): PostInput | null {\n if (!isRecord(value) || !onlyKeys(value, ['from', 'type', 'content', 'targetAgent', 'tester', 'tier'])) return null;\n if (!isBoundedString(value['from'], 1, 100)) return null;\n if (!isBoundedString(value['type'], 1, 64) || !/^[a-zA-Z0-9_-]+$/.test(value['type'].trim())) return null;\n if (!isBoundedString(value['content'], 1, 500)) return null;\n for (const [key, max] of [['targetAgent', 100], ['tester', 100], ['tier', 32]] as const) {\n if (value[key] !== undefined && !isBoundedString(value[key], 0, max)) return null;\n }\n return {\n from: value['from'].trim(),\n type: value['type'].trim(),\n content: value['content'].trim(),\n ...(typeof value['targetAgent'] === 'string' ? { targetAgent: value['targetAgent'].trim() } : {}),\n ...(typeof value['tester'] === 'string' ? { tester: value['tester'].trim() } : {}),\n ...(typeof value['tier'] === 'string' ? { tier: value['tier'].trim() } : {}),\n };\n}\n\nfunction parseReadInput(value: unknown): ReadInput | null {\n if (!isRecord(value) || !onlyKeys(value, ['limit', 'since', 'type'])) return null;\n if (value['limit'] !== undefined && (!Number.isInteger(value['limit']) || Number(value['limit']) < 1 || Number(value['limit']) > 100)) return null;\n if (value['since'] !== undefined && (typeof value['since'] !== 'string' || Number.isNaN(Date.parse(value['since'])))) return null;\n if (value['type'] !== undefined && !isBoundedString(value['type'], 1, 64)) return null;\n return {\n ...(typeof value['limit'] === 'number' ? { limit: value['limit'] } : {}),\n ...(typeof value['since'] === 'string' ? { since: value['since'] } : {}),\n ...(typeof value['type'] === 'string' ? { type: value['type'].trim() } : {}),\n };\n}\n\nasync function resolveCloud(fetchFn: FetchLike): Promise<{ url: string; token: string } | null> {\n const url = process.env['VO_CONTROL_PLANE_URL']?.trim().replace(/\\/$/, '');\n if (!url) return null;\n try {\n const source = createAuthTokenSourceFromEnv(process.env, fetchFn, () => readStoredCredential(process.env));\n const token = await source?.getToken();\n return token ? { url, token } : null;\n } catch {\n return null;\n }\n}\n\nasync function callWhiteboard(\n method: 'GET' | 'POST',\n bodyOrQuery: PostInput | ReadInput,\n signal?: AbortSignal,\n fetchFn: FetchLike = fetch,\n): Promise<unknown> {\n const cloud = await resolveCloud(fetchFn);\n if (!cloud) {\n return {\n ok: false,\n error: 'hq_whiteboard_not_configured',\n message: 'Set VO_CONTROL_PLANE_URL and run vo-mcp login to install a scoped HQ credential.',\n };\n }\n const timeoutSignal = AbortSignal.timeout(resolveTimeoutMs());\n const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;\n const query = new URLSearchParams();\n if (method === 'GET') {\n const input = bodyOrQuery as ReadInput;\n query.set('limit', String(input.limit ?? 25));\n if (input.since) query.set('since', input.since);\n if (input.type) query.set('type', input.type);\n }\n try {\n const response = await fetchFn(\n `${cloud.url}/api/v1/hq/whiteboard/messages${query.size ? `?${query}` : ''}`,\n {\n method,\n headers: {\n Authorization: `Bearer ${cloud.token}`,\n ...(method === 'POST' ? { 'Content-Type': 'application/json' } : {}),\n },\n ...(method === 'POST' ? { body: JSON.stringify(bodyOrQuery) } : {}),\n signal: requestSignal,\n },\n );\n const text = await response.text();\n let payload: unknown;\n try { payload = JSON.parse(text); } catch { payload = { ok: false, error: 'invalid_response', message: text.slice(0, 200) }; }\n if (!response.ok) {\n return { ok: false, error: 'hq_whiteboard_http_error', status: response.status, response: payload };\n }\n return payload;\n } catch (error) {\n return {\n ok: false,\n error: signal?.aborted\n ? 'cancelled'\n : timeoutSignal.aborted\n ? 'hq_whiteboard_timeout'\n : 'hq_whiteboard_unreachable',\n message: error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200),\n };\n }\n}\n\nexport async function handleHqWhiteboardPost(\n _deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<{ content: Array<{ type: 'text'; text: string }> }> {\n const input = parsePostInput(rawInput);\n if (!input) throw invalidParams(POST_TOOL_NAME, 'requires from, type, and 1-500 character content; unknown fields are rejected');\n return jsonContent(await callWhiteboard('POST', input, signal));\n}\n\nexport async function handleHqWhiteboardRead(\n _deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<{ content: Array<{ type: 'text'; text: string }> }> {\n const input = parseReadInput(rawInput);\n if (!input) throw invalidParams(READ_TOOL_NAME, 'limit must be 1-100, since must be ISO-8601, and unknown fields are rejected');\n return jsonContent(await callWhiteboard('GET', input, signal));\n}\n\nexport const __test = { callWhiteboard, parsePostInput, parseReadInput, resolveTimeoutMs };\n", "/**\n * Skill-corpus tools \u2014 cross-vendor access to the Algosuite skill library.\n *\n * Wires `@algosuite/skill-registry` (the vendor-neutral serializer over\n * `.claude/skills/<name>/SKILL.md`) into the MCP surface so every MCP\n * client \u2014 Claude Code, Codex, Cursor, Continue, Gemini \u2014 can discover\n * and load the SAME skill corpus Claude Code reads natively. Closes the\n * parity gap where the registry package had zero consumers (2026-07-20\n * cross-vendor parity audit) and skills were Claude-only.\n *\n * Two tools, not 28: registering every skill as its own MCP tool would\n * double the server's tool list and tax every session's context. The\n * catalog/get pair gives the same capability (discover \u2192 load) at a\n * fixed 2-tool cost. Per-skill tool registration (the registry's\n * `toMcpToolDefinitions` batch path) remains available for a future\n * opt-in mode if a client wants native per-skill tools.\n *\n * Corpus resolution order:\n * 1. `VO_SKILLS_DIR` env var (absolute path to a skills directory)\n * 2. Walk up from cwd looking for `.claude/skills` (max 8 levels)\n * Fail-open: when no corpus is found the tools stay registered and\n * return `corpus_available: false` with the reason \u2014 an installed\n * vo-mcp outside any repo must not crash `tools/list`.\n */\nimport { existsSync, statSync } from 'node:fs';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport {\n loadSkillsFromDir,\n type Skill,\n} from '@algosuite/skill-registry';\nimport type { ToolDeps } from '../../types.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\n\nexport const LIST_TOOL_NAME = 'vo_skill_list';\nexport const GET_TOOL_NAME = 'vo_skill_get';\n\nexport const listDescription =\n 'List the Algosuite skill corpus (name + trigger description for every skill). ' +\n 'Call once near session start to learn which skills exist; then fetch the full ' +\n 'instructions for a relevant skill with vo_skill_get. This is the same corpus ' +\n 'Claude Code loads natively from .claude/skills \u2014 served over MCP so every ' +\n 'vendor works from identical playbooks. Pass refresh:true to re-scan from disk.';\n\nexport const getDescription =\n 'Fetch the full markdown instructions of one Algosuite skill by name. ' +\n 'Follow the returned instructions for the current task the same way a ' +\n 'native skill invocation would. Use vo_skill_list to discover skill names.';\n\nexport const listInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n refresh: {\n type: 'boolean',\n description: 'Re-scan the skills directory instead of using the cached corpus.',\n },\n },\n required: [],\n};\n\nexport const getInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n description: 'Skill name exactly as returned by vo_skill_list.',\n },\n },\n required: ['name'],\n};\n\nconst MAX_WALK_UP_LEVELS = 8;\n\ninterface CorpusState {\n readonly skills: readonly Skill[];\n readonly skillsDir: string | null;\n /** Populated when the corpus could not be loaded; surfaced to callers. */\n readonly unavailableReason: string | null;\n}\n\nlet cachedCorpus: CorpusState | null = null;\n\n/** Locate a skills directory: env override first, then upward scan from cwd. */\nexport function resolveSkillsDir(\n env: NodeJS.ProcessEnv = process.env,\n startDir: string = process.cwd(),\n): string | null {\n const override = env.VO_SKILLS_DIR;\n if (typeof override === 'string' && override.length > 0) {\n const abs = isAbsolute(override) ? override : resolve(startDir, override);\n return existsSync(abs) && statSync(abs).isDirectory() ? abs : null;\n }\n let dir = resolve(startDir);\n for (let i = 0; i < MAX_WALK_UP_LEVELS; i += 1) {\n const candidate = join(dir, '.claude', 'skills');\n if (existsSync(candidate) && statSync(candidate).isDirectory()) return candidate;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nfunction loadCorpus(): CorpusState {\n const skillsDir = resolveSkillsDir();\n if (skillsDir === null) {\n return {\n skills: [],\n skillsDir: null,\n unavailableReason:\n 'No skills directory found. Set VO_SKILLS_DIR or run inside a repo with .claude/skills.',\n };\n }\n try {\n return { skills: loadSkillsFromDir(skillsDir), skillsDir, unavailableReason: null };\n } catch (err) {\n // Loader is fail-loud on malformed frontmatter by contract. Surface the\n // error to the caller instead of crashing the server \u2014 a broken skill\n // file must be visible, not fatal to every other tool on the server.\n const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);\n return { skills: [], skillsDir, unavailableReason: message };\n }\n}\n\nfunction getCorpus(refresh: boolean): CorpusState {\n if (refresh || cachedCorpus === null) {\n cachedCorpus = loadCorpus();\n }\n return cachedCorpus;\n}\n\n/** Test hook \u2014 drop the module-level corpus cache. */\nexport function resetCorpusCacheForTests(): void {\n cachedCorpus = null;\n}\n\nexport async function handleSkillList(\n _deps: ToolDeps,\n rawInput: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }> }> {\n const input = (rawInput ?? {}) as { refresh?: unknown };\n const refresh = input.refresh === true;\n const corpus = getCorpus(refresh);\n return jsonContent({\n corpus_available: corpus.unavailableReason === null,\n skills_dir: corpus.skillsDir,\n unavailable_reason: corpus.unavailableReason,\n skill_count: corpus.skills.length,\n skills: corpus.skills.map((s) => ({ name: s.name, description: s.description })),\n });\n}\n\nexport async function handleSkillGet(\n _deps: ToolDeps,\n rawInput: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }> }> {\n const input = (rawInput ?? {}) as { name?: unknown };\n if (typeof input.name !== 'string' || input.name.trim().length === 0) {\n throw invalidParams(GET_TOOL_NAME, 'input field \"name\" (non-empty string) is required');\n }\n const requested = input.name.trim();\n const corpus = getCorpus(false);\n if (corpus.unavailableReason !== null) {\n return jsonContent({\n corpus_available: false,\n unavailable_reason: corpus.unavailableReason,\n skill: null,\n });\n }\n const skill = corpus.skills.find((s) => s.name === requested);\n if (skill === undefined) {\n throw invalidParams(\n GET_TOOL_NAME,\n `unknown skill \"${requested}\". Known skills: ${corpus.skills.map((s) => s.name).join(', ')}`,\n );\n }\n return jsonContent({\n corpus_available: true,\n skill: {\n name: skill.name,\n description: skill.description,\n instructions: skill.body,\n source_path: skill.sourcePath,\n },\n });\n}\n", "/**\n * Skill loader \u2014 fs-scan a directory of skills + parse each SKILL.md.\n *\n * Expects the directory layout used by `.claude/skills/`:\n *\n * <skillsDir>/\n * <skill-a>/\n * SKILL.md \u2190 required, with YAML frontmatter\n * <skill-b>/\n * SKILL.md\n * README.md \u2190 ignored (no subdirectory)\n *\n * The frontmatter parser is intentionally minimal \u2014 accepts just\n * `name` and `description` fields. Adding fields is a V1 task and\n * should be done by extending `Skill` in types.ts first.\n */\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Skill } from './types.js';\n\n/** Thrown when a skill directory lacks a SKILL.md file. */\nexport class MissingSkillFileError extends Error {\n override readonly name = 'MissingSkillFileError';\n constructor(public readonly skillDir: string) {\n super(`Skill directory has no SKILL.md: ${skillDir}`);\n }\n}\n\n/** Thrown when a SKILL.md has malformed or absent frontmatter. */\nexport class InvalidSkillFrontmatterError extends Error {\n override readonly name = 'InvalidSkillFrontmatterError';\n constructor(public readonly skillFile: string, public readonly reason: string) {\n super(`Invalid frontmatter in ${skillFile}: ${reason}`);\n }\n}\n\nconst FRONTMATTER_DELIMITER = '---';\n\nfunction parseFrontmatter(rawInput: string, sourcePath: string): { name: string; description: string; body: string } {\n // Normalize line endings \u2014 SKILL.md files on Windows often have CRLF,\n // but the parser logic is LF-only. This is a tolerant normalization\n // that doesn't affect file content semantics.\n const raw = rawInput.replace(/\\r\\n/g, '\\n');\n if (!raw.startsWith(`${FRONTMATTER_DELIMITER}\\n`)) {\n throw new InvalidSkillFrontmatterError(sourcePath, 'file does not start with frontmatter delimiter \"---\"');\n }\n const afterFirst = raw.slice(FRONTMATTER_DELIMITER.length + 1);\n const closingIdx = afterFirst.indexOf(`\\n${FRONTMATTER_DELIMITER}\\n`);\n if (closingIdx === -1) {\n throw new InvalidSkillFrontmatterError(sourcePath, 'missing closing frontmatter delimiter \"---\"');\n }\n const frontmatterText = afterFirst.slice(0, closingIdx);\n const body = afterFirst.slice(closingIdx + `\\n${FRONTMATTER_DELIMITER}\\n`.length);\n\n let name = '';\n let description = '';\n for (const line of frontmatterText.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed.length === 0) continue;\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx === -1) continue;\n const key = trimmed.slice(0, colonIdx).trim();\n const value = trimmed.slice(colonIdx + 1).trim();\n if (key === 'name') name = value;\n else if (key === 'description') description = value;\n }\n if (name.length === 0) {\n throw new InvalidSkillFrontmatterError(sourcePath, 'missing required field \"name\"');\n }\n if (description.length === 0) {\n throw new InvalidSkillFrontmatterError(sourcePath, 'missing required field \"description\"');\n }\n return { name, description, body };\n}\n\n/**\n * Load and parse every SKILL.md in the given directory.\n *\n * Top-level files (e.g. README.md) and non-directory entries are\n * skipped. Subdirectories without SKILL.md are skipped silently\n * (allows scaffolding-in-progress without breaking the registry).\n *\n * Throws `InvalidSkillFrontmatterError` if ANY skill has malformed\n * frontmatter \u2014 the loader is fail-loud on bad data because skills\n * are surfaced to model panels and silent skips hide real bugs.\n */\nexport function loadSkillsFromDir(skillsDir: string): readonly Skill[] {\n const entries = readdirSync(skillsDir);\n const skills: Skill[] = [];\n for (const entry of entries) {\n const entryPath = join(skillsDir, entry);\n let stat;\n try {\n stat = statSync(entryPath);\n } catch {\n continue;\n }\n if (!stat.isDirectory()) continue;\n const skillFile = join(entryPath, 'SKILL.md');\n let raw: string;\n try {\n raw = readFileSync(skillFile, 'utf8');\n } catch {\n // Subdirectory without SKILL.md \u2014 silently skip\n continue;\n }\n const { name, description, body } = parseFrontmatter(raw, skillFile);\n skills.push({ name, description, body, sourcePath: skillFile });\n }\n // Stable sort by name for deterministic output.\n return [...skills].sort((a, b) => a.name.localeCompare(b.name));\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 * 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 { getVoMcpConsensusPanel } from './consensus-panel.js';\nimport {\n resolveAgreementGate,\n resolveMinResponders,\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 /** Per-call inference tokens, when the adapter reported usage (additive). */\n input_tokens?: number;\n output_tokens?: number;\n /** Exact attested upstream cost in micro-USD, when a receipt provided it. */\n cost_micro_usd?: number;\n error?: { category: string; message: string };\n }>;\n /** Engine-computed cumulative usage across EVERY round (B44-3). */\n token_usage?: {\n per_model_tokens_in: Readonly<Record<string, number>>;\n per_model_tokens_out: Readonly<Record<string, number>>;\n cost_micro_usd: number | null;\n rounds_counted: number;\n };\n degraded: boolean;\n /** Engine min_responders quorum \u2014 true when the verdict was floored to 'uncertain' below quorum. */\n quorum_failed?: 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 /** Critique-uptake visibility report (engine gates/critique-uptake.ts) \u2014 passed through opaquely. */\n critique_uptake?: Readonly<Record<string, unknown>>;\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 /** Quorum floor \u2014 below this many responders the verdict floors to 'uncertain'. */\n min_responders?: number;\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 // Quorum floor (engine min_responders). DEFAULT ON at 2 \u2014 a panel that\n // degrades to a single responder must never ship a badged verdict (the\n // engine's single-survivor >=0.8 agreement floor). Override with\n // VO_CONSENSUS_MIN_RESPONDERS=<n>; '0' disables entirely.\n const minResponders = resolveMinResponders(options.env);\n const engineOptions: {\n panel: readonly ConsensusModelAdapter[];\n per_model_timeout_ms?: number;\n agreement_gate?: EngineAgreementGateShape;\n shadow_synthesis?: { enabled: boolean };\n min_responders?: number;\n } = {\n panel,\n ...(options.per_model_timeout_ms !== undefined\n ? { per_model_timeout_ms: options.per_model_timeout_ms }\n : {}),\n ...(minResponders !== undefined ? { min_responders: minResponders } : {}),\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 ...(response.quorum_failed === true ? { quorum_failed: true } : {}),\n duration_ms: response.duration_ms,\n engine_version: response.engine_version,\n // Cumulative cross-round inference usage (B44-3). Absent when no panel\n // member reported usage; forwarded verbatim \u2014 the aggregator prefers it\n // over summing final-round verdicts (which under-reports deliberation).\n ...(response.token_usage !== undefined ? { token_usage: response.token_usage } : {}),\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 // Critique-uptake (2026-07-20 red-team fix) \u2014 verifier-critique\n // visibility report; previously computed by the engine on every\n // call but dropped at this boundary.\n ...(response.critique_uptake !== undefined\n ? { critique_uptake: response.critique_uptake }\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 per provider \u2014 the registered `voMcp` consensus panel.\n// Formalized out of inline literals here into `consensus-panel.ts` (roadmap\n// Track D / PR-T) so upstream model deprecations flow through the model-ops\n// codegen instead of silently breaking the panel. See that module's header\n// for why the panel is registered in-package rather than in functions-shared.\nconst DEFAULT_MODELS = getVoMcpConsensusPanel();\n\ntype ProviderKey = 'anthropic' | 'openai' | 'google';\n\ntype ConsensusModelCaller = (\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\ninterface EngineFactoryShape {\n createAdapter(\n providerId: ProviderKey,\n options: {\n readonly model: string;\n readonly caller: ConsensusModelCaller;\n readonly envSource?: Readonly<Record<string, string | undefined>>;\n },\n ): ConsensusModelAdapter;\n}\n\ninterface FunctionsSharedShape {\n callAnthropicWithMetrics: ConsensusModelCaller;\n callOpenAIWithMetrics: ConsensusModelCaller;\n callGeminiWithMetrics: ConsensusModelCaller;\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 // Restricted providers never join the local direct panel merely because a\n // key is present. DeepSeek and Muse use sanitized Model Firewall capsules.\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, ConsensusModelCaller>> = {\n anthropic: loaded.shared.callAnthropicWithMetrics,\n openai: loaded.shared.callOpenAIWithMetrics,\n google: loaded.shared.callGeminiWithMetrics,\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 };\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 /** Engine min_responders quorum \u2014 true when the verdict was floored to 'uncertain' below quorum. */\n readonly quorum_failed?: 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 * 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", "/** Muse Spark is restricted to sanitized Model Firewall task capsules. */\n\n// MODEL-OPS-INTENTIONAL: catalog identity retained for health and migration UI.\nexport const META_CONSENSUS_MODEL = 'muse-spark-1.1';\nexport const META_MODEL_API_KEY_ENV = 'MODEL_API_KEY';\nexport const META_MODEL_API_KEY_ALIAS = 'META_API';\n\ntype EnvSource = Readonly<Record<string, string | undefined>>;\ntype FetchImpl = typeof fetch;\n\nexport interface MetaModelCallerOptions {\n readonly fetchImpl?: FetchImpl;\n readonly envSource?: EnvSource;\n readonly reasoningEffort?: 'low' | 'medium' | 'high' | 'xhigh';\n}\n\nexport function createMetaModelCaller(options: MetaModelCallerOptions = {}) {\n void options;\n return async function callMetaWithMetrics(): Promise<never> {\n throw new Error(\n 'Muse Spark direct consensus is disabled. Use an explicit sanitized task capsule through the AlgoSuite Model Firewall.',\n );\n };\n}\n\nexport const callMetaWithMetrics = createMetaModelCaller();\n", "/**\n * vo-mcp consensus panel \u2014 the registered `voMcp` judgment panel.\n *\n * AlgoHQ roadmap Track D / PR-T (`docs/vo/vo-roadmap-2026-05-26.md` \u00A73.3) calls\n * for formalizing the previously-inline consensus model IDs in\n * `engine-client.ts` into a single registered panel so upstream model\n * deprecations can't silently break alpha customers.\n *\n * RED-LINE-safe location. The roadmap's literal wording pointed at\n * `functions-shared/src/shared-model-resolvers.ts::CONSENSUS_PANELS`, but ANY\n * non-test edit to `functions-shared/src/*` auto-escalates the deploy scope to\n * every function codebase (~24 codebases / ~1,132 instances / ~24h full\n * deploy \u2014 the operator's #1 operational RED LINE). No Cloud Function reads\n * this panel: vo-mcp is a local stdio MCP server (`node dist/cli.js`), never\n * deployed to Firebase. So the panel is registered HERE, colocated with its\n * only consumer, and protected by the SAME model-ops mechanism as the\n * functions-shared panels: an allowlist entry in\n * `scripts/ci/.model-pin-allowlist.json` marks this file as a MODEL_REWRITE\n * target, so the model-ops codegen rewrites these IDs on deprecation exactly\n * as it does for CONSENSUS_PANELS.\n *\n * Keyed by consensus-engine provider key (NOT persona), to match the adapter\n * construction path in `engine-client.ts` (`createAdapter(provider, ...)`).\n */\n\nimport { META_CONSENSUS_MODEL } from './meta-model-caller.js';\n\n/** Providers the vo-mcp consensus panel can draw from. */\nexport type VoMcpPanelProvider = 'anthropic' | 'openai' | 'google' | 'deepseek' | 'meta';\n\n/**\n * Registered default model IDs for the vo-mcp consensus panel, one per\n * provider. Callers may override any slot at construction time\n * (`EngineFromEnvOptions.models`); these are the fallbacks.\n */\nexport const VO_MCP_CONSENSUS_PANEL: Readonly<Record<VoMcpPanelProvider, string>> = {\n // claude-opus-5 (2026-07-24). Opus 4.7 was STRICTLY DOMINATED, not merely old:\n // Opus 5 is $5/$25 per MTok vs Opus 4.7's $15/$75 \u2014 a 3x cost cut on this slot,\n // corroborated by our own catalog (constants/pricing/sciencePricing.ts prices\n // claude-opus-5 at 0.010 vs claude-opus-4-7 at 0.030) \u2014 AND the same 2026-07-24\n // release note REMOVED fast mode from Opus 4.7 outright: `speed: \"fast\"` now\n // returns an error there rather than degrading, unlike the Opus 4.6 removal.\n // Verified served: GET /v1/models/claude-opus-5 -> HTTP 200 (2026-07-30).\n //\n // Claude-5 API safety checked before this swap: Opus 5 rejects `temperature` /\n // `top_p` / `top_k` and manual `thinking.budget_tokens` with HTTP 400. Neither\n // the consensus-engine Anthropic adapter nor functions-shared `callAnthropic`\n // sends any of them, and buildAdaptiveThinking emits `thinking: {type:'adaptive'}`\n // (the supported form) \u2014 so this swap cannot 400.\n anthropic: 'claude-opus-5',\n // gpt-5.6-terra (GA 2026-07-09; \u221220% price cut 2026-07-30). NOTE the real IDs\n // are tiered \u2014 `gpt-5.6-sol` / `-terra` / `-luna`; there is NO bare `gpt-5.6`\n // alias (verified against the served model list, 2026-07-30). Terra is the\n // cost/capability balance point and the right default for a judgment panel;\n // Sol is available if verdict quality ever needs it.\n openai: 'gpt-5.6-terra',\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 // Muse Spark identity is owned by meta-model-caller.ts (single source of\n // truth for the meta slot); re-exported here so the panel stays complete.\n meta: META_CONSENSUS_MODEL,\n} as const;\n\n/**\n * Return the vo-mcp consensus panel. Throws on any missing / blank model ID so\n * a bad model-ops codegen rewrite fails loud at module load instead of\n * producing a silently mis-routed adapter \u2014 the \"don't silently break alpha\n * customers\" guarantee PR-T exists to deliver. Mirrors `getConsensusPanel` in\n * `functions-shared/src/shared-model-resolvers.ts`.\n *\n * `panel` is injectable purely so the validation path is unit-testable; callers\n * should use the default.\n */\nexport function getVoMcpConsensusPanel(\n panel: Readonly<Record<VoMcpPanelProvider, string>> = VO_MCP_CONSENSUS_PANEL,\n): Readonly<Record<VoMcpPanelProvider, string>> {\n for (const [provider, modelId] of Object.entries(panel)) {\n if (typeof modelId !== 'string' || modelId.trim().length === 0) {\n throw new Error(\n `getVoMcpConsensusPanel: panel slot \"${provider}\" has a missing or blank model ID`,\n );\n }\n }\n return panel;\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\nexport const MIN_RESPONDERS_ENV_VAR = 'VO_CONSENSUS_MIN_RESPONDERS';\n\n/**\n * Quorum floor is opt-OUT (DEFAULT 2): a panel that degrades to a single\n * responder must never ship a badged verdict \u2014 the engine's strict-consensus\n * single-survivor path clamps confidence UP to >=0.8 while agreement metrics\n * vanish below 2 responders. Override with `VO_CONSENSUS_MIN_RESPONDERS=<n>`;\n * `0` (or any non-positive/invalid value) disables the floor entirely.\n * Returns undefined when disabled so the engine option is simply omitted.\n */\nexport function resolveMinResponders(\n env: Readonly<Record<string, string | undefined>> | undefined,\n): number | undefined {\n const raw = (env ?? {})[MIN_RESPONDERS_ENV_VAR];\n if (raw === undefined || raw.trim() === '') return 2;\n const parsed = Number.parseInt(raw.trim(), 10);\n if (!Number.isFinite(parsed) || parsed <= 0) return undefined;\n return parsed;\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", "/**\n * Cloud consensus client \u2014 repoints the consensus tools at the LIVE\n * `vo-moat-plane` `/api/v1/verify` endpoint (ADR-002 \"fat secret server, thin\n * client\"). Increment 2 of the AlgoHQ Command Center unification\n * (`docs/vo/vo-command-center-unification-spec-2026-06-05.md` \u00A75): removes the\n * 4 model API keys (`ANTHROPIC/OPENAI/GOOGLE/DEEPSEEK_API_KEY`) from the local\n * install \u2014 the local process no longer calls model providers directly.\n *\n * Only a SYNTHESIZED verdict crosses the wire (never prompts / thresholds /\n * panel composition \u2014 ADR-002), so `per_model_verdicts` is `[]` in cloud mode.\n * The consensus tools already tolerate that (`toEventPerModelVerdicts([])`,\n * `check-assertion-strength.ts` \"empty by design\"); the synthesized verdict\n * carries the result.\n *\n * FAIL-CLOSED: every unconfigured / non-2xx / malformed / network / aborted\n * path returns `{ ok: false, reason }` \u2014 NEVER a fake-green `pass`. Mirrors the\n * proven `cloud-run/vo-agent-executor/src/cloud/moat-client.ts`.\n */\nimport { randomUUID } from 'node:crypto';\nimport {\n CANCELLED_REASON,\n type ConsensusEngineClient,\n type ConsensusRequest,\n type ConsensusResult,\n} from './client.js';\n\n/** Default live moat endpoint (ADR-002 Inc 2). Override via `VO_MOAT_ENDPOINT`. */\nexport const DEFAULT_VO_MOAT_ENDPOINT = 'https://vo-moat-plane-620873201932.us-central1.run.app';\n\n/** Engine-version identifier stamped onto cloud verdicts (event-log grouping). */\nexport const MOAT_ENGINE_VERSION = 'moat-cloud-v1';\n\n/** Server-enforced excerpt cap (bytes); pre-checked client-side for a clear diagnostic. */\nexport const MOAT_EXCERPT_MAX_BYTES = 50_000;\n\n/** Stable cloud-mode unavailable reasons (pinned for event-log / dashboard grouping). */\nexport const MOAT_CLIENT_REASONS = {\n /** `available:false` from the moat (quota exhausted / <2 server-side keys / not entitled). */\n UNAVAILABLE: 'consensus-cloud-unavailable',\n /** Non-2xx HTTP response (status appended, e.g. `...:401`). */\n HTTP_ERROR: 'consensus-cloud-http-error',\n /** 2xx but the body is missing/invalid verdict fields (per the server contract). */\n MALFORMED: 'consensus-cloud-malformed-verdict',\n /** Network failure / local timeout (not the caller's cancellation). */\n NETWORK: 'consensus-cloud-network-error',\n /** Excerpt exceeds the server's 50KB cap \u2014 refused locally with a clear reason. */\n EXCERPT_TOO_LARGE: 'consensus-cloud-excerpt-too-large',\n} as const;\n\ntype FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface MoatConsensusClientOptions {\n readonly moatEndpoint: string;\n readonly entitlementToken: string;\n /** Local request timeout; default 15s. Independent of the caller's `signal`. */\n readonly timeoutMs?: number;\n /** Injectable fetch (tests); defaults to the global `fetch`. */\n readonly fetchFn?: FetchLike;\n}\n\n/** The wire verdict returned by `vo-moat-plane` `/api/v1/verify` (mirror of its contract). */\ninterface VerifyVerdict {\n readonly ok?: boolean;\n readonly available: boolean;\n readonly approved: boolean;\n readonly confidence?: number;\n readonly reason: string;\n readonly models_agreeing?: number;\n readonly degraded?: boolean;\n /** Moat decision ledger id (UUID) \u2014 surfaced to callers as the verification receipt. */\n readonly decision_id?: string;\n}\n\nfunction isVerdict(v: unknown): v is VerifyVerdict {\n if (!v || typeof v !== 'object') return false;\n const r = v as Record<string, unknown>;\n return (\n typeof r['available'] === 'boolean' &&\n typeof r['approved'] === 'boolean' &&\n // Contract: reason is a high-level rationale, min 10 chars. Reject empty/short\n // reasons as malformed (matches the proven vo-agent-executor client) so a verdict\n // never lands in the audit trail without a rationale.\n typeof r['reason'] === 'string' &&\n r['reason'].trim().length >= 10\n );\n}\n\n/** Clamp confidence to the contract's 0..1; non-finite/out-of-range \u21D2 0.5 (fail-safe). */\nfunction normalizeConfidence(raw: unknown): number {\n return typeof raw === 'number' && Number.isFinite(raw) && raw >= 0 && raw <= 1 ? raw : 0.5;\n}\n\n/** Accept models_agreeing only as a non-negative integer; anything else \u21D2 undefined. */\nfunction normalizeAgreeing(raw: unknown): number | undefined {\n return typeof raw === 'number' && Number.isInteger(raw) && raw >= 0 ? raw : undefined;\n}\n\n/**\n * Build a cloud-backed `ConsensusEngineClient`. Drop-in for the local engine\n * client \u2014 same interface, no model keys, verdicts computed server-side.\n */\nexport function createMoatConsensusClient(opts: MoatConsensusClientOptions): ConsensusEngineClient {\n const endpoint = opts.moatEndpoint.replace(/\\/+$/, '');\n const token = opts.entitlementToken;\n const timeoutMs = opts.timeoutMs ?? 15_000;\n const fetchFn: FetchLike = opts.fetchFn ?? ((input, init) => fetch(input, init));\n\n return {\n async run(request: ConsensusRequest): Promise<ConsensusResult> {\n // Honor the caller's cancellation up front.\n if (request.signal?.aborted) return { ok: false, reason: CANCELLED_REASON };\n\n // The server caps the excerpt at 50KB; refuse oversize locally with a clear\n // reason rather than burning a round-trip for an opaque 400/413.\n if (Buffer.byteLength(request.prompt, 'utf8') > MOAT_EXCERPT_MAX_BYTES) {\n return { ok: false, reason: MOAT_CLIENT_REASONS.EXCERPT_TOO_LARGE };\n }\n\n const startedAt = Date.now();\n const controller = new AbortController();\n const onAbort = (): void => controller.abort();\n request.signal?.addEventListener('abort', onAbort, { once: true });\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n const body = JSON.stringify({\n task_id: randomUUID(),\n gate_type: request.gate_type,\n excerpt: request.prompt,\n ...(request.system_prompt ? { question: request.system_prompt } : {}),\n ...(request.caller_context ? { context: request.caller_context } : {}),\n });\n\n try {\n const res = await fetchFn(`${endpoint}/api/v1/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n // SECURITY: entitlement token \u2014 never logged.\n Authorization: `Bearer ${token}`,\n },\n body,\n signal: controller.signal,\n });\n\n if (!res.ok) {\n return { ok: false, reason: `${MOAT_CLIENT_REASONS.HTTP_ERROR}:${res.status}` };\n }\n\n const parsed: unknown = await res.json().catch(() => null);\n if (!isVerdict(parsed)) {\n return { ok: false, reason: MOAT_CLIENT_REASONS.MALFORMED };\n }\n\n // available:false \u21D2 quota exhausted / not entitled / <2 server keys.\n // Map to the engine's \"unavailable\" envelope so the tools fall back\n // exactly as they do for a missing local engine.\n if (!parsed.available) {\n return { ok: false, reason: parsed.reason || MOAT_CLIENT_REASONS.UNAVAILABLE };\n }\n\n const agreeing = normalizeAgreeing(parsed.models_agreeing);\n const reasoning =\n agreeing !== undefined ? `${parsed.reason} (${agreeing} models agreeing)` : parsed.reason;\n\n const receiptId = typeof parsed.decision_id === 'string' && parsed.decision_id.trim().length > 0\n ? parsed.decision_id.trim().slice(0, 120)\n : null;\n return {\n ok: true,\n ...(receiptId ? { receipt_id: receiptId } : {}),\n synthesized_verdict: {\n verdict: parsed.approved ? 'pass' : 'fail',\n confidence: normalizeConfidence(parsed.confidence),\n reasoning_excerpt: reasoning,\n synthesizer: 'cloud-moat',\n dissent_summary: null,\n },\n // Per-model detail stays server-side by design (ADR-002).\n per_model_verdicts: [],\n degraded: parsed.degraded === true,\n duration_ms: Date.now() - startedAt,\n engine_version: MOAT_ENGINE_VERSION,\n };\n } catch (err) {\n // Distinguish the caller's cancellation from a local timeout / network error.\n if (request.signal?.aborted) return { ok: false, reason: CANCELLED_REASON };\n if (err instanceof Error && err.name === 'AbortError') {\n return { ok: false, reason: `${MOAT_CLIENT_REASONS.NETWORK}:timeout` };\n }\n return { ok: false, reason: MOAT_CLIENT_REASONS.NETWORK };\n } finally {\n clearTimeout(timeoutId);\n request.signal?.removeEventListener('abort', onAbort);\n }\n },\n };\n}\n\n/**\n * Factory: returns a cloud consensus client when `VO_ENTITLEMENT_TOKEN` is set\n * (endpoint defaults to the live moat), else `null` so the caller falls back to\n * the local engine. The token's PRESENCE is the cloud-mode switch \u2014 identical\n * to `vo-agent-executor` + the control-plane gate, so a user who sets the token\n * (and drops the 4 model keys) gets cloud consensus with zero other config.\n */\nexport function tryCreateMoatConsensusClientFromEnv(\n env: Readonly<Record<string, string | undefined>> = process.env,\n fetchFn?: FetchLike,\n): ConsensusEngineClient | null {\n const token = env['VO_ENTITLEMENT_TOKEN']?.trim();\n if (!token) return null;\n const endpoint = env['VO_MOAT_ENDPOINT']?.trim() || DEFAULT_VO_MOAT_ENDPOINT;\n const timeoutRaw = env['VO_MOAT_TIMEOUT_MS'];\n const timeoutMs =\n timeoutRaw && Number.isFinite(Number(timeoutRaw)) && Number(timeoutRaw) > 0\n ? Number(timeoutRaw)\n : undefined;\n return createMoatConsensusClient({\n moatEndpoint: endpoint,\n entitlementToken: token,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n ...(fetchFn ? { fetchFn } : {}),\n });\n}\n", "/**\n * Consensus engine client interface \u2014 the boundary between the open\n * MCP server and the closed `@algosuite/consensus-engine` package.\n *\n * Per handoff \u00A7C-1 (\"open shell, closed content\"):\n * - This file defines the SHAPE the open shell consumes.\n * - `null-client.ts` provides a \"not configured\" implementation that\n * returns the unimplemented envelope shape; used when credentials\n * are absent or the engine is intentionally disabled.\n * - Production wires in a real client that calls `runConsensus` from\n * `@algosuite/consensus-engine`.\n *\n * Same shape as the ratchets boundary \u2014 narrow public interface, full\n * implementation lives in the closed package.\n */\n\n/** Verdict emitted by the consensus engine. */\nexport type ConsensusVerdict = 'pass' | 'fail' | 'uncertain';\n\nexport interface ConsensusPerModelVerdict {\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 /**\n * Per-call inference tokens, forwarded by value from the engine verdict when\n * the adapter reported usage (additive; absent on mocks/usage-less callers and\n * on the error path). Aggregated into the telemetry event's per_model_tokens\n * maps \u2014 the raw fuel for the cost-instrumentation dataset (Base1 lesson).\n */\n readonly input_tokens?: number;\n readonly output_tokens?: number;\n /** Exact attested upstream cost (micro-USD) when a broker receipt provided it. */\n readonly cost_micro_usd?: number;\n readonly error?: {\n readonly category: string;\n readonly message: string;\n };\n}\n\n/**\n * Discrete calibrated-confidence badge (engine Feature 2; arXiv:2606.13591).\n * Mirrors the engine's `ConfidenceBadge`. Surfaced so UI consumers can render\n * the band directly (UI-trust \"Confidence\" pillar) without re-deriving it.\n */\nexport type ConsensusConfidenceBadge = 'high' | 'medium' | 'low';\n\nexport interface ConsensusSynthesizedVerdict {\n readonly verdict: ConsensusVerdict;\n readonly confidence: number;\n readonly reasoning_excerpt: string;\n readonly synthesizer: string;\n readonly dissent_summary: string | null;\n /**\n * Engine Feature 2 (calibrated-confidence) \u2014 ADDITIVE cross-model calibrated\n * confidence. Does NOT replace `confidence` (which stays the synthesizer's\n * own value). Present whenever the engine's confidence-aggregation layer ran.\n * For the `runConsensus` path the engine attaches this UNCONDITIONALLY, so\n * VO gates get it ON by default (it only ADDS a field, never changes a verdict).\n */\n readonly calibrated_confidence?: number;\n /** Engine Feature 2 \u2014 discrete badge derived from `calibrated_confidence`. Present iff that is. */\n readonly confidence_badge?: ConsensusConfidenceBadge;\n}\n\n/**\n * Engine Feature 1 (agreement-gate) \u2014 fan-out cost accounting. Pure telemetry;\n * never alters the verdict. Present only when the agreement-gated early-exit\n * fan-out ran for this call. Mirrors the engine's `FanOutDiagnostics`.\n */\nexport interface ConsensusFanOutDiagnostics {\n readonly models_called: number;\n readonly panel_size: number;\n readonly early_exit: boolean;\n readonly refused: number;\n}\n\n/**\n * Stage A7-shadow comparison \u2014 the ADAPTIVE verdict (log-odds pooling, skill-weighted,\n * optionally calibrated) computed ALONGSIDE the live incumbent verdict, for grading.\n * PII-free: verdict labels + confidences only (no prompt, no model reasoning). Present\n * iff shadow mode is enabled. Pure observability \u2014 the live verdict is never affected.\n */\nexport interface ConsensusShadowSynthesis {\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 * Engine source-grounded Feature 1 (citation-grader) \u2014 deterministic Tier-4\n * citation / governed-fact grade. Mirrors the engine's `CitationGradeResult`,\n * projected to the open shell. Present only on the source-grounded path with\n * the grader enabled.\n */\nexport interface ConsensusCitationGrade {\n /** Gate decision \u2014 false means at least one claim was unsupported or low-confidence. */\n readonly ok: boolean;\n readonly unsupported_count: number;\n readonly uncertain_count: number;\n /** Per-claim grading rows (claim text, label, confidence). */\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/**\n * Behaviour-changing source-grounded feature toggles. ALL default OFF so the\n * production gate behaviour is unchanged until the operator opts in.\n */\nexport interface ConsensusSourceGroundedConfig {\n /**\n * Engine source-grounded Feature 1 \u2014 deterministic citation grader (Tier-4).\n * BEHAVIOUR-CHANGING: a failing grade raises an escalation signal. Default OFF.\n *\n * Requires an injected `ClaimClassifier` (a cheap model in prod, a fake in\n * tests). No cheap engine-backed classifier is auto-constructed \u2014 the engine's\n * `createAdapter` produces verdict-style adapters, not claim classifiers \u2014 so\n * this stays OFF until an operator/caller injects one. See engine-client.ts.\n */\n readonly citation_grader?: {\n readonly enabled: boolean;\n /**\n * Injected semantic grader (engine `ClaimClassifier` shape). MUST NOT throw.\n * OPTIONAL: when omitted (and `enabled === true`) the engine-client supplies\n * a real DEFAULT classifier built from its own panel adapter\n * (`createClaimClassifier` over the first live model) \u2014 so a caller that has\n * no classifier of its own (e.g. the `vo_consensus_judgment` tool) can still\n * turn grading on. A caller MAY inject its own to override the default.\n */\n readonly classifier?: ClaimClassifierShape;\n /** Uncertain-claim escalation floor; default 0.85 in the engine. */\n readonly escalate_below?: number;\n };\n /** Optional per-call URL allowlist function (engine `SourceUrlAllowFn`). */\n readonly allow_url?: (url: string) => boolean;\n}\n\n/**\n * Structural mirror of the engine's `ClaimClassifier`. Declared here so the\n * open shell can accept an injected classifier without importing the closed\n * engine's types. MUST NOT throw \u2014 return `{ label:'uncertain', confidence:0 }`.\n */\nexport interface ClaimClassifierShape {\n classify(args: {\n readonly claim: string;\n readonly sources: string;\n }): Promise<{ readonly label: 'supported' | 'unsupported' | 'uncertain'; readonly confidence: number }>;\n}\n\nexport interface ConsensusRequest {\n readonly gate_type: string;\n readonly prompt: string;\n readonly system_prompt?: string;\n readonly caller_context?: Record<string, unknown>;\n /**\n * Cancellation signal. When the SDK receives `notifications/cancelled`\n * for the request, this fires; the engine client races the engine call\n * against this signal and returns `{ ok: false, reason: 'cancelled' }`\n * promptly. Per-model API calls also receive the signal via the\n * wrapped adapter panel so in-flight fetch() calls are aborted at the\n * provider level. Added 2026-05-25 (audit HIGH \u2014 MCP cancellation).\n */\n readonly signal?: AbortSignal;\n /**\n * Optional source URLs. When NON-EMPTY, the engine-client routes the call to\n * the engine's source-grounded path (`runSourceGroundedConsensus`) instead of\n * the plain `runConsensus` fan-out, which activates the Tier-4 source-grounded\n * features (retrieval-confidence flagging + optional citation grading). When\n * absent/empty the call stays on the unchanged `runConsensus` path.\n *\n * NOTE: the `vo_consensus_judgment` tool does not currently populate this \u2014\n * the source-grounded features are wired-but-dormant until a caller passes\n * sources. See engine-client.ts and the dispatch report.\n */\n readonly source_urls?: readonly string[];\n /** Behaviour-changing source-grounded toggles (citation grader, allowlist). All default OFF. */\n readonly source_grounded?: ConsensusSourceGroundedConfig;\n}\n\n/** Stable reason string emitted when a consensus run is aborted via the signal. */\nexport const CANCELLED_REASON = 'cancelled';\n\nexport interface ConsensusSuccess {\n readonly ok: true;\n readonly synthesized_verdict: ConsensusSynthesizedVerdict;\n /**\n * The verification receipt (2026-08-15): the moat plane's `decision_id`\n * (UUID) for a cloud-verified call. This is what an agent pastes into its\n * PR body as `receipt id: <uuid>` and what the runner persists as the\n * task's `consensus_receipt_id` \u2014 the outcome ledger's proof that the\n * consensus stage actually ran. Absent on local-engine and test runs.\n */\n readonly receipt_id?: string;\n readonly per_model_verdicts: readonly ConsensusPerModelVerdict[];\n /**\n * CUMULATIVE inference usage across EVERY round of the call (engine-computed).\n * `per_model_verdicts` carries only the final round, so on a deliberating call\n * this is the honest total. Preferred by the telemetry aggregator when present.\n */\n readonly token_usage?: {\n readonly per_model_tokens_in: Readonly<Record<string, number>>;\n readonly per_model_tokens_out: Readonly<Record<string, number>>;\n readonly cost_micro_usd: number | null;\n readonly rounds_counted: number;\n };\n readonly degraded: boolean;\n /** Engine min_responders quorum \u2014 true when the verdict was floored to 'uncertain' below quorum. */\n readonly quorum_failed?: boolean;\n readonly duration_ms: number;\n readonly engine_version: string;\n /**\n * Phase 2 Lane D-1 \u2014 additive escalation signal from the `human-tiebreak`\n * synthesizer. Optional so the existing strict-consensus / majority-vote\n * paths stay unchanged. When present and `required: true`, surfaces a\n * human-review recommendation to the caller (e.g. `vo_architecture_review`).\n */\n readonly escalation_required?: boolean;\n readonly escalation_reason?: string;\n /**\n * Engine Feature 1 (agreement-gate) \u2014 fan-out cost accounting. Present only\n * when the agreement-gated early-exit fan-out ran for this call (opt-in via\n * `VO_CONSENSUS_AGREEMENT_GATE`). Pure telemetry; never alters the verdict.\n */\n readonly fan_out_diagnostics?: ConsensusFanOutDiagnostics;\n /**\n * Engine source-grounded Feature 1 (citation-grader) \u2014 present only on the\n * source-grounded path with the grader enabled. `ok === false` means the\n * answer's claims were not all supported by their cited sources.\n */\n readonly citation_grade?: ConsensusCitationGrade;\n /**\n * Engine source-grounded Feature 2 (retrieval-confidence) \u2014 1-based ordinals\n * of fetched sources the retrieval-confidence heuristic flagged\n * `low_confidence`. Present only on the source-grounded path. Empty array\n * means all retrievals were high-confidence.\n */\n readonly low_confidence_sources?: readonly number[];\n /** True when the source-grounded path ran (sources were provided). Absent on the plain path. */\n readonly source_grounded?: boolean;\n /**\n * Stage A7-shadow \u2014 the adaptive-vs-incumbent comparison (PII-free). Present when\n * shadow mode is enabled (default ON for VO; kill via `VO_CONSENSUS_SHADOW=0`).\n * Pure observability for grading the adaptive path; never alters the live verdict.\n */\n readonly shadow_synthesis?: ConsensusShadowSynthesis;\n /**\n * Critique-uptake visibility report \u2014 passed through from the engine so\n * callers can see whether verifier critiques changed the verdict or were\n * silently outvoted. Pure observability; never alters the live verdict.\n */\n readonly critique_uptake?: Readonly<Record<string, unknown>>;\n}\n\nexport interface ConsensusUnavailable {\n readonly ok: false;\n readonly reason: string;\n}\n\nexport type ConsensusResult = ConsensusSuccess | ConsensusUnavailable;\n\nexport interface ConsensusEngineClient {\n /** Returns `{ ok: false }` when the engine cannot run (no credentials, no panel, etc). */\n run(request: ConsensusRequest): Promise<ConsensusResult>;\n}\n", "import {\n CANCELLED_REASON,\n type ConsensusEngineClient,\n type ConsensusRequest,\n type ConsensusResult,\n} from './client.js';\n\nexport interface ConsensusFallbackOptions {\n readonly onFallback?: (reason: string) => void;\n}\n\nconst MIN_VALID_LOCAL_VERDICTS = 2;\nconst INSUFFICIENT_LOCAL_VERDICTS_REASON = 'local-panel-insufficient-valid-verdicts';\n\n/** Try the user's local panel first, then the paid cloud moat if local consensus is unavailable. */\nexport function createConsensusFallbackClient(\n primary: ConsensusEngineClient,\n fallback: ConsensusEngineClient,\n options: ConsensusFallbackOptions = {},\n): ConsensusEngineClient {\n return {\n async run(request: ConsensusRequest): Promise<ConsensusResult> {\n const primaryResult = await primary.run(request);\n if (request.signal?.aborted || (!primaryResult.ok && primaryResult.reason === CANCELLED_REASON)) {\n return primaryResult;\n }\n if (primaryResult.ok) {\n const validVerdicts = primaryResult.per_model_verdicts.filter(\n (verdict) => verdict.verdict !== 'error',\n );\n if (validVerdicts.length >= MIN_VALID_LOCAL_VERDICTS) return primaryResult;\n options.onFallback?.(INSUFFICIENT_LOCAL_VERDICTS_REASON);\n return fallback.run(request);\n }\n options.onFallback?.(primaryResult.reason);\n return fallback.run(request);\n },\n };\n}\n", "import { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\nconst KEY_SERVICE = 'algosuite-vo';\n\ninterface KeyringEntry {\n getPassword(): string | null;\n}\n\ntype KeyringEntryCtor = new (service: string, account: string) => KeyringEntry;\n\n/** Keychain account `vo-mcp set-key --provider moat` writes (agent-key-store `accountFor('moat')`). */\nexport const MOAT_ENTITLEMENT_KEYCHAIN_ACCOUNT = 'moat-api-key';\n\nconst KEYCHAIN_TARGETS = [\n { account: 'anthropic-api-key', envVar: 'ANTHROPIC_API_KEY' },\n { account: 'openai-api-key', envVar: 'OPENAI_API_KEY' },\n { account: 'meta-api-key', envVar: 'MODEL_API_KEY' },\n // The cloud-consensus entitlement (ADR-002 moat). Read here so an npm-installed\n // runner \u2014 where the local engine package never exists \u2014 still has a working\n // consensus path without an operator plumbing a secret into the daemon's env.\n { account: MOAT_ENTITLEMENT_KEYCHAIN_ACCOUNT, envVar: 'VO_ENTITLEMENT_TOKEN' },\n] as const;\n\nfunction loadEntryCtor(): KeyringEntryCtor | null {\n try {\n return (require('@napi-rs/keyring') as { Entry?: KeyringEntryCtor }).Entry ?? null;\n } catch {\n return null;\n }\n}\n\nfunction readKey(EntryCtor: KeyringEntryCtor, account: string): string | null {\n try {\n return new EntryCtor(KEY_SERVICE, account).getPassword()?.trim() || null;\n } catch {\n return null;\n }\n}\n\nexport interface LocalCredentialEnvOptions {\n readonly EntryCtor?: KeyringEntryCtor | null;\n}\n\n/**\n * Hydrate a fresh env view from credentials stored by `vo-mcp set-key`.\n * Explicit env vars always win and key material never leaves this process.\n */\nexport function withLocalConsensusCredentials(\n baseEnv: Readonly<Record<string, string | undefined>> = process.env,\n options: LocalCredentialEnvOptions = {},\n): Readonly<Record<string, string | undefined>> {\n const env = { ...baseEnv };\n if (!env.OPENAI_API_KEY?.trim() && env.CODEX_API_KEY?.trim()) {\n env.OPENAI_API_KEY = env.CODEX_API_KEY;\n }\n const EntryCtor = options.EntryCtor === undefined ? loadEntryCtor() : options.EntryCtor;\n if (!EntryCtor) return env;\n\n for (const target of KEYCHAIN_TARGETS) {\n if (env[target.envVar]?.trim()) continue;\n const key = readKey(EntryCtor, target.account);\n if (key) env[target.envVar] = key;\n }\n return env;\n}\n", "/**\n * `vo-mcp login` \u2014 the thin-client loopback browser-callback flow (Increment 3b,\n * Option A; `docs/vo/vo-command-center-inc3b-login-design-2026-06-05.md`).\n *\n * The standard CLI-auth pattern (`gcloud auth login`, `gh auth login`): start a\n * loopback HTTP server on 127.0.0.1, open the browser to the dashboard's\n * `/cli-login` page (which runs the live Firebase Google/GitHub SSO), and capture\n * the user's refresh token when the page redirects back to the loopback. The\n * captured token (the user's OWN credential \u2014 never the god-token, never model\n * keys) is stored locally; Inc 3a's source auto-refreshes ID tokens from it.\n *\n * FROZEN loopback contract v1 (the dashboard `/cli-login` page builds against this):\n * CLI \u2192 browser: <dashboard>/cli-login?port=<loopbackPort>&state=<csrf>\n * page \u2192 browser: redirect to http://127.0.0.1:<port>/callback#state=..&refresh_token=..&api_key=..&email=..\n * (data in the URL FRAGMENT \u2014 never sent to a server log; the loopback /callback\n * page reads the fragment and POSTs it same-origin to /capture as JSON.)\n * CLI /capture: validate `state`, store {refresh_token, api_key, email}, 200, close.\n */\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { randomBytes } from 'node:crypto';\nimport { spawn } from 'node:child_process';\nimport { writeStoredCredential, type StoredCredential } from './credential-store.js';\n\n// The /cli-login page lives in the MAIN app (algosuite.ai) per the 2026-06-08\n// surface pivot (unification-spec \u00A711) \u2014 the standalone vo-dashboard never\n// shipped the page and its deploy lane is operator-dispatch-only. Override with\n// VO_DASHBOARD_URL (env) or opts.dashboardUrl.\nexport const DEFAULT_DASHBOARD_URL = 'https://algosuite.ai';\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst MAX_BODY_BYTES = 16_384;\n\nexport interface LoginResult {\n readonly email?: string;\n readonly credentialPath: string;\n}\n\nexport interface CaptureOutcome {\n readonly ok: boolean;\n readonly httpStatus: number;\n readonly result?: LoginResult;\n readonly error?: string;\n /**\n * The captured Firebase refresh credential (Inc 3b.4b). The caller may exchange\n * it for a scoped `vocred_` and re-store, dropping the raw refresh token.\n */\n readonly captured?: { readonly refresh_token: string; readonly api_key: string; readonly email?: string };\n}\n\n/**\n * Pure core of the /capture handler \u2014 validate the CSRF state + required fields,\n * then persist via the injected `store`. Fully unit-testable; the http server is\n * a thin shell around this.\n */\nexport function processCapture(\n rawBody: string,\n expectedState: string,\n store: (cred: StoredCredential) => string,\n): CaptureOutcome {\n let data: { state?: unknown; refresh_token?: unknown; api_key?: unknown; email?: unknown };\n try {\n data = JSON.parse(rawBody);\n } catch {\n return { ok: false, httpStatus: 400, error: 'invalid JSON body' };\n }\n if (!data || typeof data !== 'object') return { ok: false, httpStatus: 400, error: 'invalid body' };\n // CSRF: the state echoed by the browser MUST match the one we generated.\n if (typeof data.state !== 'string' || data.state !== expectedState) {\n return { ok: false, httpStatus: 403, error: 'state mismatch (possible CSRF) \u2014 login aborted' };\n }\n const refresh = typeof data.refresh_token === 'string' ? data.refresh_token.trim() : '';\n const apiKey = typeof data.api_key === 'string' ? data.api_key.trim() : '';\n if (!refresh || !apiKey) {\n return { ok: false, httpStatus: 400, error: 'login response missing refresh_token / api_key' };\n }\n const email = typeof data.email === 'string' && data.email.trim() ? data.email.trim() : undefined;\n const path = store({ refresh_token: refresh, api_key: apiKey, ...(email ? { email } : {}) });\n return {\n ok: true,\n httpStatus: 200,\n result: { ...(email ? { email } : {}), credentialPath: path },\n captured: { refresh_token: refresh, api_key: apiKey, ...(email ? { email } : {}) },\n };\n}\n\n/** The tiny page served at /callback: read the fragment, POST it same-origin to /capture. */\nfunction captureHtml(): string {\n return `<!doctype html><html><head><meta charset=\"utf-8\"><title>AlgoHQ login</title></head>\n<body style=\"font-family:system-ui;max-width:32rem;margin:4rem auto;text-align:center\">\n<h2 id=\"m\">Completing sign-in\u2026</h2>\n<script>\n(function(){\n var h=location.hash.replace(/^#/,''), p=new URLSearchParams(h), b={};\n ['state','refresh_token','api_key','email'].forEach(function(k){ if(p.get(k)) b[k]=p.get(k); });\n fetch('/capture',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(b)})\n .then(function(r){ document.getElementById('m').textContent = r.ok ? 'Sign-in complete \u2014 you can close this tab.' : 'Sign-in failed \u2014 check the terminal.'; })\n .catch(function(){ document.getElementById('m').textContent = 'Sign-in failed \u2014 check the terminal.'; });\n})();\n</script></body></html>`;\n}\n\n/** Cross-platform browser open (best-effort; the URL is also printed for manual open). */\nexport function defaultOpenBrowser(url: string): void {\n const platform = process.platform;\n if (platform === 'win32') {\n // NOT `cmd /c start`: cmd re-parses the joined command line, so an\n // unquoted `&` truncates the URL at the first query separator \u2014 the\n // browser opened with `?port=` but no `state`, and the CLI login page\n // correctly rejected the pair (observed live, 2026-07-21). rundll32's\n // FileProtocolHandler passes the URL to the default browser with no\n // shell parsing, so `&` survives intact.\n spawn('rundll32', ['url.dll,FileProtocolHandler', url], { detached: true, stdio: 'ignore' }).unref();\n } else if (platform === 'darwin') {\n spawn('open', [url], { detached: true, stdio: 'ignore' }).unref();\n } else {\n spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref();\n }\n}\n\nexport interface RunLoginOptions {\n readonly dashboardUrl?: string;\n readonly timeoutMs?: number;\n readonly env?: Readonly<Record<string, string | undefined>>;\n readonly openBrowser?: (url: string) => void;\n readonly log?: (msg: string) => void;\n readonly nowIso?: () => string;\n /**\n * Inc 3b.4b: exchange the captured Firebase refresh credential for a scoped\n * `vocred_`. When provided AND it succeeds, the vocred_ is stored INSTEAD of the\n * raw refresh token (spec \u00A76). Fail-open: a null/throwing exchange keeps the\n * refresh credential. Production wires `exchangeForVoCredential` when a\n * `VO_CONTROL_PLANE_URL` is configured.\n */\n readonly exchange?: (\n refreshToken: string,\n apiKey: string,\n label?: string,\n ) => Promise<{ vo_credential: string; expires_at: string } | null>;\n}\n\n/**\n * Run the interactive loopback login. Resolves with the stored credential path on\n * success; rejects on timeout / CSRF mismatch / malformed response. FAIL-CLOSED:\n * nothing is stored unless the state matches and the fields are present.\n */\nexport async function runLogin(opts: RunLoginOptions = {}): Promise<LoginResult> {\n const env = opts.env ?? process.env;\n const dashboardUrl = (opts.dashboardUrl ?? env['VO_DASHBOARD_URL']?.trim() ?? DEFAULT_DASHBOARD_URL)\n .replace(/\\/+$/, '') || DEFAULT_DASHBOARD_URL;\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const log = opts.log ?? ((m: string) => console.error(m));\n const nowIso = opts.nowIso ?? (() => new Date().toISOString());\n const openBrowser = opts.openBrowser ?? defaultOpenBrowser;\n const state = randomBytes(32).toString('base64url');\n\n return new Promise<LoginResult>((resolve, reject) => {\n let settled = false;\n const finish = (err: Error | null, result?: LoginResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n server.close();\n if (err) reject(err);\n else resolve(result as LoginResult);\n };\n\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url ?? '/', 'http://127.0.0.1');\n if (req.method === 'GET' && url.pathname === '/callback') {\n res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });\n res.end(captureHtml());\n return;\n }\n if (req.method === 'POST' && url.pathname === '/capture') {\n let body = '';\n req.on('data', (chunk: Buffer) => {\n body += chunk.toString('utf8');\n if (body.length > MAX_BODY_BYTES) req.destroy();\n });\n req.on('end', () => {\n void (async () => {\n // Guard against a dribble that crossed the cap before destroy() took effect.\n if (body.length > MAX_BODY_BYTES) {\n res.writeHead(413, { 'content-type': 'text/plain' });\n res.end('payload too large');\n finish(new Error('login request body exceeded the size cap'));\n return;\n }\n // Inc 3b.4b: when an exchange is configured, VALIDATE without writing the\n // raw refresh token first \u2014 then write exactly ONE credential (the\n // vocred_ on success, else the refresh on fail-open). This avoids a\n // window where the raw Firebase refresh token persists on disk during\n // the exchange round-trip (\u00A76: the raw token must not persist at all).\n const writeNow = (cred: StoredCredential): string => writeStoredCredential(cred, nowIso(), env);\n const outcome = processCapture(body, state, opts.exchange ? () => 'pending' : writeNow);\n let result = outcome.result;\n if (outcome.ok && outcome.captured && opts.exchange) {\n const capt = outcome.captured;\n let cred: StoredCredential = {\n refresh_token: capt.refresh_token,\n api_key: capt.api_key,\n ...(capt.email ? { email: capt.email } : {}),\n };\n try {\n const voc = await opts.exchange(capt.refresh_token, capt.api_key);\n if (voc && voc.vo_credential) {\n // Store ONLY the vocred_ \u2014 the raw refresh token never lands on disk.\n cred = {\n vo_credential: voc.vo_credential,\n vo_credential_expires_at: voc.expires_at,\n ...(capt.email ? { email: capt.email } : {}),\n };\n }\n } catch {\n /* keep the refresh credential \u2014 fail-open */\n }\n const path = writeNow(cred);\n result = { ...(capt.email ? { email: capt.email } : {}), credentialPath: path };\n }\n res.writeHead(outcome.httpStatus, { 'content-type': 'text/html; charset=utf-8' });\n res.end(outcome.ok ? '<h2>AlgoHQ login complete \u2014 you can close this tab.</h2>' : `<h2>Login failed: ${outcome.error}</h2>`);\n finish(outcome.ok ? null : new Error(outcome.error ?? 'login failed'), result);\n })();\n });\n return;\n }\n res.writeHead(404);\n res.end('not found');\n });\n\n const timer = setTimeout(\n () => finish(new Error(`login timed out after ${timeoutMs}ms \u2014 no sign-in captured`)),\n timeoutMs,\n );\n\n server.on('error', (e: Error) => finish(e));\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address();\n const port = addr && typeof addr === 'object' ? addr.port : 0;\n if (!port) {\n finish(new Error('failed to bind a loopback port'));\n return;\n }\n const loginUrl = `${dashboardUrl}/cli-login?port=${port}&state=${encodeURIComponent(state)}`;\n log(`[vo-mcp] Opening your browser to sign in:\\n ${loginUrl}`);\n log('[vo-mcp] If it did not open, paste that URL into your browser. Waiting for sign-in\u2026');\n try {\n openBrowser(loginUrl);\n } catch {\n /* user opens manually */\n }\n });\n });\n}\n", "/**\n * Increment 3b.4b \u2014 exchange a captured Firebase refresh credential for a scoped,\n * revocable VO credential. The thin client mints ONCE via the control-plane\n * `POST /api/v1/auth/vo-credential` (authenticated with a fresh Firebase ID token\n * derived from the refresh token), then presents the `vocred_` instead of the raw\n * Firebase token thereafter \u2014 so the raw refresh token need not persist on disk\n * (spec \u00A76: kill the local secret).\n *\n * FAIL-OPEN to the refresh credential: ANY failure (an older control-plane without\n * the mint endpoint, a network error, a non-operator principal, a malformed\n * response) returns `null`, and the caller keeps using the Firebase refresh flow.\n * Never throws.\n */\nimport { createFirebaseRefreshTokenSource } from './auth-token-source.js';\n\n/** Minimal fetch shape (matches `auth-token-source`'s injectable fetch). */\ntype ExchangeFetch = (\n url: string,\n init?: { method?: string; headers?: Record<string, string>; body?: string },\n) => Promise<{ status: number; text: () => Promise<string> }>;\n\nexport interface VoCredentialResult {\n readonly vo_credential: string;\n readonly expires_at: string;\n}\n\nexport interface ExchangeOptions {\n readonly refreshToken: string;\n readonly apiKey: string;\n /** Control-plane base URL (e.g. `https://vo-control-plane-\u2026run.app`). */\n readonly controlPlaneUrl: string;\n /** Operator-friendly label stored with the credential (e.g. host + client). */\n readonly label?: string;\n /** Injectable fetch (tests); defaults to the global `fetch`. */\n readonly fetchFn?: ExchangeFetch;\n}\n\n/**\n * Mint a `vocred_` from a Firebase refresh credential. Returns the credential on\n * success, else `null` (fail-open). Never throws.\n */\nexport async function exchangeForVoCredential(opts: ExchangeOptions): Promise<VoCredentialResult | null> {\n const base = opts.controlPlaneUrl.replace(/\\/+$/, '');\n if (!base) return null;\n const fetchFn: ExchangeFetch = opts.fetchFn ?? (globalThis.fetch as unknown as ExchangeFetch);\n try {\n // 1. Derive a fresh Firebase ID token from the refresh credential (Inc 3a flow).\n const idToken = await createFirebaseRefreshTokenSource({\n refreshToken: opts.refreshToken,\n apiKey: opts.apiKey,\n ...(opts.fetchFn ? { fetchFn: opts.fetchFn } : {}),\n }).getToken();\n if (!idToken) return null;\n\n // 2. Mint the scoped credential with the ID token.\n const res = await fetchFn(`${base}/api/v1/auth/vo-credential`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${idToken}` },\n body: JSON.stringify(opts.label ? { label: opts.label } : {}),\n });\n if (res.status !== 200) return null;\n const parsed = JSON.parse(await res.text()) as { token?: unknown; expires_at?: unknown };\n const token = typeof parsed.token === 'string' ? parsed.token : '';\n const expiresAt = typeof parsed.expires_at === 'string' ? parsed.expires_at : '';\n if (!token.startsWith('vocred_') || !expiresAt) return null;\n return { vo_credential: token, expires_at: expiresAt };\n } catch {\n return null;\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\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 * Schema-version registry. Re-export v1 today; future v2/v3 dispatch by\n * `schema_version` field on the parsed input.\n */\nexport {\n ArchitecturalDefaultRuleSchema,\n parseRule,\n safeParseRule,\n type ArchitecturalDefaultRuleParsed,\n} from './rule-v1.js';\nexport {\n TenantOverrideSchema,\n parseOverride,\n safeParseOverride,\n type TenantOverrideParsed,\n} from './override-v1.js';\n\nexport const CURRENT_SCHEMA_VERSION = 1 as const;\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 * Public API for @algosuite/vo-arch-defaults.\n *\n * Stable surface \u2014 what external consumers (the vo-mcp `vo_architecture_review`\n * tool, the `vo-arch-check` CLI, future cloud control plane) import.\n */\n\n// Types\nexport type {\n ArchitecturalDefaultRule,\n AppliesReason,\n AppliesWhen,\n Category,\n ChangeType,\n EvidenceHit,\n EvidenceMatcher,\n EvidenceMatcherKind,\n QueryHit,\n QueryInput,\n QueryResult,\n Reference,\n ReferenceType,\n Severity,\n Stack,\n StaleRuleInfo,\n TenantOverride,\n} from './types.js';\n\n// Schema\nexport {\n ArchitecturalDefaultRuleSchema,\n TenantOverrideSchema,\n parseRule,\n safeParseRule,\n parseOverride,\n safeParseOverride,\n CURRENT_SCHEMA_VERSION,\n} from './schema/index.js';\n\n// Storage\nexport {\n loadBundledCorpus,\n resolveBundledCorpusDir,\n type LoadBundledOptions,\n type LoadBundledResult,\n} from './storage/load-bundled.js';\nexport {\n loadTenantOverride,\n defaultOverridePath,\n type LoadOverrideOptions,\n type LoadOverrideResult,\n} from './storage/load-override.js';\nexport {\n mergeCorpusWithOverride,\n type MergeResult,\n} from './storage/merge.js';\n\n// Query\nexport { appliesToStack } from './query/applies-to-stack.js';\nexport { appliesToChangeType } from './query/applies-to-change.js';\nexport { appliesToFiles } from './query/applies-to-files.js';\nexport {\n findApplicableRules,\n type FindApplicableRulesOptions,\n} from './query/run-query.js';\nexport { globToRegExp, matchesAnyGlob } from './query/glob.js';\nexport { computeCorpusVersionHash } from './query/corpus-version.js';\nexport { resolveStack } from './query/resolve-stack.js';\nexport {\n computeStaleRules,\n DEFAULT_STALENESS_THRESHOLD_DAYS,\n type StaleRule,\n type ComputeStaleRulesOptions,\n} from './query/staleness.js';\n\n// Analyze\nexport {\n parseUnifiedDiff,\n type ParsedDiff,\n type ParsedFile,\n type ParsedChangeType,\n} from './analyze/diff-parser.js';\nexport { runEvidenceMatcher } from './analyze/evidence-matchers/index.js';\n\n// PII / secret scrubber (PR-U 2026-05-25 \u2014 moved here from vo-mcp so KB\n// consumers don't need vo-mcp as a dependency to sanitize evidence excerpts).\nexport {\n sanitizeExcerpt,\n SANITIZE_DEFAULT_MAX_LEN,\n} from './pii-sanitize.js';\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 * 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/**\n * Aggregate per-call inference cost into the telemetry event's top-level shape\n * (Base44 rec #4 \u2014 \"instrument per-task cost NOW so the corpus can later justify\n * a learned router / cheaper tier\").\n *\n * PREFERS the engine's cumulative `token_usage` when present: `per_model_verdicts`\n * carries only the FINAL round, so summing it under-reports any call that\n * deliberated \u2014 and deliberation is what the priciest gate (`final-deep-verify`)\n * does. The engine accumulates every fan-out round, so its total is the honest one.\n * Falls back to summing the verdicts when the engine didn't report usage (older\n * engine build, or a path that never fanned out).\n *\n * - `per_model_tokens_in` / `per_model_tokens_out`: keyed by model id. Null when NO\n * usage was reported \u2014 never a map of fabricated zeros, so \"we don't know\" is\n * distinguishable from \"zero tokens\".\n * - `total_cost_usd`: summed ONLY from attested `cost_micro_usd` receipts, converted\n * micro-USD \u2192 USD. Null when nothing attested \u2014 never estimated from a price table.\n */\nexport function aggregateEventTokenUsage(\n src: ConsensusSuccess['per_model_verdicts'],\n engineUsage?: ConsensusSuccess['token_usage'],\n): Pick<ConsensusCallEvent, 'per_model_tokens_in' | 'per_model_tokens_out' | 'total_cost_usd'> {\n if (engineUsage !== undefined) {\n const hasIn = Object.keys(engineUsage.per_model_tokens_in).length > 0;\n const hasOut = Object.keys(engineUsage.per_model_tokens_out).length > 0;\n return {\n per_model_tokens_in: hasIn ? engineUsage.per_model_tokens_in : null,\n per_model_tokens_out: hasOut ? engineUsage.per_model_tokens_out : null,\n total_cost_usd:\n engineUsage.cost_micro_usd === null ? null : engineUsage.cost_micro_usd / 1_000_000,\n };\n }\n const tokensIn: Record<string, number> = {};\n const tokensOut: Record<string, number> = {};\n let anyTokensIn = false;\n let anyTokensOut = false;\n let costMicroUsd = 0;\n let anyCost = false;\n\n for (const v of src) {\n if (typeof v.input_tokens === 'number') {\n tokensIn[v.model] = (tokensIn[v.model] ?? 0) + v.input_tokens;\n anyTokensIn = true;\n }\n if (typeof v.output_tokens === 'number') {\n tokensOut[v.model] = (tokensOut[v.model] ?? 0) + v.output_tokens;\n anyTokensOut = true;\n }\n if (typeof v.cost_micro_usd === 'number') {\n costMicroUsd += v.cost_micro_usd;\n anyCost = true;\n }\n }\n\n return {\n per_model_tokens_in: anyTokensIn ? tokensIn : null,\n per_model_tokens_out: anyTokensOut ? tokensOut : null,\n total_cost_usd: anyCost ? costMicroUsd / 1_000_000 : null,\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 * 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\n/** Read only the OS keychain; never fall back to env-selected plaintext files. */\nexport function readStoredCredentialKeychainOnly(\n env: Readonly<Record<string, string | undefined>> = process.env,\n keychain: KeychainBackend = realKeychain,\n): StoredCredential | null {\n if (!keychainEnabled(env, keychain)) return null;\n const raw = keychain.get();\n return raw ? deserialize(raw) : null;\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", "import { resolve, sep } from 'node:path';\n\nconst SAFE_MEMORY_FILE_RE = /^[A-Za-z0-9][A-Za-z0-9._ -]*\\.md$/i;\n\nexport function isSafeMemoryFileName(fileName: string): boolean {\n return fileName.length <= 200\n && fileName.trim() === fileName\n && !fileName.includes('/')\n && !fileName.includes('\\\\')\n && !fileName.includes(':')\n && SAFE_MEMORY_FILE_RE.test(fileName);\n}\n\nexport function resolveMemoryFilePath(memoryDir: string, fileName: string): string {\n if (!isSafeMemoryFileName(fileName)) {\n throw new Error(`unsafe memory file_name: ${fileName.slice(0, 80)}`);\n }\n const root = resolve(memoryDir);\n const filePath = resolve(root, fileName);\n const rootPrefix = root.endsWith(sep) ? root : `${root}${sep}`;\n if (filePath !== root && !filePath.startsWith(rootPrefix)) {\n throw new Error(`memory file path escapes memory directory: ${fileName.slice(0, 80)}`);\n }\n return filePath;\n}\n", "/**\n * Who holds the memory-sync lock, and is that holder still alive?\n *\n * Split out of `sync-lock.ts` (400-line cap) because the abandonment decision is\n * the part that has to be reasoned about \u2014 and tested \u2014 quadrant by quadrant.\n *\n * ## The rule, and the defect it closes\n *\n * PR #9327 evaluated the TTL branch FIRST and independently of the liveness\n * evidence:\n *\n * ```ts\n * if (ageMs !== null && ageMs > ttlMs) return true; // \u2190 wins outright\n * \u2026then the same-host dead-pid check\n * ```\n *\n * So a sync that legitimately ran past the 15-minute TTL was declared abandoned\n * and STOLEN **while it was still writing** \u2014 producing exactly the two\n * concurrent writers the lock exists to prevent. Bigger inputs made it more\n * likely, which is backwards: the slower a sync is, the more certain it was to\n * be robbed.\n *\n * The rule here inverts that precedence. **Live-holder evidence DOMINATES.**\n *\n * | holder liveness | age \u2264 TTL | age > TTL |\n * | -------------------------- | --------- | --------- |\n * | `alive` (same host, pid up) | keep | **keep** |\n * | `dead` (same host, pid gone)| steal | steal |\n * | `unknown` (foreign/unreadable) | keep | steal |\n *\n * The TTL is now what it should always have been: the fallback for the one case\n * where liveness is genuinely unknowable, not an override for the case where it\n * is known.\n *\n * ## The residue this creates, stated rather than discovered later\n *\n * A REUSED pid reads as `alive`, so a lock whose holder died and whose pid was\n * recycled is never reclaimed by this host. That is the safe direction \u2014 the\n * contending sync fails closed with an error naming the lock path, and the\n * operator deletes one file \u2014 where the alternative is silent corruption. It is\n * also much rarer than it was: a sync can no longer run for minutes (see\n * `SYNC_DEADLINE_MS` in `memory-sync-http.ts`), so the window in which a lock is\n * held at all is now seconds.\n */\nimport { statSync, readFileSync } from 'node:fs';\n\n/** What a lock file contains. Pretty-printed on disk so a human can read it. */\nexport interface MemorySyncLockPayload {\n readonly pid: number;\n readonly hostname: string;\n readonly sessionId: string | null;\n /** Unique per acquire \u2014 the ownership proof `release()` checks. */\n readonly token: string;\n readonly acquiredAt: string;\n readonly acquiredAtMs: number;\n}\n\nexport interface LockRecord {\n readonly raw: string;\n readonly payload: MemorySyncLockPayload | null;\n}\n\n/**\n * Does `pid` exist on this host? Signal 0 performs the permission/existence\n * check without delivering anything. EPERM means the process EXISTS but belongs\n * to another user \u2014 alive, and therefore not reclaimable.\n */\nexport function defaultIsProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch (err) {\n return (err as NodeJS.ErrnoException).code === 'EPERM';\n }\n}\n\nexport function toPayload(parsed: unknown): MemorySyncLockPayload | null {\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const record = parsed as Record<string, unknown>;\n const token = record['token'];\n const host = record['hostname'];\n if (typeof token !== 'string' || token.length === 0) return null;\n const pid = record['pid'];\n const acquiredAtMs = record['acquiredAtMs'];\n return {\n pid: typeof pid === 'number' && Number.isInteger(pid) && pid > 0 ? pid : 0,\n hostname: typeof host === 'string' ? host : '',\n sessionId: typeof record['sessionId'] === 'string' ? record['sessionId'] : null,\n token,\n acquiredAt: typeof record['acquiredAt'] === 'string' ? record['acquiredAt'] : '',\n acquiredAtMs: typeof acquiredAtMs === 'number' && Number.isFinite(acquiredAtMs) ? acquiredAtMs : Number.NaN,\n };\n}\n\nexport function readLockRecord(path: string): LockRecord | null {\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n return null;\n }\n try {\n return { raw, payload: toPayload(JSON.parse(raw)) };\n } catch {\n return { raw, payload: null };\n }\n}\n\n/**\n * Age of a held lock in ms, or null when it cannot be established. Null is the\n * fail-closed value: an age we cannot compute must never read as expired.\n */\nexport function lockAgeMs(record: LockRecord, path: string, nowMs: number): number | null {\n let startedMs = Number.NaN;\n if (record.payload) {\n if (Number.isFinite(record.payload.acquiredAtMs)) {\n startedMs = record.payload.acquiredAtMs;\n } else if (record.payload.acquiredAt) {\n startedMs = Date.parse(record.payload.acquiredAt);\n }\n }\n if (!Number.isFinite(startedMs)) {\n try {\n startedMs = statSync(path).mtimeMs;\n } catch {\n return null;\n }\n }\n const age = nowMs - startedMs;\n // A negative age means the writer's clock is ahead of ours (or ours moved\n // back). Unknown, not expired.\n return Number.isFinite(age) && age >= 0 ? age : null;\n}\n\n/**\n * `alive` and `dead` are POSITIVE findings about the holder process. `unknown`\n * is everything we cannot interrogate: a foreign hostname (pid numbers mean\n * nothing across hosts), an unparseable payload (we do not know whose it is), a\n * missing pid, or a host we cannot even name.\n */\nexport type HolderLiveness = 'alive' | 'dead' | 'unknown';\n\nexport function classifyHolderLiveness(\n record: LockRecord,\n isProcessAlive: (pid: number) => boolean,\n thisHost: string,\n): HolderLiveness {\n const payload = record.payload;\n if (payload === null) return 'unknown';\n if (payload.pid <= 0) return 'unknown';\n if (thisHost.length === 0) return 'unknown';\n if (payload.hostname !== thisHost) return 'unknown';\n return isProcessAlive(payload.pid) ? 'alive' : 'dead';\n}\n\n/**\n * Is this lock abandoned? See the table in the module header \u2014 liveness\n * dominates, and the TTL applies ONLY to an unknowable holder.\n */\nexport function isLockAbandoned(\n record: LockRecord,\n ageMs: number | null,\n ttlMs: number,\n isProcessAlive: (pid: number) => boolean,\n thisHost: string,\n): boolean {\n const liveness = classifyHolderLiveness(record, isProcessAlive, thisHost);\n // A provably-running writer is NEVER stolen from, at any age. This is the\n // whole point of the module; do not reorder it below the TTL check again.\n if (liveness === 'alive') return false;\n // A same-host pid that no longer exists cannot be mid-write. Reclaim at once\n // rather than making every other session wait out the TTL.\n if (liveness === 'dead') return true;\n // Unknowable holder: the age is the only evidence there is, and an age we\n // could not compute (null) must never read as expired.\n return ageMs !== null && ageMs > ttlMs;\n}\n", "/**\n * memory sync LOCK \u2014 one writer at a time over `~/.claude/projects/<slug>/memory/`.\n *\n * ## The defect this closes\n *\n * Up to seven agent sessions run against the SAME memory directory on this\n * machine, and nothing serialized them. `vo_sync_config pull` rewrites every\n * local memory file while another session's `push` is mid-`readdirSync`/\n * `readFileSync`, so a push can ship a file that is half of the old content and\n * half of the new one; two concurrent `push` runs interleave their PUTs against\n * the same `memory_id`. Both are silent \u2014 the loser gets an HTTP 200.\n *\n * ## Why `openSync(path, 'wx')`\n *\n * O_EXCL create is the same race-free primitive `../../swarm/spawn-ledger.ts`\n * uses for slot claims: the kernel guarantees exactly one concurrent caller\n * creates the file, so the winner is decided without a second lock. This module\n * deliberately follows that file's shape rather than introducing a second\n * locking style into the package.\n *\n * ## Stale takeover, and why it is REQUIRED\n *\n * A killed process runs no `finally`, so an abandoned lock must be reclaimable\n * or one kill would deadlock memory sync on the host forever. The decision \u2014\n * which is where PR #9327's second MAJOR lived \u2014 is in `sync-lock-liveness.ts`:\n * live-holder evidence DOMINATES the TTL, so a sync that legitimately runs long\n * is never stolen from mid-write. Read that module's header table before\n * touching {@link DEFAULT_LOCK_TTL_MS}.\n *\n * ## Fail-closed rules (absent/unknown never resolves toward more concurrency)\n *\n * - Contention waits with backoff to a bounded deadline; on expiry the caller\n * gets an ERROR, never an unlocked sync.\n * - An unreadable lock file is NOT stolen on the pid signal (we cannot tell\n * whose it is) \u2014 only the mtime-derived TTL can reclaim it.\n * - A negative age (a clock that moved backwards) reads as UNKNOWN, not as\n * expired.\n * - `release()` removes the lock ONLY when the file still carries this handle's\n * token, so a caller that was legitimately taken over cannot delete its\n * successor's live lock.\n * - Acquiring does NOT create the memory directory unless the caller asks\n * ({@link MemorySyncLockOptions.createDir}). A push over a slug that has no\n * memory has nothing to serialize, and PR #9327's unconditional `mkdirSync`\n * littered an empty `memory/` into every project dir on the machine.\n *\n * RESIDUE, stated rather than discovered later: takeover is not atomic on a\n * POSIX/Win32 filesystem. Two callers can both judge the same lock abandoned;\n * the second re-reads the file and aborts its `unlinkSync` if the bytes changed,\n * with no `await` between that read and the unlink, so the window is a single\n * syscall gap rather than a scheduling quantum. It is not zero. The alternative\n * \u2014 never reclaiming \u2014 is a permanent deadlock, which is strictly worse.\n *\n * RESIDUE 2, the one this lock does NOT solve: mutual exclusion here is a\n * HOST-LOCAL file, while the resource it protects \u2014 the operator's cloud memory\n * index \u2014 is operator-GLOBAL. Two machines still race. See the fragment\n * `docs/vo/roadmap-log/2026-08-02-memory-sync-completable.md`.\n */\nimport { closeSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { hostname } from 'node:os';\nimport { join } from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport {\n defaultIsProcessAlive,\n isLockAbandoned,\n lockAgeMs,\n readLockRecord,\n toPayload,\n type LockRecord,\n type MemorySyncLockPayload,\n} from './sync-lock-liveness.js';\n\nexport {\n classifyHolderLiveness,\n defaultIsProcessAlive,\n isLockAbandoned,\n lockAgeMs,\n type HolderLiveness,\n type LockRecord,\n type MemorySyncLockPayload,\n} from './sync-lock-liveness.js';\n\n/**\n * Lock file name, inside the memory directory.\n *\n * It is NOT a memory file and must never be pushed. Two independent filters\n * already exclude it \u2014 `pushMemory` keeps only `*.md`, and\n * `isSafeMemoryFileName` rejects a leading `.` \u2014 and `memory-sync-http.ts`\n * excludes it by name on top of that. `sync-config-lock.test.ts` asserts the\n * exclusion against the real push path.\n */\nexport const MEMORY_SYNC_LOCK_FILE = '.memory-sync.lock';\n\n/**\n * 15 minutes. Applies ONLY to a holder whose liveness is unknowable (a foreign\n * hostname or an unreadable lock file) \u2014 a live same-host holder is never\n * stolen from at any age, and a dead one is reclaimed immediately without\n * waiting this out.\n *\n * Derivation, so the next reader can re-check it rather than trust it: a sync\n * is now hard-bounded at `SYNC_DEADLINE_MS` (120s) plus one in-flight request\n * (`REQUEST_TIMEOUT_MS`, 15s) = ~135s worst case \u2014 see `memory-sync-http.ts`.\n * 15 minutes is ~6.7x that. The old derivation (\"140 serial round trips \u2248 5\n * minutes\") described a push that could not finish inside any hook budget at\n * all; that push is gone.\n */\nexport const DEFAULT_LOCK_TTL_MS = 15 * 60_000;\n\n/**\n * How long a contending caller waits before failing closed \u2014 10 seconds.\n *\n * This number MUST fit inside the budget of whatever is calling. PR #9327 set\n * it to 60_000 while its only production callers were hooks that killed the\n * subprocess after 2-3 SECONDS, so the contention budget could never elapse and\n * the fail-closed error it guarded could never surface \u2014 dead code by\n * construction. 10s is under the SessionStart pull hook's own 20s ceiling; the\n * detached Stop push, which has no session-blocking budget, passes a larger\n * value explicitly (`--lock-wait-ms`) rather than inheriting a guess.\n */\nexport const DEFAULT_LOCK_WAIT_MS = 10_000;\n\nconst INITIAL_BACKOFF_MS = 25;\nconst MAX_BACKOFF_MS = 500;\nconst BACKOFF_FACTOR = 1.6;\n\nexport interface MemorySyncLockOptions {\n /** Directory the lock lives in. NOT created unless {@link createDir}. */\n readonly memoryDir: string;\n readonly sessionId?: string | undefined;\n /** Contention budget, ms. Default {@link DEFAULT_LOCK_WAIT_MS}. */\n readonly waitMs?: number | undefined;\n /** Abandonment threshold for an UNKNOWABLE holder, ms. Default {@link DEFAULT_LOCK_TTL_MS}. */\n readonly ttlMs?: number | undefined;\n /**\n * Create `memoryDir` if absent. Only a caller that is about to WRITE files\n * into it (i.e. `pull`) should pass true. A `push` must not conjure a memory\n * directory for a project slug that legitimately has none.\n */\n readonly createDir?: boolean | undefined;\n readonly now?: (() => number) | undefined;\n readonly sleep?: ((ms: number) => Promise<void>) | undefined;\n /** Injected for tests; production uses signal-0 against the real process table. */\n readonly isProcessAlive?: ((pid: number) => boolean) | undefined;\n}\n\nexport interface MemorySyncLockHandle {\n readonly path: string;\n readonly payload: MemorySyncLockPayload;\n /** The abandoned holder this acquire reclaimed from, else null. */\n readonly tookOverFrom: MemorySyncLockPayload | null;\n /** Idempotent. NEVER throws. Removes the file only if this handle still owns it. */\n release(): void;\n}\n\nfunction positiveOr(value: number | undefined, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : fallback;\n}\n\ntype CreateOutcome = { readonly ok: true } | { readonly ok: false; readonly exists: boolean; readonly message: string };\n\n/** Create `path` with O_EXCL, writing `contents`. Never leaves a partial file. */\nfunction createExclusive(path: string, contents: string): CreateOutcome {\n let fd: number;\n try {\n fd = openSync(path, 'wx');\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n return { ok: false, exists: code === 'EEXIST', message: err instanceof Error ? err.message : String(err) };\n }\n try {\n writeFileSync(fd, contents, 'utf8');\n } catch (err) {\n closeSync(fd);\n // The empty file we just created would otherwise be a lock nobody holds.\n try {\n unlinkSync(path);\n } catch {\n /* best effort \u2014 the TTL reclaims it */\n }\n return { ok: false, exists: false, message: err instanceof Error ? err.message : String(err) };\n }\n // Closed BEFORE any later unlink: Windows refuses to unlink a file that is\n // still held open (EPERM), which would make every release on Windows leak.\n closeSync(fd);\n return { ok: true };\n}\n\n/**\n * Remove a lock we judged abandoned, but only if it is still byte-identical to\n * what we judged.\n *\n * This is the compare-and-delete guard that stops two simultaneous takeovers\n * from deleting a live SUCCESSOR's lock: A and B both judge lock L abandoned, A\n * unlinks it and immediately re-creates its own lock A'; B must then NOT unlink\n * A'. B's confirming read sees A' bytes, which differ from L, so it aborts.\n * There is deliberately no `await` between the read and the unlink \u2014 see the\n * RESIDUE note in the header for the syscall-gap window that remains.\n * Covered by `sync-lock-takeover-guard.test.ts`, including a mutation that\n * proves removing the byte-comparison lets B destroy A'.\n */\nexport function removeAbandoned(path: string, expectedRaw: string): void {\n let current: string;\n try {\n current = readFileSync(path, 'utf8');\n } catch {\n return; // already gone; the acquire loop retries the create\n }\n if (current !== expectedRaw) return; // replaced since we judged it \u2014 not ours to remove\n try {\n unlinkSync(path);\n } catch {\n /* raced or unwritable; the acquire loop re-evaluates against the deadline */\n }\n}\n\n/** Release is defined here so it never captures the acquire loop's locals. */\nfunction makeRelease(path: string, token: string): () => void {\n let released = false;\n return () => {\n if (released) return;\n released = true;\n let raw: string;\n try {\n raw = readFileSync(path, 'utf8');\n } catch {\n // ENOENT \u2014 someone reclaimed a lock we had let go stale. Tolerated by\n // contract: release must never throw.\n return;\n }\n let stillOurs: boolean;\n try {\n stillOurs = toPayload(JSON.parse(raw))?.token === token;\n } catch {\n stillOurs = false;\n }\n if (!stillOurs) return; // a successor owns it; deleting theirs would un-serialize them\n try {\n unlinkSync(path);\n } catch {\n /* never throw from release */\n }\n };\n}\n\nfunction describeHolder(record: LockRecord | null): string {\n const payload = record?.payload;\n if (!payload) return 'an unreadable lock file';\n return `pid ${payload.pid} on ${payload.hostname || '(unknown host)'} (session ${payload.sessionId ?? 'unknown'}, held since ${payload.acquiredAt || 'unknown'})`;\n}\n\n/**\n * Take the memory-sync lock, waiting out contention up to `waitMs`.\n *\n * @throws when the deadline expires or the lock file is unusable \u2014 a caller\n * that cannot hold the lock must NOT sync. Callers should prefer\n * {@link withMemorySyncLock}, which releases in `finally`.\n */\nexport async function acquireMemorySyncLock(options: MemorySyncLockOptions): Promise<MemorySyncLockHandle> {\n const waitMs = positiveOr(options.waitMs, DEFAULT_LOCK_WAIT_MS);\n const ttlMs = positiveOr(options.ttlMs, DEFAULT_LOCK_TTL_MS);\n const now = options.now ?? Date.now;\n const sleep = options.sleep ?? ((ms: number) => new Promise<void>((resolve) => { setTimeout(resolve, ms); }));\n const isProcessAlive = options.isProcessAlive ?? defaultIsProcessAlive;\n const thisHost = hostname();\n const path = join(options.memoryDir, MEMORY_SYNC_LOCK_FILE);\n\n // Only when the caller is about to write into it. See MemorySyncLockOptions.\n if (options.createDir === true) mkdirSync(options.memoryDir, { recursive: true });\n\n const deadline = now() + waitMs;\n let backoffMs = INITIAL_BACKOFF_MS;\n let tookOverFrom: MemorySyncLockPayload | null = null;\n let holderDescription = 'another session';\n\n for (;;) {\n const acquiredAtMs = now();\n const payload: MemorySyncLockPayload = {\n pid: process.pid,\n hostname: thisHost,\n sessionId: options.sessionId ?? null,\n token: randomUUID(),\n acquiredAt: new Date(acquiredAtMs).toISOString(),\n acquiredAtMs,\n };\n const created = createExclusive(path, `${JSON.stringify(payload, null, 2)}\\n`);\n if (created.ok) {\n return { path, payload, tookOverFrom, release: makeRelease(path, payload.token) };\n }\n if (!created.exists) {\n throw new Error(\n `memory sync lock ${path} could not be created (${created.message}) \u2014 refusing to sync without exclusion`,\n );\n }\n\n const record = readLockRecord(path);\n let reclaimed = false;\n if (record) {\n holderDescription = describeHolder(record);\n const age = lockAgeMs(record, path, now());\n if (isLockAbandoned(record, age, ttlMs, isProcessAlive, thisHost)) {\n tookOverFrom = record.payload;\n removeAbandoned(path, record.raw);\n reclaimed = true;\n }\n }\n\n // Checked on EVERY path, including after a takeover, so an unremovable lock\n // cannot spin here forever.\n if (now() >= deadline) {\n throw new Error(\n `memory sync lock ${path} is held by ${holderDescription}; waited ${waitMs}ms \u2014 ` +\n 'refusing to sync unlocked (concurrent memory writes corrupt the shared index). ' +\n 'If that holder is provably gone, delete the lock file.',\n );\n }\n // ALWAYS yield, including straight after a takeover. Retrying a reclaim\n // immediately looks free, but a lock that cannot actually be removed (EPERM,\n // or one another session keeps re-creating) then busy-spins for the whole\n // wait budget \u2014 a hot loop on a host that already runs seven sessions. A\n // reclaim resets the backoff instead, so the retry is still fast (25ms).\n if (reclaimed) backoffMs = INITIAL_BACKOFF_MS;\n await sleep(Math.max(1, Math.min(backoffMs, deadline - now())));\n if (!reclaimed) backoffMs = Math.min(MAX_BACKOFF_MS, Math.ceil(backoffMs * BACKOFF_FACTOR));\n }\n}\n\n/** Run `fn` holding the memory-sync lock. Releases in `finally`, always. */\nexport async function withMemorySyncLock<T>(\n options: MemorySyncLockOptions,\n fn: (handle: MemorySyncLockHandle) => Promise<T>,\n): Promise<T> {\n const handle = await acquireMemorySyncLock(options);\n try {\n return await fn(handle);\n } finally {\n handle.release();\n }\n}\n", "/**\n * MEMORY.md index MERGE \u2014 stop the shared index being last-writer-wins.\n *\n * ## The defect this closes\n *\n * Every session appends its own lines to ONE ~19KB flat index,\n * `~/.claude/projects/<slug>/memory/MEMORY.md`. `pushMemory` maps\n * `file_name -> memory_id` and PUTs the local bytes, so session B's push\n * REPLACES the document session A pushed minutes earlier. A's topic files\n * survive (they are separate entries); A's index LINES are gone. With up to\n * seven sessions the index is whichever one pushed last.\n *\n * A lock serializes the writes and does not fix this: B still PUTs a document\n * that never contained A's lines. The write has to be a MERGE.\n *\n * ## What is merged\n *\n * The index is one line per memory: `- [Title](file.md) \u2014 hook`. The union is\n * keyed on the LINK TARGET (`file.md`), case-insensitively, because that is the\n * memory's identity \u2014 a re-worded title or hook for the same file must UPDATE\n * the row in place, not add a second one.\n *\n * - Local order is preserved exactly; the local wording wins for any file both\n * sides carry (the pushing session is the one that just edited it).\n * - Cloud-only rows are inserted after the LAST local row, in cloud order, so\n * they join the list rather than landing after whatever prose follows it.\n * - Only local NON-row lines (headings, prose, blanks) survive. Merging the\n * cloud's prose too would duplicate the heading on every push.\n *\n * ## The trade-off, chosen deliberately\n *\n * A union cannot distinguish \"session A DELETED this row\" from \"session A never\n * had it\": a row A removed locally is still in the cloud, so the next push\n * RESURRECTS it. That is a real cost and it is the one we accept. The\n * alternative \u2014 last-writer-wins \u2014 silently destroys up to six other sessions'\n * index entries on every push, and a lost row means a memory that recall can no\n * longer find. A resurrected row is visible, wrong-looking, and one edit away\n * from gone; a lost row is invisible. Deleting a memory for real therefore means\n * deleting the topic FILE (which drops out of the push set) and then removing\n * the row \u2014 the row alone will come back.\n *\n * Wording is still last-writer-wins PER ROW: if A re-words a hook and B pushes\n * afterwards with the old wording, B's wording lands. No row is ever lost, which\n * is the property this module is responsible for.\n */\n\n/** The one index file. Everything else in the memory dir is a topic. */\nexport const MEMORY_INDEX_FILE = 'MEMORY.md';\n\n/** Case-insensitive, matching `memory-knowledge-bridge.ts`'s own comparison. */\nexport function isMemoryIndexFile(fileName: string): boolean {\n return fileName.toUpperCase() === MEMORY_INDEX_FILE.toUpperCase();\n}\n\n/** `- [Title](target) \u2014 hook`, with `*` bullets and leading indent tolerated. */\nconst INDEX_ROW_RE = /^\\s*[-*]\\s+\\[[^\\]]*\\]\\(([^)]+)\\)/;\n\n/**\n * The link target of an index row, NORMALIZED for keying, or null.\n *\n * Normalization is the point: the raw target is whatever the writing session\n * happened to type, and Markdown accepts several spellings of the same file.\n * PR #9327 keyed on the raw string, so `- [X](./foo.md)` and `- [X](foo.md)`\n * were two different memories and the union emitted BOTH \u2014 a duplicate row per\n * spelling, forever, in the index every session reads. Reproduced before the\n * fix: merging those two documents returned `addedFromCloud.length === 1`.\n *\n * Handled, in order: surrounding `<\u2026>` (Markdown's escape for targets with\n * spaces), a trailing `\"Title\"` / `'Title'` link title, Windows `\\` separators,\n * any number of leading `./` segments, and case. Deliberately NOT handled:\n * percent-decoding and `../` resolution \u2014 a memory index is a flat list of\n * sibling files, so those spellings do not occur, and guessing at them would\n * risk collapsing two genuinely different targets into one row.\n */\nexport function indexRowKey(line: string): string | null {\n const match = INDEX_ROW_RE.exec(line);\n if (!match) return null;\n let target = match[1].trim();\n if (target.startsWith('<') && target.endsWith('>')) target = target.slice(1, -1).trim();\n target = target.replace(/\\s+([\"'])[\\s\\S]*\\1$/, '').trim(); // drop a link title\n // A URL is not a memory file; leave it exactly as written rather than\n // mangling its scheme's `//` while collapsing path separators below.\n if (!/^[a-z][a-z0-9+.-]*:\\/\\//i.test(target)) {\n target = target.replace(/\\\\/g, '/').replace(/\\/{2,}/g, '/');\n target = target.replace(/^(?:\\.\\/)+/, '');\n }\n return target.length > 0 ? target.toLowerCase() : null;\n}\n\nexport interface MemoryIndexMergeResult {\n /** The document to push. */\n readonly content: string;\n /** Rows present only in the cloud copy that this merge preserved. */\n readonly addedFromCloud: readonly string[];\n}\n\n/**\n * Union the local index with the cloud index, keyed on each row's link target.\n *\n * Returns the local content untouched when there is no cloud document to merge\n * (first push, or a cloud read that returned nothing) \u2014 an absent remote must\n * never subtract from what the caller was about to push.\n */\nexport function mergeMemoryIndex(localContent: string, cloudContent: string | null | undefined): MemoryIndexMergeResult {\n if (typeof cloudContent !== 'string' || cloudContent.trim().length === 0) {\n return { content: localContent, addedFromCloud: [] };\n }\n\n const eol = localContent.includes('\\r\\n') ? '\\r\\n' : '\\n';\n const localLines = localContent.split(/\\r?\\n/);\n\n const localKeys = new Set<string>();\n let lastLocalRowIndex = -1;\n for (let i = 0; i < localLines.length; i++) {\n const key = indexRowKey(localLines[i]);\n if (key === null) continue;\n localKeys.add(key);\n lastLocalRowIndex = i;\n }\n\n const addedFromCloud: string[] = [];\n const seenCloudKeys = new Set<string>();\n for (const rawLine of cloudContent.split(/\\r?\\n/)) {\n const key = indexRowKey(rawLine);\n if (key === null) continue;\n if (localKeys.has(key) || seenCloudKeys.has(key)) continue;\n seenCloudKeys.add(key);\n addedFromCloud.push(rawLine.replace(/\\r$/, ''));\n }\n\n if (addedFromCloud.length === 0) {\n return { content: localContent, addedFromCloud: [] };\n }\n\n const merged =\n lastLocalRowIndex >= 0\n ? [...localLines.slice(0, lastLocalRowIndex + 1), ...addedFromCloud, ...localLines.slice(lastLocalRowIndex + 1)]\n : [...localLines, ...addedFromCloud];\n\n return { content: merged.join(eol), addedFromCloud };\n}\n", "/**\n * The two budget primitives every memory round trip runs under, plus the bounded\n * fan-out that makes a first-run push finish in seconds instead of minutes.\n *\n * These exist because \"how long can a sync take?\" had NO answer before: the push\n * was an unbounded serial loop over ~140 round trips with no per-request\n * timeout, so a single hung socket held the memory lock indefinitely and every\n * budget downstream (hook deadline, lock TTL, contention wait) was a guess about\n * an unbounded quantity. Bound the parts and the whole becomes statable:\n *\n * worst case = SYNC_DEADLINE_MS + REQUEST_TIMEOUT_MS \u2248 135s\n *\n * That single number is what `DEFAULT_LOCK_TTL_MS`, the Stop hook's\n * `--lock-wait-ms`, and the PR body's duration claim are all derived from. If\n * you change a constant here, change those with it.\n */\n\n/**\n * Ceiling on ONE control-plane round trip. A warm call is ~0.2-0.5s; a Cloud Run\n * cold start is ~5-10s. 15s covers the cold start with headroom and still fails\n * a genuinely hung socket fast enough that the overall deadline means something.\n */\nexport const REQUEST_TIMEOUT_MS = 15_000;\n\n/**\n * Ceiling on a whole push or pull, checked cooperatively BETWEEN round trips.\n * Two minutes is ~2x the measured worst-case first-run push (~60s: 140 trips at\n * concurrency 6 over ~24 waves), so it only ever fires on a genuinely sick\n * control plane.\n */\nexport const SYNC_DEADLINE_MS = 120_000;\n\n/**\n * Simultaneous in-flight round trips. The per-file PUT/POSTs are independent \u2014\n * different `memory_id`s, no ordering between them \u2014 so serializing them bought\n * nothing but latency. 6 keeps a first run to ~24 waves without hammering the\n * control plane from seven sessions at once.\n */\nexport const PUSH_CONCURRENCY = 6;\n\n/** Thrown when the overall sync deadline expires. Distinct so callers can name it. */\nexport class SyncDeadlineExceededError extends Error {\n constructor(elapsedMs: number, budgetMs: number) {\n super(\n `memory sync exceeded its ${budgetMs}ms deadline after ${elapsedMs}ms \u2014 ` +\n 'aborting so the lock is released instead of held indefinitely',\n );\n this.name = 'SyncDeadlineExceededError';\n }\n}\n\n/** Thrown when a single round trip exceeds {@link REQUEST_TIMEOUT_MS}. */\nexport class RequestTimeoutError extends Error {\n constructor(url: string, budgetMs: number) {\n super(`memory sync request to ${url} exceeded ${budgetMs}ms`);\n this.name = 'RequestTimeoutError';\n }\n}\n\n/**\n * A cooperative deadline. `check()` throws once the budget is spent; callers\n * invoke it before starting each round trip, so a sync that is already over\n * budget stops issuing work rather than discovering it at the end.\n */\nexport interface SyncDeadline {\n check(): void;\n remainingMs(): number;\n}\n\nexport function createSyncDeadline(budgetMs: number = SYNC_DEADLINE_MS, now: () => number = Date.now): SyncDeadline {\n const startedAt = now();\n return {\n check() {\n const elapsed = now() - startedAt;\n if (elapsed > budgetMs) throw new SyncDeadlineExceededError(elapsed, budgetMs);\n },\n remainingMs() {\n return Math.max(0, budgetMs - (now() - startedAt));\n },\n };\n}\n\n/**\n * Race a round trip against {@link REQUEST_TIMEOUT_MS}.\n *\n * The abandoned request keeps running \u2014 `FetchLike` has no abort surface \u2014 but\n * the SYNC stops waiting on it, which is the property that matters: the caller\n * unwinds, `withMemorySyncLock`'s `finally` runs, and the lock is released\n * rather than pinned to a hung socket for the life of the process.\n */\nexport async function withRequestTimeout<T>(\n url: string,\n run: () => Promise<T>,\n budgetMs: number = REQUEST_TIMEOUT_MS,\n): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n return await Promise.race([\n run(),\n new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => reject(new RequestTimeoutError(url, budgetMs)), budgetMs);\n // Do not keep the process alive just for this timer.\n timer.unref?.();\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\n/**\n * Map `items` through `fn` with at most `limit` in flight, ALWAYS awaiting every\n * started task before returning.\n *\n * Never rejects: each result is settled individually. A rejecting task must not\n * abandon its siblings mid-flight \u2014 that is how you get a half-finished push\n * whose successful uploads were never recorded in the state cache, which then\n * re-sends them forever.\n */\nexport async function mapWithConcurrency<T, R>(\n items: readonly T[],\n limit: number,\n fn: (item: T, index: number) => Promise<R>,\n): Promise<Array<{ ok: true; value: R } | { ok: false; error: unknown }>> {\n const results: Array<{ ok: true; value: R } | { ok: false; error: unknown }> = new Array(items.length);\n const width = Math.max(1, Math.min(limit, items.length));\n let next = 0;\n\n async function worker(): Promise<void> {\n for (;;) {\n const index = next++;\n if (index >= items.length) return;\n try {\n results[index] = { ok: true, value: await fn(items[index] as T, index) };\n } catch (error) {\n results[index] = { ok: false, error };\n }\n }\n }\n\n await Promise.all(Array.from({ length: width }, () => worker()));\n return results;\n}\n", "/**\n * INCREMENTAL PUSH state \u2014 what this host has already uploaded, so a push does\n * not re-send ~70 unchanged files on every session end.\n *\n * ## The defect this closes\n *\n * `pushMemory` sent one PUT/POST per memory file plus one knowledge-bridge POST\n * per topic file on EVERY run: ~140 serial round trips at this operator's ~70\n * files. Measured on origin/main with a deliberately generous 20ms-per-call\n * stub, that is 140 calls / 4,623ms; at a realistic warm control-plane latency\n * (~0.2-0.5s) it is 30-70s, and against a cold Cloud Run instance it is minutes.\n * The Stop hook that drives it killed the subprocess after 3 SECONDS. The push\n * could therefore never complete, the lock's contention budget could never\n * elapse, and \u2014 because a killed process runs no `finally` \u2014 every run left a\n * lock behind.\n *\n * ## The key, and why it is the SENT bytes and not the file bytes\n *\n * Each entry records the sha256 of the payload this host last successfully\n * uploaded for that `file_name`. Not the local file's hash \u2014 the payload's.\n * That distinction is what makes `MEMORY.md` work: its pushed content is the\n * MERGE of local and cloud (`memory-index-merge.ts`), so the cloud side can\n * change the bytes to send while the local file is untouched. Hashing the\n * merged result means the index is skipped exactly when the merge is a no-op,\n * with no special case anywhere in the push loop.\n *\n * ## Fail-closed by construction\n *\n * Every uncertainty resolves toward SENDING, never toward skipping:\n *\n * - no state file, unreadable, bad JSON, wrong `version` \u2192 empty cache \u2192 push\n * everything (first run).\n * - a different `controlPlaneUrl` than the one recorded \u2192 empty cache. Pointing\n * at a new control plane must not \"skip\" files that plane has never seen.\n * - the server's GET does not list an entry for the file \u2192 push it even on a\n * hash hit. A cache hit only ever means \"the bytes I sent are the bytes I\n * would send\"; it is never evidence the server still holds them.\n * - a failed upload does NOT record a hash, so the next run retries it.\n *\n * The state file is a dotfile inside the memory dir, which three independent\n * filters already exclude from the push set (`*.md` only, `isSafeMemoryFileName`\n * rejects a leading `.`, and the bridge filters `*.md` too).\n */\nimport { createHash } from 'node:crypto';\nimport { readFileSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/** Not a memory file; never pushed. Sibling of `MEMORY_SYNC_LOCK_FILE`. */\nexport const MEMORY_SYNC_STATE_FILE = '.memory-sync-state.json';\n\nconst STATE_VERSION = 1;\n\nexport interface PushCacheEntry {\n /** sha256 of the payload last successfully PUT/POSTed to the memory route. */\n readonly memoryHash?: string;\n /** sha256 of the content last successfully upserted into the knowledge corpus. */\n readonly knowledgeHash?: string;\n}\n\nexport interface PushCache {\n readonly controlPlaneUrl: string;\n readonly entries: Map<string, PushCacheEntry>;\n /**\n * When this host last upserted EVERY memory file into the knowledge corpus,\n * epoch ms, or null if never. See {@link KNOWLEDGE_FULL_SWEEP_MS}.\n */\n knowledgeSweptAtMs: number | null;\n}\n\n/**\n * How stale the knowledge corpus is allowed to get before the next push\n * re-upserts every file regardless of hash \u2014 24 hours.\n *\n * NEIGHBOUR CHECK, and the reason this exists. `needsMemoryPush` has TWO\n * independent conditions: the hash AND `serverHasEntry`, because a local cache\n * hit is never evidence the server still holds the bytes. The knowledge bridge\n * one branch over has no equivalent \u2014 it only POSTs, never GETs, so there is no\n * server-side evidence to consult. Skipping on the hash alone would therefore\n * fail OPEN: if the corpus is wiped or a route drifts, an unchanged memory file\n * is never re-sent and `vo_private_knowledge_context` recall stays empty\n * forever. That is precisely the reservoir-empty bug the bridge was built to fix\n * (live-fire 2026-07-24), reintroduced by its own optimisation.\n *\n * A periodic full sweep bounds the divergence at 24h while keeping the\n * steady-state win for every other run of the day.\n */\nexport const KNOWLEDGE_FULL_SWEEP_MS = 24 * 60 * 60_000;\n\nexport function sha256(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\nfunction statePath(memoryDir: string): string {\n return join(memoryDir, MEMORY_SYNC_STATE_FILE);\n}\n\n/**\n * Load the cache for `controlPlaneUrl`. Returns an EMPTY cache \u2014 meaning \"push\n * everything\" \u2014 for every failure mode, including a mismatched control plane.\n */\nexport function readPushCache(memoryDir: string, controlPlaneUrl: string): PushCache {\n const empty: PushCache = { controlPlaneUrl, entries: new Map(), knowledgeSweptAtMs: null };\n let raw: string;\n try {\n raw = readFileSync(statePath(memoryDir), 'utf8');\n } catch {\n return empty;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return empty;\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return empty;\n const obj = parsed as Record<string, unknown>;\n if (obj['version'] !== STATE_VERSION) return empty;\n if (obj['controlPlaneUrl'] !== controlPlaneUrl) return empty;\n const files = obj['entries'];\n if (typeof files !== 'object' || files === null || Array.isArray(files)) return empty;\n\n const entries = new Map<string, PushCacheEntry>();\n for (const [name, value] of Object.entries(files as Record<string, unknown>)) {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) continue;\n const row = value as Record<string, unknown>;\n const memoryHash = typeof row['memoryHash'] === 'string' ? row['memoryHash'] : undefined;\n const knowledgeHash = typeof row['knowledgeHash'] === 'string' ? row['knowledgeHash'] : undefined;\n if (memoryHash === undefined && knowledgeHash === undefined) continue;\n entries.set(name, {\n ...(memoryHash !== undefined ? { memoryHash } : {}),\n ...(knowledgeHash !== undefined ? { knowledgeHash } : {}),\n });\n }\n const sweptAt = obj['knowledgeSweptAtMs'];\n return {\n controlPlaneUrl,\n entries,\n // An unreadable/absent sweep stamp reads as NEVER SWEPT, which forces a full\n // sweep \u2014 the fail-closed direction (more upserts, never fewer).\n knowledgeSweptAtMs: typeof sweptAt === 'number' && Number.isFinite(sweptAt) ? sweptAt : null,\n };\n}\n\n/**\n * Persist the cache. Best-effort by contract: a state file we cannot write\n * costs the NEXT run a full re-push, which is correct-but-slow. It must never\n * turn a completed upload into a reported failure.\n */\nexport function writePushCache(memoryDir: string, cache: PushCache): void {\n const entries: Record<string, PushCacheEntry> = {};\n for (const [name, value] of cache.entries) entries[name] = value;\n try {\n writeFileSync(\n statePath(memoryDir),\n `${JSON.stringify(\n {\n version: STATE_VERSION,\n controlPlaneUrl: cache.controlPlaneUrl,\n knowledgeSweptAtMs: cache.knowledgeSweptAtMs,\n entries,\n },\n null,\n 2,\n )}\\n`,\n 'utf8',\n );\n } catch {\n /* see the doc comment \u2014 never fail a sync over its own bookkeeping */\n }\n}\n\n/** Record a successful memory upload. Mutates `cache` in place. */\nexport function recordMemoryPush(cache: PushCache, fileName: string, payloadHash: string): void {\n cache.entries.set(fileName, { ...cache.entries.get(fileName), memoryHash: payloadHash });\n}\n\n/** Record a successful knowledge upsert. Mutates `cache` in place. */\nexport function recordKnowledgePush(cache: PushCache, fileName: string, contentHash: string): void {\n cache.entries.set(fileName, { ...cache.entries.get(fileName), knowledgeHash: contentHash });\n}\n\n/**\n * Drop rows for files that no longer exist locally, so the state file cannot\n * grow without bound as memories are renamed or deleted.\n *\n * Deleting the row is deliberately NOT a cloud delete: neither push nor pull\n * has ever removed a cloud entry, and a local delete is not evidence the\n * operator wants the backup destroyed. It only means a file re-created later\n * with identical bytes is re-sent once \u2014 the safe direction.\n */\nexport function pruneMissing(cache: PushCache, presentFileNames: readonly string[]): void {\n const present = new Set(presentFileNames);\n for (const name of [...cache.entries.keys()]) {\n if (!present.has(name)) cache.entries.delete(name);\n }\n}\n\n/**\n * Should this payload be uploaded to the memory route?\n *\n * `serverHasEntry` is the second, independent condition: a cache hit alone is\n * never proof the server still holds the bytes.\n */\nexport function needsMemoryPush(cache: PushCache, fileName: string, payloadHash: string, serverHasEntry: boolean): boolean {\n if (!serverHasEntry) return true;\n return cache.entries.get(fileName)?.memoryHash !== payloadHash;\n}\n\n/**\n * Is a FULL knowledge sweep due? True when this host has never swept, or the\n * last sweep is older than {@link KNOWLEDGE_FULL_SWEEP_MS}. Unknown always\n * resolves toward sweeping.\n */\nexport function knowledgeSweepDue(cache: PushCache, nowMs: number = Date.now()): boolean {\n const swept = cache.knowledgeSweptAtMs;\n if (swept === null || !Number.isFinite(swept)) return true;\n const age = nowMs - swept;\n // A negative age (clock moved) is unknown, not fresh.\n return !(age >= 0 && age < KNOWLEDGE_FULL_SWEEP_MS);\n}\n\n/**\n * Should this content be re-upserted into the knowledge corpus?\n *\n * `sweepDue` bypasses the hash entirely \u2014 see {@link KNOWLEDGE_FULL_SWEEP_MS}\n * for why an upsert-only route cannot be skipped on local evidence alone.\n */\nexport function needsKnowledgePush(\n cache: PushCache,\n fileName: string,\n contentHash: string,\n sweepDue = false,\n): boolean {\n if (sweepDue) return true;\n return cache.entries.get(fileName)?.knowledgeHash !== contentHash;\n}\n", "/**\n * The control-plane half of `vo_sync_config` \u2014 the pull and push round trips\n * against `/api/v1/agent-config/memory/*`.\n *\n * Callers MUST hold the memory-sync lock (see `sync-lock.ts`) \u2014 neither function\n * takes it itself, because a pull and a push have to be serialized against EACH\n * OTHER, which only the orchestrating caller can arrange.\n *\n * ## Push is INCREMENTAL and BOUNDED (the 2026-08-02 rework)\n *\n * It used to be an unbounded serial loop: one PUT/POST per memory file, every\n * run, no per-request timeout. At this operator's ~70 files that is 71 memory\n * trips plus 69 knowledge trips = ~140 serial round trips \u2014 measured on\n * origin/main at 140 calls / 4,623ms against a 20ms/call stub, i.e. 30-70s\n * warm and minutes cold. The Stop hook driving it killed the child at 3,000ms,\n * so the push could never finish, the lock's contention budget could never\n * elapse, and each killed run leaked a lock (no `finally` on a kill).\n *\n * Three changes make it completable, and the numbers now agree end to end:\n *\n * 1. **Incremental** \u2014 `memory-push-cache.ts` remembers the sha256 of the\n * payload last successfully uploaded per file. Steady state is 1 GET and\n * zero writes.\n * 2. **Bounded fan-out** \u2014 `PUSH_CONCURRENCY` (6) independent round trips in\n * flight, so even a first run is ~24 waves rather than 140 hops.\n * 3. **Bounded time** \u2014 `REQUEST_TIMEOUT_MS` per trip and a cooperative\n * `SYNC_DEADLINE_MS` overall, so \"how long can this hold the lock?\" has an\n * answer (~135s) instead of being unbounded.\n *\n * Measured costs, first run vs steady state, are asserted in\n * `memory-push-incremental.test.ts` \u2014 not estimated here.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';\nimport { resolveMemoryFilePath } from './safe-memory-file.js';\nimport { MEMORY_SYNC_LOCK_FILE } from './sync-lock.js';\nimport { isMemoryIndexFile, mergeMemoryIndex } from './memory-index-merge.js';\nimport {\n createSyncDeadline,\n mapWithConcurrency,\n PUSH_CONCURRENCY,\n withRequestTimeout,\n type SyncDeadline,\n} from './bounded-sync.js';\nimport {\n needsMemoryPush,\n pruneMissing,\n readPushCache,\n recordMemoryPush,\n sha256,\n writePushCache,\n type PushCache,\n} from './memory-push-cache.js';\n\nexport interface 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/** Options threaded in by `sync-config.ts`; defaults keep the tests' 5-arg form working. */\nexport interface PushOptions {\n readonly cache?: PushCache | undefined;\n readonly deadline?: SyncDeadline | undefined;\n readonly concurrency?: number | undefined;\n /**\n * Write the state file here. Defaults to true ONLY when this function owned\n * the cache: a caller that passes `cache` in (sync-config.ts) also runs the\n * knowledge bridge against the same object afterwards and persists once, at\n * the end \u2014 persisting here would drop every `knowledgeHash` the bridge is\n * about to record.\n */\n readonly persistCache?: boolean | undefined;\n}\n\n/**\n * Pull cloud memory entries to local files.\n *\n * One GET plus local writes \u2014 it was never the expensive direction, but it now\n * runs under the same request timeout so a hung socket cannot pin the lock.\n */\nexport async 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 withRequestTimeout(url, () =>\n fetchFn(url, {\n method: 'GET',\n headers: {\n authorization: `Bearer ${token}`,\n },\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 // Every path is resolved BEFORE any write, so one unsafe file_name aborts the\n // whole pull rather than leaving a half-applied one.\n const writes = data.entries.map((entry) => ({\n entry,\n filePath: resolveMemoryFilePath(memoryDir, entry.file_name),\n }));\n mkdirSync(memoryDir, { recursive: true });\n const files: string[] = [];\n\n for (const { entry, filePath } of writes) {\n writeFileSync(filePath, entry.content, 'utf8');\n files.push(entry.file_name);\n }\n\n return { pulled: data.entries.length, files };\n}\n\nexport interface PushMemoryResult {\n /** Files actually uploaded this run \u2014 always `created + updated`. */\n readonly pushed: number;\n readonly created: number;\n readonly updated: number;\n /**\n * Files whose payload was byte-identical to what this host last uploaded AND\n * which the server still lists. The whole point of the rework: in steady\n * state this is every file and `pushed` is 0.\n */\n readonly skipped: number;\n /**\n * Rows that existed ONLY in the cloud index and were carried into a pushed\n * document. Non-zero means this push preserved another session's entries that\n * the pre-merge code would have destroyed. Counted only for the index\n * documents actually uploaded \u2014 a skipped index preserved nothing NEW.\n */\n readonly indexRowsPreserved: number;\n}\n\n/** Local `.md` files to push. The lock file is not memory and never ships. */\nfunction listPushableFiles(memoryDir: string): string[] {\n return readdirSync(memoryDir).filter((f) => f.endsWith('.md') && f !== MEMORY_SYNC_LOCK_FILE);\n}\n\ninterface PushItem {\n readonly fileName: string;\n readonly entryType: 'index' | 'topic';\n readonly content: string;\n readonly payloadHash: string;\n readonly memoryId: string | null;\n readonly rowsPreserved: number;\n}\n\nasync function uploadOne(\n item: PushItem,\n controlPlaneUrl: string,\n token: string,\n sessionId: string,\n fetchFn: FetchLike,\n deadline: SyncDeadline,\n): Promise<'created' | 'updated'> {\n // Cooperative: an already-over-budget push stops ISSUING work rather than\n // discovering the overrun after another 20 round trips.\n deadline.check();\n\n if (item.memoryId !== null) {\n const updateUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/${item.memoryId}`;\n const updateBody: UpdateMemoryBody = { content: item.content, session_id: sessionId };\n const updateResponse = await withRequestTimeout(updateUrl, () =>\n fetchFn(updateUrl, {\n method: 'PUT',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\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/${item.memoryId} returned HTTP ${updateResponse.status}: ${text.slice(0, 200)}`,\n );\n }\n const updateData = JSON.parse(await updateResponse.text()) as UpdateMemoryResponse;\n if (!updateData.ok) throw new Error(`PUT /api/v1/agent-config/memory/${item.memoryId} returned ok=false`);\n return 'updated';\n }\n\n const createUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\n const createBody: CreateMemoryBody = {\n entry_type: item.entryType,\n file_name: item.fileName,\n content: item.content,\n session_id: sessionId,\n };\n const createResponse = await withRequestTimeout(createUrl, () =>\n fetchFn(createUrl, {\n method: 'POST',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\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 const createData = JSON.parse(await createResponse.text()) as CreateMemoryResponse;\n if (!createData.ok) throw new Error('POST /api/v1/agent-config/memory/me returned ok=false');\n return 'created';\n}\n\n/**\n * Push local memory files to cloud.\n *\n * `MEMORY.md` is merged with the cloud copy first \u2014 see `memory-index-merge.ts`\n * for why a straight PUT loses every other session's index rows.\n */\nexport async function pushMemory(\n controlPlaneUrl: string,\n token: string,\n memoryDir: string,\n sessionId: string,\n fetchFn: FetchLike,\n options: PushOptions = {},\n): Promise<PushMemoryResult> {\n const empty: PushMemoryResult = { pushed: 0, created: 0, updated: 0, skipped: 0, indexRowsPreserved: 0 };\n if (!existsSync(memoryDir)) {\n return empty;\n }\n\n const localFiles = listPushableFiles(memoryDir).map((f) => ({\n file_name: f,\n content: readFileSync(resolveMemoryFilePath(memoryDir, f), 'utf8'),\n entry_type: (isMemoryIndexFile(f) ? 'index' : 'topic') as 'index' | 'topic',\n }));\n\n if (localFiles.length === 0) {\n return empty;\n }\n\n const deadline = options.deadline ?? createSyncDeadline();\n const cache = options.cache ?? readPushCache(memoryDir, controlPlaneUrl);\n\n // GET existing entries to map file_name \u2192 { memory_id, content }. The CONTENT\n // is what makes the index union possible: it is the other sessions' rows.\n deadline.check();\n const getUrl = `${controlPlaneUrl}/api/v1/agent-config/memory/me`;\n const getResponse = await withRequestTimeout(getUrl, () =>\n fetchFn(getUrl, { method: 'GET', headers: { authorization: `Bearer ${token}` } }),\n );\n\n const existingMap = new Map<string, { memoryId: string; content: 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, {\n memoryId: entry.memory_id,\n content: typeof entry.content === 'string' ? entry.content : '',\n });\n }\n }\n }\n\n // Decide, per file, whether the bytes we would send differ from the bytes we\n // last sent. Hashing the MERGED payload (not the local file) is what makes the\n // index participate correctly \u2014 see memory-push-cache.ts.\n const toUpload: PushItem[] = [];\n let skipped = 0;\n for (const localFile of localFiles) {\n const existing = existingMap.get(localFile.file_name);\n let content = localFile.content;\n let rowsPreserved = 0;\n if (localFile.entry_type === 'index') {\n const merged = mergeMemoryIndex(localFile.content, existing?.content);\n content = merged.content;\n rowsPreserved = merged.addedFromCloud.length;\n }\n const payloadHash = sha256(content);\n if (!needsMemoryPush(cache, localFile.file_name, payloadHash, existing !== undefined)) {\n skipped++;\n continue;\n }\n toUpload.push({\n fileName: localFile.file_name,\n entryType: localFile.entry_type,\n content,\n payloadHash,\n memoryId: existing?.memoryId ?? null,\n rowsPreserved,\n });\n }\n\n const outcomes = await mapWithConcurrency(toUpload, options.concurrency ?? PUSH_CONCURRENCY, (item) =>\n uploadOne(item, controlPlaneUrl, token, sessionId, fetchFn, deadline),\n );\n\n let created = 0;\n let updated = 0;\n let indexRowsPreserved = 0;\n let firstError: unknown;\n for (let i = 0; i < outcomes.length; i++) {\n const outcome = outcomes[i] as (typeof outcomes)[number];\n const item = toUpload[i] as PushItem;\n if (outcome.ok) {\n if (outcome.value === 'created') created++;\n else updated++;\n indexRowsPreserved += item.rowsPreserved;\n // Recorded ONLY on success, so a failed upload is retried next run.\n recordMemoryPush(cache, item.fileName, item.payloadHash);\n } else if (firstError === undefined) {\n firstError = outcome.error;\n }\n }\n\n pruneMissing(cache, localFiles.map((f) => f.file_name));\n if (options.persistCache ?? options.cache === undefined) writePushCache(memoryDir, cache);\n\n // Every started upload has settled before we surface the failure \u2014 a partial\n // push whose successes were never recorded would re-send them forever.\n if (firstError !== undefined) throw firstError;\n\n return { pushed: created + updated, created, updated, skipped, indexRowsPreserved };\n}\n", "/**\n * The operator's kill switch for the DETACHED memory push.\n *\n * ## Why this exists\n *\n * Two Stop hooks push the operator's memory directory to the cloud, both\n * detached, both unref'd, both with stdio ignored, on EVERY session Stop across\n * up to seven parallel sessions:\n *\n * A) `~/.claude/hooks/memory-corpus-sync.mjs` (machine-installed, wired at\n * `~/.claude/settings.json`) \u2192 spawns `packages/vo-mcp/dist/cli.js sync push`\n * B) `vo-claude-plugin/hooks/write-back-cloud-memory.mjs` \u2192 `startMemorySync()`\n * in `vo-claude-plugin/lib/vo-mcp-launch.mjs` \u2192 the same `vo-mcp sync push`\n *\n * Neither read ANY environment variable, so an operator watching a misbehaving\n * background push had no lever short of editing hook source or uninstalling.\n * The anti-swarm doctrine requires a kill switch on any unattended spawn path.\n *\n * ## The two mechanisms \u2014 EITHER one alone disables\n *\n * 1. ENV VAR `VO_MCP_DISABLE_MEMORY_SYNC` \u2014 for anything the operator launches\n * after setting it.\n * 2. SENTINEL FILE `~/.claude/vo-memory-sync-disabled` \u2014 because an env var\n * cannot reach shells that are ALREADY RUNNING, which is exactly the\n * situation an operator is in when they want the thing off NOW. A single\n * `New-Item ~/.claude/vo-memory-sync-disabled` stops the next Stop-hook push\n * in every already-open session. Delete the file to re-enable.\n *\n * They are ORed, never ANDed: an explicit `=0` on one mechanism does NOT veto the\n * other. A veto would mean an operator who once exported `...=0` could create the\n * sentinel and watch it be ignored.\n *\n * ## Value handling is DELIBERATELY forgiving (and deliberately unlike isTruthyFlag)\n *\n * `isTruthyFlag()` in `../../consensus/engine-options.ts` is an ALLOW-LIST\n * (`1|true|yes|on`). That is right for an opt-IN: an unrecognised value falls back\n * to the safe default of \"feature off\". For an OFF-switch the same shape is a\n * trap \u2014 an operator typing `DISABLE=please` or `DISABLE=y` under pressure would\n * get a push that keeps running. So this is a DENY-LIST: any non-empty value\n * disables EXCEPT an explicit `0` / `false` / `no`.\n *\n * `off` is NOT in the negation list. In a variable named `..._DISABLE` the string\n * `off` is genuinely ambiguous (\"disabling is off\" vs \"sync off\"), and an\n * ambiguous off-switch must break toward honouring the operator, not ignoring it.\n *\n * ## Blast radius \u2014 deliberately the WHOLE of memory sync, not just the push\n *\n * The gate lives at the top of `runMemorySync`, so it also covers:\n * - `sync pull` (the SessionStart hook `fetch-cloud-memory.mjs`), which writes\n * cloud state OVER local memory files. An off switch that stopped only the\n * upload would leave the more destructive half running.\n * - the `vo_sync_config` MCP tool, so an agent cannot drive a cloud write\n * through a hole in the operator's switch.\n * Consequence, stated so it is not a surprise: with the switch thrown, tests that\n * drive a real `runMemorySync` report `DISABLED by \u2026` rather than syncing. That\n * message says exactly what happened.\n *\n * ## What DISABLED looks like (this is the point of the whole slice)\n *\n * Disabled is VISIBLE, never silent: no spawn, no network, no lock acquired, and\n * one line naming the mechanism appended to `~/.claude/vo-mcp-sync.log`. An\n * operator must be able to tell \"I turned it off\" from \"it silently broke\".\n *\n * A plugin-side twin of this module lives at\n * `vo-claude-plugin/lib/memory-sync-kill-switch.mjs` (the plugin ships as plain\n * scripts and cannot import from the published vo-mcp tarball). The two are held\n * in lockstep BEHAVIOURALLY by\n * `packages/vo-mcp/test/memory/kill-switch-twin-lockstep.test.ts`, which drives\n * both over one shared value table.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\n\n/** Env var that disables the memory push. Matches the `VO_MCP_DISABLE_*` shape of `VO_MCP_DISABLE_KEYCHAIN`. */\nexport const MEMORY_SYNC_DISABLE_ENV = 'VO_MCP_DISABLE_MEMORY_SYNC';\n\n/** Sentinel filename under `~/.claude`. Its EXISTENCE is the signal; see `isKillSwitchValueOn` for contents. */\nexport const MEMORY_SYNC_DISABLE_SENTINEL = 'vo-memory-sync-disabled';\n\n/** Values that mean \"do NOT disable\". Everything else non-empty disables. */\nconst NEGATIONS = new Set(['0', 'false', 'no']);\n\n/** Longest operator value echoed into the log line (a flag, not a payload). */\nconst MAX_LOGGED_VALUE = 32;\n\nexport interface MemorySyncKillSwitch {\n /** True when the memory push must not run. */\n readonly disabled: boolean;\n /**\n * One-line, log-ready explanation naming the mechanism(s) that fired, or null\n * when nothing did. Always starts `memory sync DISABLED by ` so it is greppable\n * and unmistakably distinct from a failure line.\n */\n readonly reason: string | null;\n}\n\n/** Absolute path of the sentinel file for a given home directory. */\nexport function memorySyncSentinelPath(home: string): string {\n return join(home, '.claude', MEMORY_SYNC_DISABLE_SENTINEL);\n}\n\n/**\n * Deny-list truthiness for an off-switch value.\n *\n * `undefined` / `null` / empty / whitespace-only \u2192 NOT on (an unset or\n * shell-blanked variable must not disable anything). Any other value is on\n * unless it trims-and-lowercases to `0`, `false`, or `no`.\n */\nexport function isKillSwitchValueOn(raw: string | undefined | null): boolean {\n if (raw === undefined || raw === null) return false;\n const v = raw.trim().toLowerCase();\n if (v === '') return false;\n return !NEGATIONS.has(v);\n}\n\nfunction clip(raw: string): string {\n const v = raw.trim();\n return v.length > MAX_LOGGED_VALUE ? `${v.slice(0, MAX_LOGGED_VALUE)}\u2026` : v;\n}\n\n/**\n * Decide whether the memory push is disabled, and by what.\n *\n * Reads at most one env var and one small file \u2014 no network, no spawn, no lock.\n * Callers MUST invoke this before acquiring the memory-sync lock, so that turning\n * the switch on can never leave a stale lock behind to block a later enabled run.\n *\n * Never throws: an unreadable-but-present sentinel counts as DISABLED (the\n * operator created a file; a permission error is not a reason to overrule them).\n */\nexport function evaluateMemorySyncKillSwitch(\n deps: {\n readonly env?: Readonly<Record<string, string | undefined>>;\n readonly home?: string;\n readonly fileExists?: (p: string) => boolean;\n readonly readFile?: (p: string) => string;\n } = {},\n): MemorySyncKillSwitch {\n const env = deps.env ?? process.env;\n const home = deps.home ?? homedir();\n const fileExists = deps.fileExists ?? existsSync;\n const readFile = deps.readFile ?? ((p: string) => readFileSync(p, 'utf8'));\n\n const fired: string[] = [];\n\n const rawEnv = env[MEMORY_SYNC_DISABLE_ENV];\n if (isKillSwitchValueOn(rawEnv)) {\n fired.push(`env ${MEMORY_SYNC_DISABLE_ENV}=${clip(rawEnv as string)}`);\n }\n\n // Evaluated INDEPENDENTLY of the env var \u2014 either mechanism alone must disable.\n const sentinel = memorySyncSentinelPath(home);\n let sentinelPresent: boolean;\n try {\n sentinelPresent = fileExists(sentinel);\n } catch {\n // A stat that throws is not an operator decision \u2014 leave the push enabled.\n sentinelPresent = false;\n }\n if (sentinelPresent) {\n let contents = '';\n let readable = true;\n try {\n contents = readFile(sentinel);\n } catch {\n readable = false;\n }\n // An empty file is the `touch` case and MUST disable; only explicit negation\n // in the contents spares it. Unreadable \u2192 honour the file's existence.\n if (!readable || isKillSwitchValueOn(contents) || contents.trim() === '') {\n fired.push(`sentinel file ${sentinel}`);\n }\n }\n\n if (fired.length === 0) return { disabled: false, reason: null };\n return { disabled: true, reason: `memory sync DISABLED by ${fired.join(' + ')}` };\n}\n", "/**\n * memory-knowledge-bridge \u2014 feeds operator memory files into the private\n * knowledge corpus so `vo_private_knowledge_context` recall works across\n * Claude/Codex/Cursor/cowork clients.\n *\n * Why this exists (live-fire finding, 2026-07-24): the recall pipe was fully\n * built \u2014 tools, auth, vendor wiring \u2014 but the corpus behind it was EMPTY.\n * `vo-mcp sync push` backed memory files up to AgentConfigMemory (raw file\n * backup for restore), while `vo_private_knowledge_context` searches private\n * knowledge entries that only `vo_private_knowledge_upsert` writes \u2014 and\n * nothing ever upserted. Cross-vendor recall was a working pipe to an empty\n * reservoir. This bridge closes the loop: every memory push also upserts each\n * file as a knowledge entry (class 'memory', idempotent on source_path).\n *\n * Fail-soft contract: knowledge upserts must NEVER fail the memory backup \u2014\n * per-file failures are counted and reported, not thrown.\n *\n * ## Incremental and bounded, for the same reason the push is (2026-08-02)\n *\n * This loop was the OTHER half of the ~140 serial round trips that made a sync\n * impossible to finish inside any hook budget: one POST per topic file, every\n * run, unconditionally, serially, with no per-request timeout. It now shares the\n * push's state cache (skip a file whose content is byte-identical to the last\n * successful upsert) and its bounded fan-out. Steady state is ZERO requests.\n */\nimport { existsSync, readdirSync, readFileSync } from 'node:fs';\nimport type { FetchLike } from './sync-config.js';\nimport { resolveMemoryFilePath } from './safe-memory-file.js';\nimport { mapWithConcurrency, PUSH_CONCURRENCY, withRequestTimeout, type SyncDeadline } from './bounded-sync.js';\nimport {\n knowledgeSweepDue,\n needsKnowledgePush,\n recordKnowledgePush,\n sha256,\n type PushCache,\n} from './memory-push-cache.js';\n\nexport interface BridgeResult {\n readonly attempted: number;\n readonly upserted: number;\n readonly failed: number;\n /** Files whose content was byte-identical to the last successful upsert. */\n readonly skipped: number;\n /** First few failure reasons, for the sync result payload (bounded). */\n readonly failures: readonly string[];\n}\n\n/** Hard schema limit server-side (private-knowledge-v1 content max); anything\n * larger is a guaranteed 400, so skip it with a named failure instead. */\nconst CONTENT_HARD_LIMIT = 500_000;\n\n/**\n * Retrieval-friendly title for a memory file: frontmatter `description:` \u2192\n * first `# ` heading \u2192 the file name. Retrieval matches on titles, so the\n * one-line description memories carry is exactly the right surface. The\n * description lookup is anchored INSIDE the leading frontmatter fence \u2014 a body\n * line starting \"description:\" must not outrank a real heading (red-team F5).\n */\nexport function extractMemoryTitle(fileName: string, content: string): string {\n const frontmatter = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---/);\n if (frontmatter) {\n const description = frontmatter[1].match(/^description:\\s*(.+)$/m);\n if (description && description[1].trim()) return description[1].trim().slice(0, 200);\n }\n const heading = content.match(/^#\\s+(.+)$/m);\n if (heading && heading[1].trim()) return heading[1].trim().slice(0, 200);\n return fileName;\n}\n\n/**\n * Upsert every local memory .md file as a private-knowledge entry.\n * `source_path` is `memory/<file_name>` \u2014 the schema's idempotent upsert key \u2014\n * so repeated pushes refresh rather than duplicate.\n */\nexport async function upsertMemoryFilesAsKnowledge(options: {\n controlPlaneUrl: string;\n token: string;\n memoryDir: string;\n fetchFn: FetchLike;\n /** Shared with `pushMemory`; omit to upsert every file (no incrementality). */\n cache?: PushCache | undefined;\n deadline?: SyncDeadline | undefined;\n concurrency?: number | undefined;\n}): Promise<BridgeResult> {\n const { controlPlaneUrl, token, memoryDir, fetchFn, cache, deadline } = options;\n let files: string[];\n try {\n if (!existsSync(memoryDir)) {\n return { attempted: 0, upserted: 0, failed: 0, skipped: 0, failures: [] };\n }\n // MEMORY.md excluded: the index duplicates every topic's keywords, so it\n // out-scores real entries on nearly any query and burns a retrieval slot\n // on a near-random snippet (red-team F5). Topics carry the knowledge.\n files = readdirSync(memoryDir).filter(\n (f) => f.endsWith('.md') && f.toUpperCase() !== 'MEMORY.MD',\n );\n } catch (err) {\n // Fail-soft even on directory-level errors (red-team F2): the memory\n // BACKUP already succeeded when this runs \u2014 never let the bridge convert\n // that success into a reported sync failure.\n return {\n attempted: 0, upserted: 0, failed: 1, skipped: 0,\n failures: [`memory dir scan: ${err instanceof Error ? err.message : String(err)}`],\n };\n }\n\n // Read + hash first so the skip decision costs no network at all.\n //\n // `sweepDue` is the fail-closed half: this route only ever POSTs, so unlike\n // `needsMemoryPush` there is no server-side evidence to pair the hash with.\n // Once a day we re-upsert everything, which bounds how long a wiped corpus can\n // stay wiped. See KNOWLEDGE_FULL_SWEEP_MS.\n const sweepDue = cache ? knowledgeSweepDue(cache) : true;\n const candidates: Array<{ fileName: string; content: string; hash: string }> = [];\n const failures: string[] = [];\n let skipped = 0;\n for (const fileName of files) {\n try {\n const content = readFileSync(resolveMemoryFilePath(memoryDir, fileName), 'utf8');\n if (content.length > CONTENT_HARD_LIMIT) {\n failures.push(`${fileName}: ${content.length} chars exceeds the ${CONTENT_HARD_LIMIT} server limit \u2014 split the memory file`);\n continue;\n }\n const hash = sha256(content);\n if (cache && !needsKnowledgePush(cache, fileName, hash, sweepDue)) {\n skipped += 1;\n continue;\n }\n candidates.push({ fileName, content, hash });\n } catch (err) {\n failures.push(`${fileName}: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n const url = `${controlPlaneUrl}/api/v1/knowledge/private`;\n const outcomes = await mapWithConcurrency(candidates, options.concurrency ?? PUSH_CONCURRENCY, async (candidate) => {\n deadline?.check();\n const base = {\n knowledge_class: 'memory',\n source_path: `memory/${candidate.fileName}`,\n title: extractMemoryTitle(candidate.fileName, candidate.content),\n content: candidate.content,\n };\n const post = (body: unknown) => withRequestTimeout(url, () => fetchFn(url, {\n method: 'POST',\n headers: {\n authorization: `Bearer ${token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n }));\n let response = await post({\n ...base,\n provenance: { written_by: 'memory-bridge', source_kind: 'operator_memory' },\n });\n if (response.status === 400) {\n // Pre-provenance control-plane (strict schema rejects the new key).\n // Retry without it so client/server deploy order can't strand the\n // corpus; the next server deploy starts recording provenance.\n response = await post(base);\n }\n if (response.status >= 200 && response.status < 300) return true;\n const text = await response.text();\n throw new Error(`HTTP ${response.status} ${text.slice(0, 80)}`);\n });\n\n let upserted = 0;\n for (let i = 0; i < outcomes.length; i++) {\n const outcome = outcomes[i] as (typeof outcomes)[number];\n const candidate = candidates[i] as { fileName: string; hash: string };\n if (outcome.ok) {\n upserted += 1;\n // Only a SUCCESSFUL upsert is remembered, so a 403 retries next run\n // instead of being skipped forever with an empty corpus behind it.\n if (cache) recordKnowledgePush(cache, candidate.fileName, candidate.hash);\n } else {\n const error = outcome.error;\n failures.push(`${candidate.fileName}: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // The sweep stamp advances ONLY when a due sweep actually covered every file\n // without a single failure. A sweep that 403'd half the corpus must not mark\n // the corpus as freshly swept \u2014 that would be the fail-open version of this\n // whole guard.\n if (cache && sweepDue && failures.length === 0) {\n cache.knowledgeSweptAtMs = Date.now();\n }\n\n return {\n // Every memory file this run considered. `attempted === upserted + skipped\n // + failed` holds, so a caller can tell \"nothing to do\" from \"nothing ran\".\n attempted: files.length,\n upserted,\n failed: failures.length,\n skipped,\n failures: failures.slice(0, 5),\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 *\n * The round trips live in `memory-sync-http.ts`. Both actions run under the\n * memory-sync LOCK (`sync-lock.ts`): up to seven sessions share this one\n * directory, so an unserialized pull can rewrite the files a concurrent push is\n * midway through reading. Failing to take the lock FAILS the sync \u2014 it never\n * degrades to an unlocked one.\n */\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport type { ToolDeps } from '../../types.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\nimport { pullMemory, pushMemory, type FetchLike } from './memory-sync-http.js';\nimport { createSyncDeadline } from './bounded-sync.js';\nimport { readPushCache, writePushCache } from './memory-push-cache.js';\nimport { withMemorySyncLock, type MemorySyncLockOptions } from './sync-lock.js';\nimport { evaluateMemorySyncKillSwitch } from './sync-kill-switch.js';\n\nexport const TOOL_NAME = 'vo_sync_config';\n\n/** Re-exported so `memory-knowledge-bridge.ts` / `private-knowledge.ts` keep their import path. */\nexport type { FetchLike } from './memory-sync-http.js';\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. Serialized across concurrent sessions by an exclusive lock in the memory dir.';\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 // MUST mirror Claude Code's own project-dir slug: it maps a cwd to\n // ~/.claude/projects/<slug>/ by replacing EVERY non-alphanumeric char with a\n // dash, preserving case, with NO trailing-separator strip and NO collapsing\n // of consecutive separators. Verified against real on-disk project dirs\n // (2026-07-24): 'D:\\\\' -> 'D--', 'D:\\\\nexus-backup\\\\Nexus' ->\n // 'D--nexus-backup-Nexus', and 'C:\\\\Users\\\\greyl\\\\OneDrive\\\\Documents\\\\New\n // project\\\\Nexus' -> 'C--Users-greyl-OneDrive-Documents-New-project-Nexus'\n // (note the space becomes a dash too).\n //\n // The earlier trailing-separator strip diverged from Claude Code for the ONE\n // real cwd that ends in a separator \u2014 a drive root. `process.cwd()` returns\n // 'D:\\\\' for a drive-root session, which Claude Code slugs 'D--' but the\n // stripped form produced 'D-', so memory-sync silently read/wrote an EMPTY\n // 'D-/memory' instead of the operator's real 'D--/memory'. Removing the strip\n // realigns them; deep-path cwds (no trailing separator) are unaffected.\n //\n // Non-root trailing separators are the inverse hazard: `process.cwd()` never\n // produces 'D:\\\\foo\\\\', but a hand-passed --cwd / MCP cwd can, and Claude Code\n // itself would only ever see the canonical 'D:\\foo' \u2014 so the trailing form\n // must slug identically ('D--foo', not 'D--foo-'). Strip separators ONLY when\n // they don't terminate a bare root ('D:\\\\', '/'), keeping the fix above.\n return cwd\n .replace(/([^:\\\\/])[\\\\/]+$/, '$1') // canonicalize non-root trailing separators\n .replace(/\\\\/g, '/') // normalize Windows separators (redundant with the final map, kept for the drive regex)\n .replace(/^([a-zA-Z]):/, (_m, drive: string) => `${drive.toUpperCase()}:`) // Windows cwds arrive upper-cased; match Claude's dir\n .replace(/[^a-zA-Z0-9]/g, '-'); // every 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\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 /** Push only: files whose bytes were already uploaded \u2014 the incremental win. */\n readonly skipped?: number;\n readonly memory_dir?: string;\n /** Private-knowledge bridge (push only, additive-optional \u2014 schema stays v1):\n * how many memory files were upserted into the recall corpus. */\n readonly knowledge_upserted?: number;\n readonly knowledge_failed?: number;\n readonly knowledge_skipped?: number;\n readonly knowledge_failures?: readonly string[];\n /** Push only: cloud-only MEMORY.md rows this push preserved instead of overwriting. */\n readonly index_rows_preserved?: number;\n /** Set when this run reclaimed an abandoned lock \u2014 names the dead holder's pid. */\n readonly lock_taken_over_from_pid?: number;\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.\n *\n * A lock failure is deliberately NOT in this set: \"another session holds the\n * memory lock\" is a real, retryable failure and must surface as one.\n *\n * `DISABLED by` IS in the set: an operator throwing the kill switch is a\n * deliberate no-op, not an error, so the CLI exits 0 and the durable log records\n * it as `skip \u2026 memory sync DISABLED by <mechanism>` rather than `FAIL`. That\n * wording is the whole point \u2014 the operator must be able to read \"I turned it\n * off\" and never mistake it for \"it silently broke\". */\nexport function isNoopSyncReason(reason: string | undefined): boolean {\n if (!reason) return false;\n return /not set|No auth configured|Failed to obtain auth token|DISABLED by/.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/` UNDER THE MEMORY-SYNC LOCK.\n * Shared by the `vo_sync_config` MCP tool AND the `vo-mcp sync` CLI used by the\n * enforcement hooks. Fail-soft: returns `{ synced:false, reason }` when cloud\n * mode is off or auth is missing rather than throwing, so callers can no-op\n * cleanly.\n *\n * `lockOptions` overrides the lock's timing for tests; production passes none\n * and gets the documented defaults.\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 lockOptions: Omit<MemorySyncLockOptions, 'memoryDir' | 'sessionId'> = {},\n): Promise<MemorySyncResult> {\n // OPERATOR KILL SWITCH \u2014 first statement in the function, deliberately.\n // This is the shared entrypoint BOTH detached Stop-hook push paths reach, so\n // it is the one place that can stop a push that is already spawned. It runs\n // BEFORE the auth token fetch (a network call) and BEFORE withMemorySyncLock,\n // so a disabled run makes no request and \u2014 critically \u2014 never acquires the\n // lock it would then have to release. Turning the switch on can therefore\n // never strand a lock that blocks a later enabled run.\n const killSwitch = evaluateMemorySyncKillSwitch();\n if (killSwitch.disabled) {\n return { synced: false, reason: killSwitch.reason as string };\n }\n\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\n // A push over a slug with no memory directory has nothing to read and nothing\n // to serialize. Returning here \u2014 rather than inside pushMemory, after the lock\n // has already run mkdirSync \u2014 is what stops memory sync creating an empty\n // `memory/` in EVERY project dir on the machine (PR #9327 regression: the lock\n // moved directory creation ahead of pushMemory's not-exists early return).\n if (action === 'push' && !existsSync(memoryDir)) {\n return {\n synced: true, action: 'push', pushed: 0, created: 0, updated: 0, skipped: 0,\n index_rows_preserved: 0, knowledge_upserted: 0, knowledge_failed: 0, memory_dir: memoryDir,\n };\n }\n\n try {\n // `createDir` ONLY for pull, which is the action that writes files into it.\n return await withMemorySyncLock({ ...lockOptions, memoryDir, sessionId, createDir: action === 'pull' }, async (lock) => {\n const takeover = lock.tookOverFrom ? { lock_taken_over_from_pid: lock.tookOverFrom.pid } : {};\n if (action === 'pull') {\n const result = await pullMemory(baseUrl, token, memoryDir, fetchFn);\n return {\n synced: true,\n action: 'pull',\n pulled: result.pulled,\n files: result.files,\n memory_dir: memoryDir,\n ...takeover,\n };\n }\n // ONE cache object for both halves of the push, persisted ONCE at the end\n // so the bridge's knowledgeHash rows survive alongside the memoryHash rows.\n const deadline = createSyncDeadline();\n const cache = readPushCache(memoryDir, baseUrl);\n let result;\n try {\n result = await pushMemory(baseUrl, token, memoryDir, sessionId, fetchFn, { cache, deadline });\n } catch (err) {\n // A partial push still uploaded real files. Persisting what LANDED is\n // what makes the retry cheap \u2014 otherwise one 500 costs a full 140-trip\n // re-push on the next run, which is how the old behaviour never\n // converged. Only successes were recorded, so the failure retries.\n writePushCache(memoryDir, cache);\n throw err;\n }\n // Bridge: also upsert each memory file into the private-knowledge corpus so\n // vo_private_knowledge_context recall works cross-vendor. Fail-soft by\n // contract \u2014 the backup above is the critical path, so bridge failures are\n // reported in the result, never thrown (live-fire 2026-07-24: the recall\n // corpus was empty because nothing fed it). The import itself is inside the\n // guard: a mispackaged bundle failing to resolve the bridge module must\n // degrade to a reported bridge failure, not convert the completed backup\n // into synced:false (red-team F2; beta.18 bundling incident class).\n let bridge: { upserted: number; failed: number; skipped: number; failures: readonly string[] };\n try {\n const { upsertMemoryFilesAsKnowledge } = await import('./memory-knowledge-bridge.js');\n bridge = await upsertMemoryFilesAsKnowledge({\n controlPlaneUrl: baseUrl, token, memoryDir, fetchFn, cache, deadline,\n });\n } catch (err) {\n bridge = {\n upserted: 0, failed: 1, skipped: 0,\n failures: [`bridge unavailable: ${err instanceof Error ? err.message : String(err)}`],\n };\n }\n // Persist AFTER both halves. Best-effort by contract (see the module) \u2014\n // a state file we cannot write costs the next run a full re-push, never\n // this run's result.\n writePushCache(memoryDir, cache);\n return {\n synced: true,\n action: 'push',\n pushed: result.pushed,\n created: result.created,\n updated: result.updated,\n skipped: result.skipped,\n index_rows_preserved: result.indexRowsPreserved,\n memory_dir: memoryDir,\n knowledge_upserted: bridge.upserted,\n knowledge_failed: bridge.failed,\n knowledge_skipped: bridge.skipped,\n ...(bridge.failed > 0 ? { knowledge_failures: bridge.failures } : {}),\n ...takeover,\n };\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", "#!/usr/bin/env node\n/**\n * vo-mcp stdio entrypoint.\n *\n * Usage:\n * node dist/cli.js # starts on stdio with default paths\n * VO_MCP_EVENTS_PATH=... node dist/cli.js\n * VO_MCP_DB_PATH=... node dist/cli.js\n *\n * Defaults:\n * events file \u2192 $VO_MCP_EVENTS_PATH or ~/.claude/vo-mcp-events.jsonl\n * cache db \u2192 $VO_MCP_DB_PATH or ~/.claude/vo-mcp-cache.db\n *\n * The MCP protocol speaks JSON-RPC on stdio. Nothing else may write to\n * stdout \u2014 keep all diagnostics on stderr. We use console.error for\n * any boot messages.\n */\nimport { homedir, hostname } from 'node:os';\nimport { randomUUID } from 'node:crypto';\nimport { join } from 'node:path';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { createServer } from './server.js';\nimport { createSqliteCache } from './cache/sqlite-cache.js';\nimport { buildAdminCallableClientFromEnv } from './cloud/admin-callable-client.js';\nimport { createFileEventsWriter, defaultEventsPath } from './logging/events-writer.js';\nimport { createStubRatchetClient } from './ratchets/stub-client.js';\nimport { probeProviders, tryCreateEngineConsensusClientFromEnv } from './consensus/engine-client.js';\nimport { tryCreateMoatConsensusClientFromEnv } from './consensus/moat-client.js';\nimport { createConsensusFallbackClient } from './consensus/fallback-client.js';\nimport { withLocalConsensusCredentials } from './consensus/local-credential-env.js';\nimport { runLogin } from './cloud/login.js';\nimport { exchangeForVoCredential } from './cloud/vo-credential-exchange.js';\nimport { VO_MCP_VERSION } from './tools/common.js';\n\nfunction defaultCacheDbPath(): string {\n const env = process.env['VO_MCP_DB_PATH'];\n if (env && env.length > 0) return env;\n return join(homedir(), '.claude', 'vo-mcp-cache.db');\n}\n\n/**\n * Probe the installed `@algosuite/consensus-engine` package's semver from\n * its package.json. Returns null when the engine package isn't installed\n * (the production state today \u2014 the null-client takes over in that case).\n *\n * The version becomes part of the cache-key namespace (audit BLOCKER #3):\n * upgrading the engine package flips the namespace and invalidates the\n * cache, so cached verdicts from the previous engine version are not\n * silently returned by newer logic.\n */\nasync function probeEngineVersion(): Promise<string | null> {\n try {\n // Split string keeps the import opaque to bundle-analyzers \u2014 same\n // trick as engine-client.ts uses for the runtime engine import.\n const moduleName = ['@algosuite', 'consensus-engine'].join('/');\n const mod = (await import(`${moduleName}/package.json`, { with: { type: 'json' } })) as {\n default?: { version?: string };\n };\n const v = mod.default?.version;\n return typeof v === 'string' && v.length > 0 ? v : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Probe the vo-arch-defaults bundled corpus and compute a stable version\n * hash. Returns null when the KB package isn't installed.\n *\n * Loads the corpus synchronously inside an async wrapper. The corpus is\n * small (~30 rules at v1) so this is fast \u2014 ~10ms at startup.\n */\nasync function probeCorpusVersionHash(): Promise<string | null> {\n try {\n const moduleName = ['@algosuite', 'vo-arch-defaults'].join('/');\n const mod = (await import(moduleName)) as {\n loadBundledCorpus?: () => { rules: ReadonlyArray<{ rule_id: string; rule_version: number }> };\n computeCorpusVersionHash?: (\n rules: ReadonlyArray<{ rule_id: string; rule_version: number }>,\n ) => string;\n };\n if (typeof mod.loadBundledCorpus !== 'function') return null;\n if (typeof mod.computeCorpusVersionHash !== 'function') return null;\n const { rules } = mod.loadBundledCorpus();\n return mod.computeCorpusVersionHash(rules);\n } catch {\n return null;\n }\n}\n\n/**\n * Probe the installed `@algosuite/vo-arch-defaults` package's semver from\n * its package.json. Returns null when the KB package isn't installed.\n *\n * Added PR-Q 2026-05-25 to close a cache-correctness gap exposed by PR-P:\n * `corpusHash` (above) is computed from rule CONTENT (rule_id, rule_version).\n * It does NOT change when the EVALUATION LOGIC changes \u2014 e.g. PR-P's\n * `applies-to-files.ts` semantic flip (wholesale-drop \u2192 per-file include\n * AND not-exclude), or PR-R's `glob.ts` brace expansion. Both materially\n * change which rules apply to a given diff without touching corpus content.\n *\n * Mixing the KB package version into the cache namespace as a distinct\n * `kb_logic@<v>` component flips the namespace on every vo-arch-defaults\n * release \u2014 invalidating cached `vo_architecture_review` / `vo_check_*` /\n * `vo_verify_answer` envelopes that were produced under older evaluation\n * logic. Operators on a new vo-arch-defaults version get fresh verdicts\n * instead of silently-stale cached ones.\n */\nasync function probeArchDefaultsPkgVersion(): Promise<string | null> {\n try {\n const moduleName = ['@algosuite', 'vo-arch-defaults'].join('/');\n const mod = (await import(`${moduleName}/package.json`, { with: { type: 'json' } })) as {\n default?: { version?: string };\n };\n const v = mod.default?.version;\n return typeof v === 'string' && v.length > 0 ? v : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Assemble the cache-key version namespace. Components default to 'none'\n * when their source is unavailable. Format pinned by tests so cloud-side\n * downstream consumers can parse it if they need to.\n *\n * Format (PR-Q 2026-05-25 \u2014 4 components, was 3):\n * `vo_mcp@<v>|engine@<v|none>|corpus@<h|none>|kb_logic@<v|none>`\n *\n * Forward-compatible: parsers that `split('|')` continue to work; older\n * 3-component caches simply miss under the new 4-component key shape.\n */\nexport function buildCacheVersionNamespace(\n voMcpVersion: string,\n engineVersion: string | null,\n corpusHash: string | null,\n archDefaultsPkgVersion: string | null,\n): string {\n return (\n `vo_mcp@${voMcpVersion}` +\n `|engine@${engineVersion ?? 'none'}` +\n `|corpus@${corpusHash ?? 'none'}` +\n `|kb_logic@${archDefaultsPkgVersion ?? 'none'}`\n );\n}\n\nasync function loadTestEngineClient(modulePath: string): Promise<ReturnType<typeof tryCreateEngineConsensusClientFromEnv> | null> {\n // Phase 2 Lane C \u2014 test-only opt-in escape hatch for the stdio-spawned\n // integration test. The pointed-at module exports a default async\n // factory `createTestConsensusEngineClient(): ConsensusEngineClient`.\n // Production code paths (real credentials \u2192 real adapters) are\n // unaffected; the env var is undocumented and intended for tests only.\n try {\n const url = modulePath.startsWith('file:')\n ? modulePath\n : `file:///${modulePath.replace(/\\\\/g, '/')}`;\n const mod = (await import(url)) as { createTestConsensusEngineClient?: unknown };\n if (typeof mod.createTestConsensusEngineClient === 'function') {\n const client = (mod.createTestConsensusEngineClient as () => unknown)();\n if (client !== null && typeof client === 'object' && 'run' in client) {\n return client as ReturnType<typeof tryCreateEngineConsensusClientFromEnv>;\n }\n }\n console.error(`[vo-mcp] test-engine-module loaded but missing createTestConsensusEngineClient export: ${modulePath}`);\n return null;\n } catch (err) {\n console.error(`[vo-mcp] failed to load VO_MCP_TEST_ENGINE_MODULE=${modulePath}:`, err);\n return null;\n }\n}\n\nasync function main(): Promise<void> {\n // Probe version components for the cache-key namespace BEFORE constructing\n // the cache. All three probes are graceful: null when the package isn't\n // installed (the current production state for engine; vo-arch-defaults\n // is workspace-resident so its probes typically succeed).\n const [engineVersion, corpusHash, archDefaultsPkgVersion] = await Promise.all([\n probeEngineVersion(),\n probeCorpusVersionHash(),\n probeArchDefaultsPkgVersion(),\n ]);\n const cacheVersionNamespace = buildCacheVersionNamespace(\n VO_MCP_VERSION,\n engineVersion,\n corpusHash,\n archDefaultsPkgVersion,\n );\n console.error(`[vo-mcp] cache version namespace: ${cacheVersionNamespace}`);\n\n const cache = createSqliteCache({\n dbPath: defaultCacheDbPath(),\n cacheVersionNamespace,\n });\n const events = createFileEventsWriter();\n const ratchets = createStubRatchetClient();\n // Phase 2 Lane C \u2014 test-only escape hatch (undocumented for production).\n const testModule = process.env['VO_MCP_TEST_ENGINE_MODULE'];\n const testClient =\n testModule !== undefined && testModule.length > 0\n ? await loadTestEngineClient(testModule)\n : null;\n // Consensus client selection (Increment 2 \u2014 AlgoHQ Command Center unification \u00A75):\n // 1. an injected test client always wins;\n // 2. else LOCAL consensus uses \u22652 credentials from env / the OS keychain;\n // 3. the paid cloud moat is fallback-only when an entitlement is configured.\n // `tryCreateMoatConsensusClientFromEnv` returns null when no entitlement token\n // is configured, so existing local-key installs are unaffected (backward-compat).\n // 2026-05-24 audit MEDIUM \u2014 `tryCreateEngineConsensusClientFromEnv` ALWAYS\n // returns a non-null client; on the failure path it surfaces a specific reason\n // (`consensus-credentials-missing`, etc.), so it is the correct final fallback.\n const localEnv = withLocalConsensusCredentials();\n const localProviders = probeProviders(localEnv);\n for (const key of ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_API_KEY', 'DEEPSEEK_API_KEY', 'MODEL_API_KEY'] as const) {\n if (!process.env[key] && localEnv[key]) process.env[key] = localEnv[key];\n }\n const localConsensus = tryCreateEngineConsensusClientFromEnv({ envSource: localEnv });\n // The moat entitlement may come from the OS keychain (`vo-mcp set-key\n // --provider moat`) as well as the env \u2014 read it from the hydrated view so an\n // npm-installed runner (no local engine package) gets a working consensus path.\n const cloudConsensus = testClient ? null : tryCreateMoatConsensusClientFromEnv(localEnv);\n let consensus = testClient ?? localConsensus;\n if (!testClient && cloudConsensus && localProviders.length >= 2) {\n console.error(`[vo-mcp] local-first consensus active (${localProviders.join(', ')}); cloud moat is fallback-only`);\n consensus = createConsensusFallbackClient(localConsensus, cloudConsensus, {\n onFallback: (reason) => console.error(`[vo-mcp] local consensus unavailable (${reason}); using cloud moat fallback`),\n });\n } else if (!testClient && cloudConsensus) {\n console.error('[vo-mcp] fewer than 2 linked local providers; cloud moat consensus active');\n consensus = cloudConsensus;\n } else if (!testClient) {\n // No entitlement anywhere: on an npm install the local engine can never load, so\n // say so ONCE at boot instead of letting every tool call discover it.\n console.error('[vo-mcp] no VO_ENTITLEMENT_TOKEN (env or keychain via `vo-mcp set-key --provider moat`); consensus tools depend on a locally installed engine and will report engine-unavailable on an npm-installed runner');\n }\n\n // PR 3 \u2014 wire vo-control-plane admin proxy when env vars are set.\n // Returns null when VO_CONTROL_PLANE_URL + VO_CONTROL_PLANE_ADMIN_TOKEN\n // are unset (local mode \u2014 the default). Throws when exactly one is set.\n let adminCallables: ReturnType<typeof buildAdminCallableClientFromEnv> = null;\n try {\n adminCallables = buildAdminCallableClientFromEnv();\n } catch (err) {\n console.error(\n `[vo-mcp] admin-callable client config error \u2014 falling back to local mode: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n if (adminCallables) {\n console.error('[vo-mcp] cloud mode active \u2014 admin proxies forward to vo-control-plane');\n }\n\n const server = createServer({ cache, events, ratchets, consensus, adminCallables });\n const transport = new StdioServerTransport();\n\n process.on('SIGINT', () => {\n void shutdown();\n });\n process.on('SIGTERM', () => {\n void shutdown();\n });\n\n async function shutdown(): Promise<void> {\n try {\n await server.close();\n } finally {\n cache.close();\n events.close();\n process.exit(0);\n }\n }\n\n await server.connect(transport);\n // stderr only \u2014 stdout is reserved for JSON-RPC.\n console.error(\n `[vo-mcp] connected on stdio (events=${defaultEventsPath()}, db=${defaultCacheDbPath()})`,\n );\n}\n\nif (process.argv[2] === 'login') {\n // `vo-mcp login` \u2014 interactive per-user sign-in (Inc 3b). Retires the god-token\n // from the client config in favor of the user's own auto-refreshing credential.\n // Inc 3b.4b: when a control-plane URL is configured, exchange the captured\n // Firebase refresh token for a scoped, revocable vocred_ and store THAT instead.\n const controlPlaneUrl = process.env['VO_CONTROL_PLANE_URL']?.trim();\n // A host-derived label so the operator can identify + revoke THIS machine's\n // credential in the control-plane (`vo-mcp-cli@<hostname>`).\n const credentialLabel = `vo-mcp-cli@${hostname()}`.slice(0, 200);\n runLogin(\n controlPlaneUrl\n ? {\n exchange: (refreshToken, apiKey) =>\n exchangeForVoCredential({ refreshToken, apiKey, controlPlaneUrl, label: credentialLabel }),\n }\n : {},\n )\n // NO process.exit() here or in the sync path below: a hard exit tears down\n // the environment while @napi-rs/keyring's native async handle can still be\n // live, which on Windows trips libuv's `!(handle->flags & UV_HANDLE_CLOSING)`\n // assertion in src/win/async.c and kills the process with exit code 9 AFTER\n // the success line printed (observed 5/5 in non-interactive shells,\n // 2026-07-21) \u2014 violating the fail-soft exit-0 contract. Setting exitCode\n // and letting the loop drain exits cleanly, and measured drain time after\n // the work settles is <1ms (nothing else holds the loop).\n .then((r) => {\n console.error(`[vo-mcp] login successful${r.email ? ` as ${r.email}` : ''}. Credential stored at ${r.credentialPath}.`);\n console.error('[vo-mcp] You can now remove VO_CONTROL_PLANE_ADMIN_TOKEN (the god-token) from your MCP config.');\n console.error('[vo-mcp] NOTE: per-user auth requires VO_OPERATOR_ALLOWED_EMAILS (with your email) on the deployed control-plane.');\n })\n .catch((err: unknown) => {\n console.error('[vo-mcp] login failed:', err instanceof Error ? err.message : String(err));\n process.exitCode = 1;\n });\n} else if (process.argv[2] === 'sync') {\n // `vo-mcp sync <push|pull> [--cwd <path>]` \u2014 cloud memory sync from the shell,\n // so the SessionStart/PostToolUse enforcement hooks (and any non-Claude runtime)\n // can drive the SAME push/pull logic the `vo_sync_config` MCP tool uses. The\n // operator's #1 ask: a system that MAKES SURE local memory reaches the cloud.\n //\n // Fail-soft contract (so a hook NEVER blocks a session): exit 0 when sync\n // succeeds OR when cloud mode simply isn't configured here (no control-plane\n // URL / no stored credential). Exit 1 only on a real sync error (network/HTTP).\n const action = process.argv[3];\n if (action !== 'push' && action !== 'pull') {\n console.error('[vo-mcp] usage: vo-mcp sync <push|pull> [--cwd <path>] [--lock-wait-ms <n>]');\n // Hard exit is safe ONLY here: nothing async has started and the native\n // keychain module has not loaded yet (it loads lazily on credential read).\n process.exit(2);\n }\n const cwdFlag = process.argv.indexOf('--cwd');\n const cwd =\n cwdFlag >= 0 && typeof process.argv[cwdFlag + 1] === 'string'\n ? (process.argv[cwdFlag + 1] as string)\n : process.cwd();\n // The caller's contention budget, so the lock's wait and the caller's own\n // deadline are ONE number instead of two independent guesses. The Stop hook\n // runs this detached and passes a large value; the SessionStart pull hook\n // passes one that fits inside its own ceiling. Absent/garbage \u2192 the module\n // default (fail-closed: never widened by a bad argument).\n const waitFlag = process.argv.indexOf('--lock-wait-ms');\n const parsedWait = waitFlag >= 0 ? Number(process.argv[waitFlag + 1]) : Number.NaN;\n const lockOptions = Number.isFinite(parsedWait) && parsedWait >= 0 ? { waitMs: parsedWait } : {};\n // A fresh watermark per CLI run \u2014 V1 storage is last-write-wins; the watermark\n // records WHICH session last wrote each entry for future conflict detection.\n const sessionId = randomUUID();\n // Durable sync log (red-team F3): the SessionStart/Stop hook spawns this\n // detached with stdio:'ignore', so console output is discarded \u2014 a silently\n // failing knowledge bridge (403s, route drift) would leave the recall corpus\n // empty forever with NO visible signal, the exact reservoir-empty bug the\n // bridge exists to fix. Append every outcome (incl. knowledge_upserted /\n // knowledge_failed) to a file so it's inspectable regardless of stdio.\n const appendSyncLog = async (line: string) => {\n try {\n const { appendFileSync, mkdirSync } = await import('node:fs');\n const { join } = await import('node:path');\n const { homedir } = await import('node:os');\n const dir = join(homedir(), '.claude');\n mkdirSync(dir, { recursive: true });\n appendFileSync(join(dir, 'vo-mcp-sync.log'), `${line}\\n`, 'utf8');\n } catch { /* logging must never affect the sync outcome */ }\n };\n import('./tools/memory/sync-config.js')\n .then(async ({ runMemorySync, isNoopSyncReason }) => {\n const r = await runMemorySync(action, cwd, sessionId, undefined, lockOptions);\n const stamp = `${sessionId} ${action}`;\n // exitCode + natural drain, never process.exit() \u2014 see the login path\n // above for why (Windows @napi-rs/keyring teardown race \u2192 exit code 9).\n if (r.synced) {\n console.error(`[vo-mcp] sync ${action} ok: ${JSON.stringify(r)}`);\n await appendSyncLog(`ok ${stamp} ${JSON.stringify(r)}`);\n } else if (isNoopSyncReason(r.reason)) {\n // No-op (cloud off / no credential) \u2192 exit 0 so hooks treat it as a skip.\n console.error(`[vo-mcp] sync ${action} skipped: ${r.reason}`);\n await appendSyncLog(`skip ${stamp} ${r.reason ?? ''}`);\n } else {\n console.error(`[vo-mcp] sync ${action} failed: ${r.reason}`);\n await appendSyncLog(`FAIL ${stamp} ${r.reason ?? ''}`);\n process.exitCode = 1;\n }\n })\n .catch(async (err: unknown) => {\n const message = err instanceof Error ? err.message : String(err);\n console.error('[vo-mcp] sync fatal:', message);\n await appendSyncLog(`FATAL ${sessionId} ${action} ${message}`);\n process.exitCode = 1;\n });\n} else {\n main().catch((err: unknown) => {\n console.error('[vo-mcp] fatal:', err);\n process.exit(1);\n });\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';\nimport * as privateKnowledge from './tools/memory/private-knowledge.js';\nimport * as hqWhiteboard from './tools/hq/whiteboard.js';\nimport * as skillCorpus from './tools/skills/skill-corpus.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 [privateKnowledge.UPSERT_TOOL_NAME]: {\n definition: {\n name: privateKnowledge.UPSERT_TOOL_NAME,\n description: privateKnowledge.upsertDescription,\n inputSchema: privateKnowledge.upsertInputSchema,\n },\n handler: privateKnowledge.handlePrivateKnowledgeUpsert,\n },\n [privateKnowledge.CONTEXT_TOOL_NAME]: {\n definition: {\n name: privateKnowledge.CONTEXT_TOOL_NAME,\n description: privateKnowledge.contextDescription,\n inputSchema: privateKnowledge.contextInputSchema,\n },\n handler: privateKnowledge.handlePrivateKnowledgeContext,\n },\n [privateKnowledge.INVALIDATE_TOOL_NAME]: {\n definition: {\n name: privateKnowledge.INVALIDATE_TOOL_NAME,\n description: privateKnowledge.invalidateDescription,\n inputSchema: privateKnowledge.invalidateInputSchema,\n },\n handler: privateKnowledge.handlePrivateKnowledgeInvalidate,\n },\n [privateKnowledge.STALE_TOOL_NAME]: {\n definition: {\n name: privateKnowledge.STALE_TOOL_NAME,\n description: privateKnowledge.staleDescription,\n inputSchema: privateKnowledge.staleInputSchema,\n },\n handler: privateKnowledge.handlePrivateKnowledgeStale,\n },\n [hqWhiteboard.POST_TOOL_NAME]: {\n definition: {\n name: hqWhiteboard.POST_TOOL_NAME,\n description: hqWhiteboard.postDescription,\n inputSchema: hqWhiteboard.postInputSchema,\n },\n handler: hqWhiteboard.handleHqWhiteboardPost,\n },\n [hqWhiteboard.READ_TOOL_NAME]: {\n definition: {\n name: hqWhiteboard.READ_TOOL_NAME,\n description: hqWhiteboard.readDescription,\n inputSchema: hqWhiteboard.readInputSchema,\n },\n handler: hqWhiteboard.handleHqWhiteboardRead,\n },\n [skillCorpus.LIST_TOOL_NAME]: {\n definition: {\n name: skillCorpus.LIST_TOOL_NAME,\n description: skillCorpus.listDescription,\n inputSchema: skillCorpus.listInputSchema,\n },\n handler: skillCorpus.handleSkillList,\n },\n [skillCorpus.GET_TOOL_NAME]: {\n definition: {\n name: skillCorpus.GET_TOOL_NAME,\n description: skillCorpus.getDescription,\n inputSchema: skillCorpus.getInputSchema,\n },\n handler: skillCorpus.handleSkillGet,\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 * 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 * 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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 * 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_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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 ...(engineResult.quorum_failed === true ? { quorum_failed: true } : {}),\n // The verification receipt (moat decision_id) \u2014 the thing an agent pastes as `receipt id: <uuid>`.\n ...(engineResult.receipt_id ? { receipt_id: engineResult.receipt_id } : {}),\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 * 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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 ...(engineResult.quorum_failed === true ? { quorum_failed: true } : {}),\n // The verification receipt (moat decision_id) \u2014 the thing an agent pastes as `receipt id: <uuid>`.\n // NOTE: a content-hash cache hit replays the ORIGINAL call's receipt_id (same claim, same verdict, no new spend) \u2014\n // a receipt asserts the stage ran for this claim, not one-receipt-per-call.\n ...(engineResult.receipt_id ? { receipt_id: engineResult.receipt_id } : {}),\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 // Critique-uptake (2026-07-20 red-team fix) \u2014 the engine computes this\n // on every call; this spread closes the gap where the visibility report\n // was itself silently dropped at the payload boundary.\n ...(engineResult.critique_uptake !== undefined\n ? { critique_uptake: engineResult.critique_uptake }\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 * 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_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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 aggregateEventTokenUsage,\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 ...aggregateEventTokenUsage(engineResult.per_model_verdicts, engineResult.token_usage),\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 * 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 * Dispatches through `buildCloudOrStubResponse` and reaches the cloud\n * callable for real when cloud mode is configured; the `unimplemented`\n * envelope is the unconfigured fallback only. A live call additionally\n * requires a FOUNDING-operator credential \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_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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope with structured normalized_input when cloud mode is not configured.\";\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 * 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`.\n *\n * The cloud wiring SHIPPED. Every tool in this family now dispatches\n * through `buildCloudOrStubResponse`, which makes a real request to the\n * control plane's `/api/v1/admin/*` surface whenever cloud mode is\n * configured on the session. The \"unimplemented\" envelope is now only the\n * UNCONFIGURED fallback \u2014 it is not the permanent state, and the tool\n * descriptions must not advertise it as one.\n *\n * Two conditions therefore govern a live call, and both are real:\n * 1. Cloud mode must be configured (`vo-mcp login`). Without it the\n * dispatcher returns the stub envelope without touching the network.\n * 2. The caller must hold a FOUNDING-operator credential. The control\n * plane's scoped-operator gate allows exactly one `/api/v1/admin/*`\n * path (GET /api/v1/admin/receipts); every other admin path returns\n * HTTP 403 `scoped_operator_forbidden` for a tenant-scoped account.\n * Proven live: vo_list_pending_prs returns that 403 from\n * /api/v1/admin/pr/list \u2014 a response a stub cannot produce.\n *\n * Keep this header and the per-tool `description` strings in agreement\n * with both conditions. An agent reads the tool list BEFORE deciding to\n * call, so a description claiming \"unimplemented\" suppresses correct\n * calls and under-reports the shipped surface.\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 is not configured on this session, so the admin callable was not reached. The wiring exists (see buildCloudOrStubResponse); sign in with `vo-mcp login` to route this tool to the control plane. Note that /api/v1/admin/* additionally requires a founding-operator credential.';\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_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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Tool: `vo_list_pending_prs`\n *\n * Wraps `voListPendingPRs()` admin callable (no parameters). Returns\n * the list of open AlgoHQ-source pull requests with blocker / source /\n * tester / specialist-context metadata.\n *\n * Founder-scoped admin call \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 AlgoHQ-source pull requests with blocker / source / tester / specialist-context metadata. Read-only diagnostic for Command Center reads. Wraps `voListPendingPRs` admin Cloud Function. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * 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 * The cloud wiring SHIPPED \u2014 these tools dispatch through\n * `buildCloudOrStubResponse` and reach `/api/v1/admin/pr/*` for real when\n * cloud mode is configured. The stub envelope is the UNCONFIGURED\n * fallback only. A live call additionally requires a FOUNDING-operator\n * credential; a tenant-scoped account receives HTTP 403\n * `scoped_operator_forbidden`. See common-heal.ts header for the full\n * rationale and the live evidence.\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 is not configured on this session, so the admin callable was not reached. The wiring exists (see buildCloudOrStubResponse); sign in with `vo-mcp login` to route this tool to the control plane. Note that /api/v1/admin/* additionally requires a founding-operator credential.';\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_merge_pr`\n *\n * Wraps `voMergePR({ prNumber })` admin callable. Approves + merges a\n * single AlgoHQ-source PR by number (refuses non-AlgoHQ PRs server-side).\n *\n * Founder-scoped admin call \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 AlgoHQ-source pull request by number. Wraps `voMergePR` admin Cloud Function (server-side refuses non-AlgoHQ PRs with permission-denied). Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 AlgoHQ-source PRs and merges/auto-merges each. Returns a summary\n * of merged / accepted / total counts plus per-PR results.\n *\n * Founder-scoped admin call \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 AlgoHQ-source pull requests and merges (or arms auto-merge) on each. Returns counts of merged / accepted / total plus per-PR results. Wraps `voApproveAllFixes` admin Cloud Function. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 * Founder-scoped admin call \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 an AlgoHQ pull request and dispatches a self-heal pass to retry the same focus page. Cloud callable refuses non-AlgoHQ PRs and respects the self-heal kill switch + per-PR retry block. Wraps `voRejectAndRetry` admin Cloud Function. Requires a founding-operator credential: the control plane's scoped-operator gate refuses `/api/v1/admin/*` for tenant-scoped accounts, which receive HTTP 403 `scoped_operator_forbidden`. Falls back to a clearly-marked `unimplemented` envelope when cloud mode is not configured.\";\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 aggregateEventTokenUsage,\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 AlgoHQ-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 AlgoHQ PRs (already merged/closed, or not an AlgoHQ-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 ...aggregateEventTokenUsage(result.per_model_verdicts, result.token_usage),\n });\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 plus a user/scoped VO\n * credential (or the legacy admin token) is available, the tool forwards reports\n * to the deployed vo-control-plane HTTP API. Auto-allocates the session on first\n * report (404 from report-state \u2192 allocate \u2192 retry). Interactive agents (Claude\n * Code, Cursor, Codex, Continue) now appear on 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';\nimport { createAuthTokenSourceFromEnv } from '../../cloud/auth-token-source.js';\nimport { readStoredCredential } from '../../cloud/credential-store.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 AlgoHQ and returns a directive: 'continue' (under 70%), 'prepare_handoff' (70-84%), or 'execute_handoff_now' (\u226585%). Implements V1 launch gate #9 (fleet context lifecycle management) per the official AlgoHQ roadmap. Cloud-control-plane mode when VO_CONTROL_PLANE_URL plus a user/scoped HQ credential (or legacy admin token) is available; auto-allocates the session on first report so interactive agents (Claude Code, Cursor, Codex, Continue) appear on the live fleet whiteboard. Stub-local fallback when cloud config is absent or fails. The response shape stays stable across modes (`backend_mode` field in the payload tells the caller which mode produced the verdict).\";\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. Uses the same auth precedence as other cloud-mode tools:\n * explicit user tokens, stored scoped `vo_credential`, then legacy admin token.\n * When VO_TENANT_ID is absent, `/auth/me` supplies the operator's own tenant.\n */\ninterface CloudConfig {\n readonly url: string;\n readonly token: string;\n readonly tenant_id: string;\n readonly operator_id?: string;\n}\n\ninterface AuthMeResponse {\n readonly ok?: boolean;\n readonly provisioned?: boolean;\n readonly operator_id?: string | null;\n readonly tenant_id?: string | null;\n}\n\ntype FetchLike = typeof fetch;\n\nasync function fetchCloudIdentity(\n url: string,\n token: string,\n fetchFn: FetchLike,\n): Promise<{ operator_id: string; tenant_id: string } | null> {\n try {\n const response = await fetchFn(`${url}/api/v1/auth/me`, {\n method: 'GET',\n headers: {\n 'Authorization': `Bearer ${token}`,\n },\n });\n if (!response.ok) return null;\n const data = await response.json() as AuthMeResponse;\n if (!data.ok || !data.provisioned || !data.operator_id || !data.tenant_id) return null;\n return { operator_id: data.operator_id, tenant_id: data.tenant_id };\n } catch {\n return null;\n }\n}\n\nasync function getCloudConfig(fetchFn: FetchLike = fetch): Promise<CloudConfig | null> {\n const url = process.env['VO_CONTROL_PLANE_URL']?.trim();\n if (!url) return null;\n\n const tokenSource = createAuthTokenSourceFromEnv(\n process.env,\n fetchFn,\n () => readStoredCredential(process.env),\n );\n const token = await tokenSource?.getToken();\n if (!token) return null;\n\n const tenant_id = process.env['VO_TENANT_ID']?.trim();\n if (tenant_id) return { url, token, tenant_id };\n\n const identity = await fetchCloudIdentity(url, token, fetchFn);\n if (!identity) return null;\n return { url, token, tenant_id: identity.tenant_id, operator_id: identity.operator_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\ninterface CloudAllocateSessionResponse {\n readonly session?: {\n readonly session_id?: unknown;\n };\n}\n\nasync function tryCloudReportState(\n cloud: CloudConfig,\n input: ToolInput,\n fetchFn: FetchLike = fetch,\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 fetchFn(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: cloud.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 fetchFn(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 const allocateData: CloudAllocateSessionResponse = await allocateResponse.json() as CloudAllocateSessionResponse;\n const retrySessionId = typeof allocateData.session?.session_id === 'string'\n && allocateData.session.session_id.length > 0\n ? allocateData.session.session_id\n : input.session_id;\n const retryReportUrl = `${cloud.url}/api/v1/session/${retrySessionId}/report-state`;\n\n // Retry the report-state call\n response = await fetchFn(retryReportUrl, {\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 = await 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 * 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_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';\nimport {\n admitSubagentSpawn,\n agentBindingRefusal,\n childBindingEnvFragment,\n inheritSwarmTierBinding,\n SWARM_TIER_BINDING_ENV,\n} from '../../swarm/tier-binding.js';\nimport { resolveSuccessorLaunch } from '../../swarm/successor-launch.js';\nimport { claimSpawnSlot, resolveLedgerDir, type ClaimSpawnSlot } from '../../swarm/spawn-ledger.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 agent: {\n type: 'string',\n description:\n \"Which agent to spawn ('claude' | 'codex'). Normally omitted: the agent comes from the swarm tier binding inherited via \" +\n `${SWARM_TIER_BINDING_ENV}. When a binding IS inherited this may only RESTATE the bound agent \u2014 an agent that contradicts ` +\n 'the binding is REFUSED, because a different agent is a different payer and the payer was decided once, at admission.',\n },\n },\n required: [],\n additionalProperties: false,\n};\n\n/**\n * The retired counter input. Kept as a NAME so a stale caller is told what\n * happened, never as a value the gate reads.\n *\n * `spawns_so_far` was a declared tool input: the model being bounded supplied\n * its own spend counter, and an ABSENT one read as zero. Reproduced end-to-end,\n * one process performed 25 real spawns from a budget-3 binding by omitting it.\n * Ignoring the field silently would leave a caller believing it still bounds\n * something, so supplying it is an explicit error.\n */\nexport const RETIRED_COUNTER_INPUT = 'spawns_so_far';\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 readonly agent?: 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['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 if (o['agent'] !== undefined && typeof o['agent'] !== 'string') return false;\n return true;\n}\n\n/** Reason to reject a call that still carries the retired counter, else null. */\nexport function retiredCounterRefusal(v: unknown): string | null {\n if (typeof v !== 'object' || v === null) return null;\n if (!(RETIRED_COUNTER_INPUT in (v as Record<string, unknown>))) return null;\n return (\n `\\`${RETIRED_COUNTER_INPUT}\\` is no longer accepted: a spawn counter supplied by the process being ` +\n 'bounded bounds nothing, and an absent one read as zero. The fan-out ceiling is now enforced by the ' +\n 'durable per-swarm spawn ledger; remove the field.'\n );\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 an AlgoHQ 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 AlgoHQ 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\n/**\n * Decide WHAT to spawn, honouring an inherited swarm tier binding.\n *\n * Backward compatibility is deliberate and bounded. When NO binding is present\n * in the env \u2014 which is every caller today \u2014 this returns exactly the legacy\n * claude launch, so wiring the precedence in does not take a working tool\n * offline. The new refusal and the new codex path fire ONLY when a fan-out has\n * actually done admission and exported a binding. That is the full answer to\n * \"what newly executes\".\n *\n * When a binding IS present it is authoritative in both directions: it can bind\n * the spawn to codex, and it can REFUSE the spawn outright (tier `refused` /\n * `unresolved`, or a budget already spent). A refusal here is the point \u2014 it is\n * the mechanism that stops a fan-out spilling onto a second payer halfway\n * through.\n *\n * Exported for tests.\n */\nexport function resolveSpawnPlan(\n env: Record<string, string | undefined>,\n input: { readonly agent?: string | undefined; readonly max_turns?: number | undefined },\n nowIso: string,\n platform: string = process.platform,\n claim: ClaimSpawnSlot = claimSpawnSlot,\n):\n | {\n readonly ok: true;\n readonly bin: string;\n readonly args: readonly string[];\n readonly agent: string;\n readonly tier: string;\n readonly bound: boolean;\n readonly env: Record<string, string>;\n readonly slot: number | null;\n /** The spend the ledger DEBITED for this spawn, USD. null when uncapped. */\n readonly capUsd: number | null;\n /** Pool left after that debit, USD. null when the fan-out has no pool. */\n readonly capRemainingUsd: number | null;\n }\n | { readonly ok: false; readonly reason: string; readonly tier: string } {\n // Read once into a local so the narrowing survives, rather than re-indexing and\n // asserting non-null. A `!` here tripped @typescript-eslint/no-non-null-assertion,\n // and this repo's lint floor is 0 warnings \u2014 so it turned `Main health` red on main\n // and, while red, that required gate silently evicts every open PR.\n const rawBinding = env[SWARM_TIER_BINDING_ENV];\n const hasBinding = typeof rawBinding === 'string' && rawBinding.trim().length > 0;\n\n if (!hasBinding) {\n // Legacy path, unchanged: no fan-out admission happened, so there is no\n // tier to honour and nothing to refuse on behalf of.\n const explicit = input.agent?.trim();\n if (explicit) {\n const resolved = resolveSuccessorLaunch({ agent: explicit, maxTurns: input.max_turns, platform });\n if (!resolved.ok) return { ok: false, reason: resolved.reason, tier: 'unbound' };\n return {\n ok: true,\n bin: resolved.bin,\n args: resolved.args,\n agent: resolved.agent,\n tier: 'unbound',\n bound: false,\n env: {},\n slot: null,\n capUsd: null,\n capRemainingUsd: null,\n };\n }\n return {\n ok: true,\n bin: 'claude',\n args: buildSuccessorArgs(input.max_turns),\n agent: 'claude',\n tier: 'unbound',\n bound: false,\n env: {},\n slot: null,\n capUsd: null,\n capRemainingUsd: null,\n };\n }\n\n const binding = inheritSwarmTierBinding(env, nowIso);\n // No counter argument: `admitSubagentSpawn` here asks only \"does this binding\n // still carry allowance\". The number of spawns already performed is NOT the\n // caller's to assert \u2014 it comes from the ledger, below.\n const admission = admitSubagentSpawn(binding);\n if (!admission.allowed) {\n return { ok: false, reason: admission.reason, tier: binding.tier };\n }\n // A caller-supplied `agent` may RESTATE the bound agent; it may never\n // contradict it. `agent` is model-supplied, and a different agent is a\n // different vendor's subscription or key \u2014 a different PAYER. This used to be\n // `input.agent?.trim() || binding.agent` with no check, which meant the\n // comment above it (\"can never change the tier\") was documentation the code\n // did not implement, and admission-time binding was defeatable by its own\n // caller.\n const agentRefusal = agentBindingRefusal(binding, input.agent);\n if (agentRefusal !== null) return { ok: false, reason: agentRefusal, tier: binding.tier };\n const resolved = resolveSuccessorLaunch({\n agent: binding.agent,\n maxTurns: input.max_turns,\n platform,\n });\n if (!resolved.ok) return { ok: false, reason: resolved.reason, tier: binding.tier };\n // LAST, so a spawn refused for any other reason does not consume allowance.\n // This is the counter that replaces the model-supplied one: a durable slot in\n // a per-swarm directory, shared with the plugin spawner on the same host, and\n // fail-closed if it cannot be read or written.\n const slot = claim({\n swarmId: binding.swarm_id,\n proposedCeiling: binding.subagent_budget,\n // The spend-cap POOL, recorded once per swarm exactly like the ceiling. The\n // child's cap is DEBITED from it below, not recomputed from this binding.\n proposedCapUsd: binding.spend_cap_usd,\n dir: resolveLedgerDir(env),\n nowIso,\n });\n if (!slot.ok) return { ok: false, reason: slot.reason, tier: binding.tier };\n return {\n ok: true,\n bin: resolved.bin,\n args: resolved.args,\n agent: resolved.agent,\n tier: binding.tier,\n bound: true,\n // Re-export the same TIER with a DECREMENTED budget and the spend cap the\n // ledger just DEBITED. Exporting the binding verbatim (what this did before\n // #9312) meant the child re-read the full budget and every generation\n // restarted at zero. Recomputing the cap from THIS binding (what #9312 did)\n // bounded a chain but not a tree: three siblings each re-halved the parent's\n // untouched $50 and walked away with $75 between them.\n env: childBindingEnvFragment(binding, slot.capUsd),\n slot: slot.slot,\n capUsd: slot.capUsd,\n capRemainingUsd: slot.capRemainingUsd,\n };\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 const retired = retiredCounterRefusal(rawInput);\n if (retired !== null) throw invalidParams(TOOL_NAME, retired);\n if (!isToolInput(rawInput)) {\n throw invalidParams(TOOL_NAME, 'invalid input. Optional: { handoff_path, goal, cwd, max_turns, agent }.');\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 // Honour the fan-out's ONE tier decision before spending anything. Resolved\n // BEFORE the log file is opened so a refusal leaves no stray artifacts.\n const plan = resolveSpawnPlan(process.env, rawInput, new Date().toISOString());\n if (!plan.ok) {\n return jsonContent({\n tool: TOOL_NAME,\n schema_version: 1,\n payload: {\n spawned: false,\n reason: `swarm tier binding refused this spawn: ${plan.reason}`,\n tier: plan.tier,\n handoff_path: handoffPath,\n },\n });\n }\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(plan.bin, [...plan.args], {\n cwd: rawInput.cwd?.trim() || process.cwd(),\n detached: true,\n stdio: ['pipe', logFd, logFd],\n // Windows: the agent CLIs are .cmd shims \u2014 they need a shell to resolve.\n // The prompt goes via STDIN below, never argv, so the shell never sees it.\n shell: process.platform === 'win32',\n windowsHide: true,\n // Carry the SAME binding to the child. Without this the successor inherits\n // no tier and re-resolves its own \u2014 which is the split-payer defect one\n // generation down.\n ...(plan.bound ? { env: { ...process.env, ...plan.env } } : {}),\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}`, agent: plan.agent, tier: plan.tier, handoff_path: handoffPath }\n : {\n spawned: true,\n pid: child.pid ?? null,\n log_path: logPath,\n handoff_path: handoffPath,\n agent: plan.agent,\n tier: plan.tier,\n tier_bound: plan.bound,\n ledger_slot: plan.slot,\n // The debit, surfaced so an operator can reconcile a fan-out's spend\n // against the pool without reading the ledger directory by hand.\n ledger_cap_usd: plan.capUsd,\n ledger_cap_remaining_usd: plan.capRemainingUsd,\n },\n });\n}\n", "/**\n * Swarm tier binding \u2014 ONE billing-tier decision per fan-out, taken at\n * admission, recorded, and INHERITED by every subagent in that fan-out.\n *\n * ## The defect this closes\n *\n * The /goal precedence (connected subscription CLI or local model \u2192 the user's\n * OWN API keys \u2192 the platform owner's keys, last and catalogued) is resolved\n * PER SPAWN everywhere it is resolved at all. A fan-out has no tier concept, so\n * a 20-agent swarm can legitimately half-run on the operator's subscription and\n * half bill the platform: each spawn re-asks the question, and the answer can\n * legitimately change mid-swarm (a weekly window fills, a runner drops, a key\n * rotates). That is confusing to the user and unreconcilable for billing.\n *\n * Resolve ONCE, at admission, and hand every subagent the same answer.\n *\n * ## THE HARD RULE (inherited from the durable capability record)\n *\n * ABSENT, CORRUPT, OR STALE INPUT READS AS 'unresolved'. NEVER 'subscription'.\n *\n * `resolveOperatorCapability` in the control plane already enforces this on the\n * way in; this module must not undo it on the way out. Every failure path here\n * degrades to `unresolved`, and `unresolved` does not admit a spawn. Inferring\n * a flat-cost tier from silence would route paid work onto an assumption.\n *\n * ## EXHAUSTED IS NOT CONNECTED\n *\n * A connected subscription can be USED UP, not merely absent \u2014 #9283 measured\n * `seven_day_used_pct` 91 on a live account. An exhausted subscription that\n * still reports `authenticated: true` would otherwise bind a whole swarm to\n * tier 1 and then fail every spawn, or silently spill onto a paid fallback one\n * subagent at a time. Exhaustion is therefore a first-class disqualifier with\n * its own reason string, not a missing capability.\n *\n * ## What this record is NOT\n *\n * It is a CONSISTENCY mechanism, not an authorization one. It travels in the\n * process env alongside `VO_RUNNER_PREFER_KEY` and friends and carries exactly\n * their trust level. Billing authority stays server-side; a binding can name a\n * tier but it can never GRANT one. Consumers must treat it the way the\n * capability record treats `last_seen_runner_id`: diagnostics and coordination,\n * never an authz input.\n */\n\n/**\n * Tiers, in the operator's precedence order. `refused` and `unresolved` are\n * terminal states, not fallbacks \u2014 neither admits a spawn.\n *\n * `refused` is the operator's explicit decision for \"runner offline\": REFUSE by\n * default, with a paid fallback only via per-user opt-in plus a spend cap.\n * `unresolved` is the fail-closed answer when we could not decide at all.\n */\nexport const SWARM_TIERS = Object.freeze([\n 'tier1_subscription',\n 'tier1_local',\n 'tier2_user_key',\n 'tier3_platform_key',\n 'refused',\n 'unresolved',\n] as const);\nexport type SwarmTier = (typeof SWARM_TIERS)[number];\n\n/** The two tiers that spend the platform owner's money or none at all. */\nexport const TIER_ADMITS_SPAWN: ReadonlySet<SwarmTier> = new Set<SwarmTier>([\n 'tier1_subscription',\n 'tier1_local',\n 'tier2_user_key',\n 'tier3_platform_key',\n]);\n\n/**\n * The env var that carries a binding to every subagent.\n *\n * MUST be listed in SAFE_ENV_NAMES in\n * scripts/virtual-office/code-runner/agent-process-env.mjs. The daemon builds\n * every child env through `buildAgentProcessEnv`, which DROPS any name absent\n * from that set \u2014 the exact way #9242's `VO_RUNNER_PREFER_KEY` shipped inert\n * until #9247 added the line. A binding that cannot cross the process boundary\n * is a binding that binds nothing.\n */\nexport const SWARM_TIER_BINDING_ENV = 'VO_SWARM_TIER_BINDING';\n\n/**\n * At or above this percentage of a metered subscription window, the\n * subscription is treated as EXHAUSTED for admission purposes.\n *\n * Deliberately below 100: admission binds a WHOLE fan-out, so the question is\n * not \"is there one call left\" but \"will this subscription carry N subagents\".\n * Binding 20 agents to a window at 97% spends the operator's remaining headroom\n * on the first two and strands the rest. 95 leaves a margin that is honest\n * about the fan-out's shape without being so conservative that a normally-busy\n * account never qualifies. A null reading is NOT exhaustion \u2014 it is unknown,\n * and unknown is handled by the hard rule, not by this threshold.\n */\nexport const SUBSCRIPTION_EXHAUSTED_PCT = 95;\n\n/** Hard ceiling on subagents one binding may admit. Mirrors the coordinator's\n * platform limit (MAX_COORDINATOR_AGENTS in @algosuite/vo-resume-engine). */\nexport const MAX_BOUND_SUBAGENTS = 20;\n\n/** A usage reading for one agent, as the heartbeat carries it. */\nexport interface AgentUsageWindows {\n readonly seven_day_used_pct?: number | null;\n readonly five_hour_used_pct?: number | null;\n readonly monthly_used_pct?: number | null;\n}\n\n/**\n * One capability row, shaped to match `ResolvedCapabilityAgent` from\n * cloud-run/vo-control-plane/src/schema/operator-capability-v1.ts. Structural\n * rather than an import: vo-mcp ships to npm and cannot depend on the\n * control-plane package. `test/swarm/tier-binding-lockstep.test.ts` pins the\n * two shapes together the way runner-agent-registry-lockstep.test.ts does.\n */\nexport interface CapabilityAgentRow {\n readonly agent: string;\n readonly installed: boolean;\n readonly authenticated: boolean;\n /** 'subscription' | 'api_key' | 'local' | 'unknown'. */\n readonly auth_tier: string;\n readonly stale: boolean;\n readonly usage?: AgentUsageWindows | null;\n}\n\nexport interface ResolveSwarmTierInput {\n /** Stable id of the fan-out this binding governs. */\n readonly swarm_id: string;\n /** Rows from `resolveOperatorCapability`. Empty when the record is missing. */\n readonly agents: readonly CapabilityAgentRow[];\n /** Local models this operator has been observed serving. */\n readonly local_models?: readonly string[];\n /**\n * Is NO runner currently beating? REQUIRED, and fail-closed on read.\n *\n * This was optional until #9303's review: an ABSENT reading fell through the\n * `=== true` check and went on to BIND A TIER, which is the one input in this\n * function that must not do that. The operator's decision for runner-offline\n * is REFUSE by default, and the module's hard rule is that absent input never\n * resolves upward. Required in the type so a TS caller cannot omit it, and\n * checked against an explicit `false` at runtime so an untyped caller (JSON,\n * JS, a hand-built object) cannot omit it either.\n */\n readonly runner_offline: boolean;\n /** Per-user opt-in to a paid fallback. Required for tier3. */\n readonly paid_fallback_opt_in?: boolean;\n /** Spend cap accompanying that opt-in, USD. Required for tier3. */\n readonly paid_fallback_cap_usd?: number | null;\n /** True when the operator has their own provider key stored. */\n readonly has_user_api_key?: boolean;\n /** How many subagents this fan-out intends to run. */\n readonly requested_subagents: number;\n readonly now_iso: string;\n}\n\nexport interface SwarmTierBinding {\n readonly schema_version: 1;\n readonly swarm_id: string;\n readonly tier: SwarmTier;\n /** Which agent the fan-out is bound to, or null when no tier admits a spawn. */\n readonly agent: string | null;\n /** Why this tier, in one line. Always populated, including on refusal. */\n readonly reason: string;\n /** Agents disqualified specifically by exhaustion \u2014 never silently dropped. */\n readonly exhausted_agents: readonly string[];\n /** Max subagents this binding admits. 0 when the tier admits no spawn. */\n readonly subagent_budget: number;\n /** Spend cap for a tier3 binding, USD. null for every other tier. */\n readonly spend_cap_usd: number | null;\n readonly resolved_at: string;\n}\n\nfunction clampSubagents(requested: number): number {\n if (!Number.isFinite(requested) || requested < 1) return 0;\n return Math.min(Math.floor(requested), MAX_BOUND_SUBAGENTS);\n}\n\n/**\n * Is this a usable tier-3 spend cap?\n *\n * ONE predicate, used by the resolver, the parser, and the admission check, so\n * \"capped\" cannot mean three different things. A STRING cap ('99999') is NOT a\n * cap: nothing downstream compares it numerically, so honouring it would be\n * honouring a number nobody can enforce.\n */\nexport function isPositiveCap(cap: unknown): cap is number {\n return typeof cap === 'number' && Number.isFinite(cap) && cap > 0;\n}\n\n/**\n * Is this agent's subscription window used up?\n *\n * A null/absent reading is NOT exhaustion \u2014 it is unknown. Treating unknown as\n * exhausted would refuse a healthy operator whose usage collector is simply\n * older than the reader, which is the same \"silence becomes a decision\" error\n * as treating unknown as available, just pointed the other way. Unknown usage\n * on an authenticated subscription stays tier 1; the hard rule already governs\n * unknown TIER, which is the field that decides who pays.\n */\nexport function isSubscriptionExhausted(usage: AgentUsageWindows | null | undefined): boolean {\n if (!usage) return false;\n const readings = [usage.seven_day_used_pct, usage.five_hour_used_pct, usage.monthly_used_pct];\n return readings.some((v) => typeof v === 'number' && Number.isFinite(v) && v >= SUBSCRIPTION_EXHAUSTED_PCT);\n}\n\n/** A row is usable as tier 1 only if installed, authenticated, and fresh. */\nfunction isLiveSubscription(row: CapabilityAgentRow): boolean {\n return row.installed && row.authenticated && !row.stale && row.auth_tier === 'subscription';\n}\n\n/**\n * THE ONE DECISION. Call this EXACTLY ONCE per fan-out, at admission.\n *\n * Every subagent then inherits the result via `inheritSwarmTierBinding`; none\n * of them may call this again. Re-resolving per subagent is precisely the\n * defect \u2014 the inputs legitimately change mid-swarm, so a second call can\n * legitimately return a different tier and split one fan-out across two payers.\n */\nexport function resolveSwarmTierBinding(input: ResolveSwarmTierInput): SwarmTierBinding {\n const budget = clampSubagents(input.requested_subagents);\n const base = {\n schema_version: 1 as const,\n swarm_id: input.swarm_id,\n resolved_at: input.now_iso,\n };\n const rows = Array.isArray(input.agents) ? input.agents : [];\n const subscriptionRows = rows.filter(isLiveSubscription);\n const exhausted = subscriptionRows.filter((r) => isSubscriptionExhausted(r.usage)).map((r) => r.agent);\n\n const refuse = (reason: string): SwarmTierBinding => ({\n ...base,\n tier: 'refused',\n agent: null,\n reason,\n exhausted_agents: exhausted,\n subagent_budget: 0,\n spend_cap_usd: null,\n });\n\n // Operator decision: runner offline => REFUSE by default. Checked FIRST so a\n // stale-but-present capability row cannot bind a fan-out to a machine that is\n // not there to run it.\n //\n // ONLY an explicit `false` asserts a live runner. `undefined`, `null`, the\n // STRING 'false', or any other value is runner-liveness we were never told,\n // and unknown liveness reads as offline \u2014 the same direction every other\n // optional input in this function fails (`has_user_api_key === true`,\n // `paid_fallback_opt_in === true`). The previous `=== true` form let an absent\n // reading bind tier 1 for a whole fan-out on a machine that may not exist.\n if (input.runner_offline !== false) {\n return refuse(\n input.runner_offline === true\n ? 'runner offline \u2014 refusing by default (operator decision); paid fallback needs per-user opt-in'\n : `runner liveness was not asserted (runner_offline=${JSON.stringify(input.runner_offline) ?? 'undefined'}) \u2014 absent reads as OFFLINE; refusing by default`,\n );\n }\n\n // Tier 1a: a live, non-exhausted subscription CLI.\n const usableSubscription = subscriptionRows.find((r) => !isSubscriptionExhausted(r.usage));\n if (usableSubscription) {\n return {\n ...base,\n tier: 'tier1_subscription',\n agent: usableSubscription.agent,\n reason: `connected subscription CLI '${usableSubscription.agent}' with window headroom`,\n exhausted_agents: exhausted,\n subagent_budget: budget,\n spend_cap_usd: null,\n };\n }\n\n // Tier 1b: a local model. Reached when every subscription is exhausted, which\n // is why exhaustion must be its own state \u2014 as \"absent\" it would have looked\n // identical to never having connected one, and the reason string would lie.\n const localRow = rows.find((r) => r.installed && r.authenticated && !r.stale && r.auth_tier === 'local');\n const hasLocalModel = (input.local_models?.length ?? 0) > 0;\n if (localRow || hasLocalModel) {\n return {\n ...base,\n tier: 'tier1_local',\n agent: localRow?.agent ?? 'local',\n reason: exhausted.length > 0\n ? `subscription exhausted (${exhausted.join(', ')}) \u2014 falling back to a connected local model at zero marginal cost`\n : 'connected local model at zero marginal cost',\n exhausted_agents: exhausted,\n subagent_budget: budget,\n spend_cap_usd: null,\n };\n }\n\n // Tier 2: the operator's OWN key. Their money, so no opt-in is required \u2014\n // but the tier is still recorded so billing can tell it from tier 3.\n if (input.has_user_api_key === true) {\n const keyRow = rows.find((r) => r.installed && r.authenticated && !r.stale && r.auth_tier === 'api_key');\n return {\n ...base,\n tier: 'tier2_user_key',\n agent: keyRow?.agent ?? null,\n reason: exhausted.length > 0\n ? `subscription exhausted (${exhausted.join(', ')}) \u2014 using the operator's own API key`\n : \"no subscription or local model \u2014 using the operator's own API key\",\n exhausted_agents: exhausted,\n subagent_budget: budget,\n spend_cap_usd: null,\n };\n }\n\n // Tier 3: the platform owner's keys. LAST, and only with an explicit per-user\n // opt-in AND a spend cap. An opt-in without a cap is refused rather than\n // treated as unlimited \u2014 an uncapped fan-out on someone else's key is the\n // runaway the anti-swarm invariants exist to prevent.\n if (input.paid_fallback_opt_in === true) {\n const cap = input.paid_fallback_cap_usd;\n if (!isPositiveCap(cap)) {\n return refuse('paid fallback opted in but no positive spend cap supplied \u2014 refusing an uncapped platform-billed fan-out');\n }\n const keyRow = rows.find((r) => r.installed && !r.stale);\n return {\n ...base,\n tier: 'tier3_platform_key',\n agent: keyRow?.agent ?? null,\n reason: `platform-owner keys via explicit per-user opt-in, capped at $${cap}`,\n exhausted_agents: exhausted,\n subagent_budget: budget,\n spend_cap_usd: cap,\n };\n }\n\n if (exhausted.length > 0) {\n return refuse(`subscription exhausted (${exhausted.join(', ')}) and no local model, own key, or paid-fallback opt-in`);\n }\n return refuse('no connected subscription, local model, or own API key, and no paid-fallback opt-in');\n}\n\n/** An unresolved binding. The only thing a corrupt inheritance may become. */\nexport function unresolvedBinding(swarmId: string, nowIso: string, reason: string): SwarmTierBinding {\n return {\n schema_version: 1,\n swarm_id: swarmId,\n tier: 'unresolved',\n agent: null,\n reason,\n exhausted_agents: [],\n subagent_budget: 0,\n spend_cap_usd: null,\n resolved_at: nowIso,\n };\n}\n\n/** Serialize for the env channel. */\nexport function serializeSwarmTierBinding(binding: SwarmTierBinding): string {\n return JSON.stringify(binding);\n}\n\n/**\n * Parse a binding off the wire. Any failure \u2014 absent, malformed, unknown tier,\n * wrong schema version \u2014 yields `unresolved`, never a tier. This is the hard\n * rule's last line of defence: the env is the one input to this module that\n * arrives from outside the process.\n */\nexport function parseSwarmTierBinding(raw: string | undefined | null, nowIso: string): SwarmTierBinding {\n if (typeof raw !== 'string' || raw.trim().length === 0) {\n return unresolvedBinding('', nowIso, 'no swarm tier binding present in the environment');\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return unresolvedBinding('', nowIso, 'swarm tier binding is not valid JSON');\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {\n return unresolvedBinding('', nowIso, 'swarm tier binding is not an object');\n }\n const o = parsed as Record<string, unknown>;\n const swarmId = typeof o['swarm_id'] === 'string' ? o['swarm_id'] : '';\n if (o['schema_version'] !== 1) {\n return unresolvedBinding(swarmId, nowIso, 'swarm tier binding has an unsupported schema_version');\n }\n const tier = o['tier'];\n if (typeof tier !== 'string' || !(SWARM_TIERS as readonly string[]).includes(tier)) {\n return unresolvedBinding(swarmId, nowIso, 'swarm tier binding names an unknown tier');\n }\n const budget = o['subagent_budget'];\n const cap = o['spend_cap_usd'];\n const capNum = isPositiveCap(cap) ? cap : null;\n // `resolveSwarmTierBinding` refuses an opted-in platform fallback with no\n // positive cap (see the tier-3 branch above), but the INHERITANCE path used to\n // accept one: `{tier:'tier3_platform_key'}` with the cap absent \u2014 or a STRING\n // '99999', which fails the numeric test and lands on null \u2014 parsed to a live\n // tier3 binding with `cap: null` and admitted spawns. That let the inherited\n // path spend the platform owner's money uncapped, which is exactly what the\n // resolver forbids. An uncapped tier3 is not a tier: it is unresolved.\n if (tier === 'tier3_platform_key' && capNum === null) {\n return unresolvedBinding(\n swarmId,\n nowIso,\n 'inherited tier3_platform_key binding carries no positive numeric spend cap \u2014 refusing an uncapped platform-billed fan-out',\n );\n }\n return {\n schema_version: 1,\n swarm_id: swarmId,\n tier: tier as SwarmTier,\n agent: typeof o['agent'] === 'string' ? o['agent'] : null,\n reason: typeof o['reason'] === 'string' ? o['reason'] : 'inherited binding carried no reason',\n exhausted_agents: Array.isArray(o['exhausted_agents'])\n ? o['exhausted_agents'].filter((v): v is string => typeof v === 'string')\n : [],\n subagent_budget: typeof budget === 'number' && Number.isFinite(budget) && budget > 0\n ? Math.min(Math.floor(budget), MAX_BOUND_SUBAGENTS)\n : 0,\n spend_cap_usd: capNum,\n resolved_at: typeof o['resolved_at'] === 'string' ? o['resolved_at'] : nowIso,\n };\n}\n\n/**\n * INHERIT a binding from the environment. A subagent calls this; it must never\n * call `resolveSwarmTierBinding`. There is deliberately no re-resolution path\n * here \u2014 that absence is the feature.\n */\nexport function inheritSwarmTierBinding(\n env: Record<string, string | undefined>,\n nowIso: string,\n): SwarmTierBinding {\n return parseSwarmTierBinding(env[SWARM_TIER_BINDING_ENV], nowIso);\n}\n\n/**\n * Env fragment to merge into a subagent's environment.\n *\n * Deliberately still ONE key: the remaining budget rides INSIDE the serialized\n * binding, so there is no second env name to add to SAFE_ENV_NAMES and no way\n * for a counter to be dropped at the process boundary while the tier survives.\n * Spawners must pass `childBinding(binding, allocatedCapUsd)`, not the parent's\n * binding \u2014 see `childBindingEnvFragment`.\n */\nexport function bindingEnvFragment(binding: SwarmTierBinding): Record<string, string> {\n return { [SWARM_TIER_BINDING_ENV]: serializeSwarmTierBinding(binding) };\n}\n\n/**\n * The env fragment a spawner hands its child: same tier, decremented budget,\n * and the spend cap the LEDGER debited for this spawn.\n *\n * `allocatedCapUsd` comes from `claimSpawnSlot`'s `capUsd` and nowhere else.\n * Omitting it yields a child with NO cap, which an inherited tier3 binding\n * refuses \u2014 deliberately, so that the only way to obtain a platform-billed\n * child cap is to have debited the durable pool for it.\n */\nexport function childBindingEnvFragment(\n binding: SwarmTierBinding,\n allocatedCapUsd: number | null = null,\n): Record<string, string> {\n return bindingEnvFragment(childBinding(binding, allocatedCapUsd));\n}\n\n/**\n * May this fan-out spawn its `spawnsSoFar + 1`-th subagent?\n *\n * Pure, so the caller owns the counter \u2014 the same shape as VerifierCostGuard's\n * `admitSpawn`, and for the same reason: a cost guard that hides its own state\n * is a cost guard nobody can test.\n *\n * `spawnsSoFar` DEFAULTS TO 0 on purpose and means only \"does this binding still\n * carry allowance\". A SPAWNER must never source that number from its own caller:\n * `spawns_so_far` was a declared MCP tool input, which made the bounded process\n * the author of its own bound. Spawners get their counter from the durable\n * ledger in `spawn-ledger.ts` instead.\n */\nexport function admitSubagentSpawn(\n binding: SwarmTierBinding,\n spawnsSoFar = 0,\n): { readonly allowed: boolean; readonly reason: string } {\n if (!TIER_ADMITS_SPAWN.has(binding.tier)) {\n return { allowed: false, reason: `tier '${binding.tier}' admits no spawn: ${binding.reason}` };\n }\n // Second, independent guard on the platform's money. The parser already\n // refuses an uncapped tier3 on the way in; this catches a binding built in\n // code that never went through the parser. A cost guard with one gate is a\n // cost guard with one bug between it and the credit card.\n if (binding.tier === 'tier3_platform_key' && !isPositiveCap(binding.spend_cap_usd)) {\n return {\n allowed: false,\n reason: `swarm ${binding.swarm_id} is tier3_platform_key with no positive spend cap \u2014 refusing to spend the platform owner's money uncapped`,\n };\n }\n if (!Number.isFinite(spawnsSoFar) || spawnsSoFar < 0) {\n return { allowed: false, reason: 'spawn counter is not a finite non-negative number' };\n }\n if (spawnsSoFar >= binding.subagent_budget) {\n return {\n allowed: false,\n reason: `swarm ${binding.swarm_id} exhausted its bound subagent budget (${binding.subagent_budget})`,\n };\n }\n return { allowed: true, reason: `admitted under tier '${binding.tier}'` };\n}\n\n/**\n * The binding a CHILD inherits \u2014 the same tier decision with the budget\n * DECREMENTED and the spend cap PARTITIONED.\n *\n * ## The defect this closes\n *\n * `bindingEnvFragment(binding)` re-exported the binding VERBATIM, and every\n * consumer of an inherited binding started its own counter at zero\n * (`input.spawns_so_far ?? 0`). So a budget of 3 meant \"3 spawns per\n * generation, forever\": generation 1 spawned 3, each of those re-read budget 3\n * and spawned 3 more, and nothing ever decreased. The bound number was a\n * per-process quota dressed up as a fan-out ceiling. In a repo with an\n * anti-swarm doctrine and a prior host-resource incident, that is the most\n * consequential shape a bug can have.\n *\n * ## Why this no longer takes a counter\n *\n * It used to take `spawnsSoFar`, and the only production call site passed the\n * number the MODEL supplied. A function whose decrement is chosen by the process\n * being bounded decrements nothing. The parameter is gone: a child sees\n * `budget - 1`, floored at 0, and the counter that decides WHETHER a spawn\n * happens at all now lives in `spawn-ledger.ts`, outside the bounded process.\n *\n * ## What this guarantees, precisely\n *\n * The budget that crosses the process boundary STRICTLY DECREASES with every\n * hop, so every chain terminates and its DEPTH is bounded by the admitted\n * budget.\n *\n * The spend cap is NOT derived from the parent's cap. It is the share the\n * durable ledger DEBITED for this spawn (`claimSpawnSlot(...).capUsd`), and it\n * is clamped never to exceed the parent's own cap. `allocatedCapUsd` omitted, or\n * non-positive, yields a child with NO cap \u2014 which an inherited tier3 binding\n * refuses. That refusal is the point: a platform-billed child cap must be\n * DEBITED into existence, never computed.\n *\n * ## What it does NOT guarantee (stated, not implied)\n *\n * Nothing here bounds width on its own \u2014 this function is still pure. The\n * conservation over a TREE comes entirely from the ledger, and only on ONE host.\n * Exact cross-host accounting still belongs to the control plane, not here.\n */\nexport function childBinding(\n binding: SwarmTierBinding,\n allocatedCapUsd: number | null = null,\n): SwarmTierBinding {\n const allocated = isPositiveCap(allocatedCapUsd) ? allocatedCapUsd : null;\n const parentCap = isPositiveCap(binding.spend_cap_usd) ? binding.spend_cap_usd : null;\n return {\n ...binding,\n subagent_budget: Math.max(0, binding.subagent_budget - 1),\n // A child never carries more than its parent, whatever the ledger says: a\n // forged or hand-edited pool cannot inflate a descendant above the binding\n // it descends from.\n spend_cap_usd: allocated === null || parentCap === null ? null : Math.min(allocated, parentCap),\n };\n}\n\n/**\n * Halve a tier-3 spend cap between the process that holds it and ONE child.\n *\n * ## CORRECTION \u2014 the invariant this function was documented with was FALSE\n *\n * #9312 shipped this function and asserted, in this comment, in the plugin twin,\n * in the PR body and in the merged roadmap fragment\n * `docs/vo/roadmap-log/2026-08-02-fanout-ceiling-untrusted-counter.md`, that\n * \"the caps carried by ALL descendants of a binding sum to at most the parent's\n * own cap\".\n *\n * That claim was tested over a CHAIN and it is TRUE over a chain: 12 generations\n * of a $50 cap sum to $49.95. A swarm is a TREE. This function is PURE and holds\n * no state, so a $50 parent that spawns THREE children calls it three times and\n * hands out $25 each = $75 > $50. Re-measured end-to-end against the shipped\n * twin, a $50 root fanned 3 wide by 3 deep distributed **$356.25 \u2014 7.1x the\n * cap** \u2014 on the one field that guards the platform owner's money.\n *\n * The sentence is corrected rather than deleted because an overclaim that\n * quietly disappears teaches the next author nothing. What held: CONSERVATION\n * OVER DEPTH. What did not: conservation over WIDTH, which is what a swarm\n * actually does.\n *\n * ## What replaced it\n *\n * `spawn-ledger.ts` records the cap as a durable per-swarm POOL and DEBITS one\n * equal share of it per exclusive slot claim. Because every spawn in the tree \u2014\n * every generation, every sibling, both twins on the host \u2014 must win a distinct\n * slot from that one ledger, the sum over the whole tree is bounded by the pool.\n * Conservation now comes from persisted state, not from a pure function that a\n * sibling can re-enter with an untouched input.\n *\n * ## Why this is still here\n *\n * It is the anchor for the correction above, and it is deliberately NOT on the\n * spawn path: `childBinding` takes the ledger's debit instead. Re-wiring it back\n * into `childBinding` is covered by a mutation in\n * `scripts/ci/mutation-test-fanout-ceiling.mjs`, so the regression cannot land\n * silently. What it still is, accurately: a conserving two-way split in integer\n * cents (`child + retained === cap`) \u2014 correct for ONE parent and ONE child, and\n * nothing more.\n *\n * A non-positive / non-numeric cap partitions to `{null, null}` \u2014 the same\n * answer `isPositiveCap` gives everywhere else, so an uncapped tier3 stays\n * unresolvable rather than becoming a zero-cap tier3.\n */\nexport function partitionSpendCap(\n cap: number | null | undefined,\n): { readonly child: number | null; readonly retained: number | null } {\n if (!isPositiveCap(cap)) return { child: null, retained: null };\n // Integer cents: float halving of 0.01 repeatedly would drift, and a cap that\n // does not add back up is not a partition.\n const cents = Math.round(cap * 100);\n const childCents = Math.floor(cents / 2);\n return { child: childCents / 100, retained: (cents - childCents) / 100 };\n}\n\n/**\n * Refuse a caller-supplied `agent` that CONTRADICTS the bound one.\n *\n * ## The defect this closes\n *\n * The spawner documented \"an explicit `agent` may pick WHICH agent inside the\n * bound tier, but it can never change the tier\" and then implemented\n * `input.agent?.trim() || binding.agent` with no check at all. `agent` is\n * model-supplied. Naming a different agent moves the fan-out onto a different\n * vendor's subscription or key \u2014 a different PAYER \u2014 which is the exact decision\n * admission exists to make once. Code that does not enforce the invariant it\n * documents defeats the whole mechanism.\n *\n * Restating the bound agent is a harmless no-op and stays allowed. Anything else\n * is refused, including any agent at all when the binding named none: a null\n * bound agent plus a model-supplied name is silence resolving upward.\n *\n * Returns a refusal reason, or `null` when the request is compatible.\n */\nexport function agentBindingRefusal(\n binding: Pick<SwarmTierBinding, 'agent' | 'tier' | 'swarm_id'>,\n requestedAgent: string | null | undefined,\n): string | null {\n const requested = typeof requestedAgent === 'string' ? requestedAgent.trim() : '';\n if (requested.length === 0) return null;\n if (binding.agent !== null && requested === binding.agent) return null;\n return (\n `swarm '${binding.swarm_id}' is bound to agent '${binding.agent ?? 'none'}' under tier '${binding.tier}'; ` +\n `a caller-supplied agent '${requested}' would move this fan-out onto a different payer \u2014 refusing ` +\n '(the tier is decided once, at admission, and an inherited binding cannot be renegotiated)'\n );\n}\n", "/**\n * Per-agent launch shapes for a successor / subagent spawn.\n *\n * ## Why this file exists\n *\n * Both successor spawners hardcode `claude` \u2014 the binary AND the argv. The\n * operator's clause is that the precedence holds for subagents from workflows\n * and swarms, so a fan-out bound to `codex` must spawn codex. But the naive fix\n * \u2014 swap the binary name and keep the args \u2014 SHIPS BROKEN:\n *\n * claude: claude -p --permission-mode acceptEdits [--max-turns N]\n * codex: codex exec --json -c approval_policy=\"never\" \\\n * --sandbox workspace-write --skip-git-repo-check -\n *\n * (see scripts/virtual-office/code-runner/claude-args.mjs:46-55 and\n * codex-runner.mjs:97-107). `codex -p --permission-mode acceptEdits` is not a\n * valid codex invocation. So the resolution has to carry the whole launch, not\n * just a name.\n *\n * ## THE REFUSAL RULES\n *\n * 1. An agent with no known headless launch shape is REFUSED, never\n * approximated. Guessing argv for `cursor` would produce a spawn that either\n * fails obscurely or \u2014 worse \u2014 succeeds with different semantics.\n *\n * 2. A requested `max_turns` that the resolved agent CANNOT ENFORCE is\n * REFUSED. `--max-turns` is claude-only; codex's arg builder emits no turn\n * cap at all. Honouring the resolved agent by silently dropping the cap\n * would convert a bounded spawn into an unbounded one \u2014 removing a spend\n * guard as a side effect of a billing-correctness change is precisely the\n * backfire this slice is supposed to prevent.\n *\n * Both refusals are LOUD (a named reason on a `spawned: false` result), never a\n * silent downgrade.\n */\n\n/** What one agent's headless launch looks like. */\nexport interface AgentLaunchShape {\n readonly bin: string;\n /** Args that do not depend on the request. */\n readonly baseArgs: readonly string[];\n /** Whether this CLI can bound the run to N turns. */\n readonly enforcesMaxTurns: boolean;\n /** Flag pair used to express a turn cap, when it can. */\n readonly maxTurnsFlag?: string;\n /** Trailing args appended after any request-dependent flags. */\n readonly trailingArgs?: readonly string[];\n /**\n * Whether this argv survives `spawn(..., { shell: true })` on Windows.\n *\n * The successor spawner sets `shell: true` on win32 because the agent CLIs\n * are `.cmd` shims that cannot otherwise be resolved. That routes the whole\n * command line through cmd.exe, which re-parses quoting. claude's argv is\n * plain tokens and survives; codex's carries embedded double quotes\n * (`-c approval_policy=\"never\"`) and its behaviour under cmd.exe re-parsing\n * is UNVERIFIED here. Marking it false makes the spawner refuse on win32\n * rather than emit a command line that may silently mean something else.\n */\n readonly windowsShellSafe: boolean;\n}\n\n/**\n * The agents this repo knows how to launch headlessly with a piped prompt.\n *\n * Kept deliberately small. An agent is added here only once its real argv is\n * verified against the runner that already spawns it \u2014 an entry here is a\n * promise that the spawn works, and a wrong promise is worse than an absence\n * because the absence refuses loudly.\n */\nexport const AGENT_LAUNCH_SHAPES: Readonly<Record<string, AgentLaunchShape>> = Object.freeze({\n claude: {\n bin: 'claude',\n baseArgs: ['-p', '--permission-mode', 'acceptEdits'],\n enforcesMaxTurns: true,\n maxTurnsFlag: '--max-turns',\n windowsShellSafe: true,\n },\n codex: {\n bin: 'codex',\n baseArgs: ['exec', '--json', '-c', 'approval_policy=\"never\"', '--sandbox', 'workspace-write', '--skip-git-repo-check'],\n enforcesMaxTurns: false,\n // `-` makes codex read the prompt from stdin (injection-safe), matching how\n // codex-runner.mjs already spawns it.\n trailingArgs: ['-'],\n // `approval_policy=\"never\"` carries embedded quotes; cmd.exe re-parsing is\n // unverified, so win32 refuses rather than risking a mangled sandbox flag.\n windowsShellSafe: false,\n },\n});\n\n/**\n * Can a successor ACTUALLY be launched as `agent` on `platform`?\n *\n * ## Why a predicate exists at all\n *\n * This registry is small on purpose, and every refusal above is correct. But a\n * refusal is only honest when the caller had a CHOICE. #9313 gave the swarm tier\n * binding a producer at code-task admission, and that producer bound the real\n * dispatch agent \u2014 `cursor`, `local`, `meta`, `oai` included. None of those has\n * a shape here, so `resolveSuccessorLaunch` refused every successor spawn those\n * tasks used to make. Before #9313 no binding existed, they took the unbound\n * legacy path, and they spawned. A billing-correctness change silently deleted a\n * working capability.\n *\n * The producer cannot fix that by keeping its own list of launchable agents \u2014\n * that is a second copy of this table, and a second copy drifts. So this is the\n * ONE question it asks, derived from `AGENT_LAUNCH_SHAPES` and nothing else:\n * bind only what this lane can actually start, and leave everything else on the\n * behaviour it has today.\n *\n * `platform` is part of the question, not a detail. `codex` HAS a shape and is\n * still unlaunchable on win32 (its argv carries embedded quotes that cmd.exe\n * re-parses), so on the operator's own Windows fleet a codex binding would\n * refuse exactly like `cursor`. Answering \"yes\" here on the strength of the\n * table alone would leave that half of the regression live.\n *\n * Deliberately does NOT consider `max_turns`: that is a property of one request,\n * not of whether the lane can launch the agent at all, and a binding is minted\n * once for a whole fan-out whose per-spawn turn caps are not yet known.\n */\nexport function canLaunchSuccessorAgent(\n agent: string | null | undefined,\n platform: string = process.platform,\n): boolean {\n const name = typeof agent === 'string' ? agent.trim() : '';\n if (!name) return false;\n const shape = AGENT_LAUNCH_SHAPES[name];\n if (!shape) return false;\n if (platform === 'win32' && !shape.windowsShellSafe) return false;\n return true;\n}\n\nexport type SuccessorLaunch =\n | { readonly ok: true; readonly agent: string; readonly bin: string; readonly args: readonly string[] }\n | { readonly ok: false; readonly reason: string };\n\nexport interface ResolveSuccessorLaunchInput {\n /** The agent the binding resolved to. null/absent means nothing was bound. */\n readonly agent: string | null | undefined;\n readonly maxTurns?: number | undefined;\n /** Defaults to the host platform; injected so the win32 rule is testable. */\n readonly platform?: string | undefined;\n}\n\n/**\n * Resolve the launch for a successor spawn, or refuse with a named reason.\n *\n * There is NO default-to-claude branch. A caller that has not resolved an agent\n * gets a refusal, because defaulting is how the fan-out ends up half on the\n * operator's subscription and half on the platform's key in the first place.\n */\nexport function resolveSuccessorLaunch(input: ResolveSuccessorLaunchInput): SuccessorLaunch {\n const agent = typeof input.agent === 'string' ? input.agent.trim() : '';\n if (!agent) {\n return { ok: false, reason: 'no agent bound for this spawn \u2014 refusing rather than defaulting to claude' };\n }\n const shape = AGENT_LAUNCH_SHAPES[agent];\n if (!shape) {\n const known = Object.keys(AGENT_LAUNCH_SHAPES).join(', ');\n return {\n ok: false,\n reason: `no known headless launch shape for agent '${agent}' (known: ${known}) \u2014 refusing rather than guessing its argv`,\n };\n }\n\n const wantsMaxTurns = Number.isInteger(input.maxTurns) && (input.maxTurns as number) > 0;\n if (wantsMaxTurns && !shape.enforcesMaxTurns) {\n return {\n ok: false,\n reason: `agent '${agent}' cannot enforce a max_turns cap \u2014 refusing rather than spawning it unbounded`,\n };\n }\n\n const platform = input.platform ?? process.platform;\n if (platform === 'win32' && !shape.windowsShellSafe) {\n return {\n ok: false,\n reason: `agent '${agent}' has an argv whose behaviour under Windows cmd.exe re-parsing is unverified \u2014 refusing rather than emitting a command line that may mean something else`,\n };\n }\n\n const args = [...shape.baseArgs];\n if (wantsMaxTurns && shape.maxTurnsFlag) {\n args.push(shape.maxTurnsFlag, String(input.maxTurns));\n }\n if (shape.trailingArgs) args.push(...shape.trailingArgs);\n\n return { ok: true, agent, bin: shape.bin, args };\n}\n", "/**\n * The swarm SPAWN LEDGER \u2014 a durable, host-shared counter for one fan-out.\n *\n * ## The defect this closes\n *\n * #9311 made the inherited budget DECREMENT, which bounds a chain's DEPTH. It\n * left the counter itself in the caller's hands: `vo_spawn_successor` declared\n * `spawns_so_far` as an MCP TOOL INPUT (`input.spawns_so_far ?? 0`) and the\n * plugin CLI took `--spawns-so-far`. Both are supplied by the very process being\n * bounded, and both default to ZERO when absent. Reproduced end-to-end through\n * `handleSpawnSuccessor` with the real `resolveSpawnPlan` and a fake spawn: ONE\n * process performed 25 real spawns from a budget-3 binding \u2014 and would have\n * performed any number \u2014 simply by omitting the field. Absent optional input\n * resolving toward MORE spending is the exact fail-open shape this PR series\n * already fixed for `runner_offline`.\n *\n * A pure predicate may take a counter (`admitSubagentSpawn` does, deliberately \u2014\n * a cost guard that hides its state is a cost guard nobody can test). A SPAWNER\n * may not take one from the model. So the spawners now get their counter from\n * here.\n *\n * ## Why the filesystem\n *\n * The bound this replaces is per-process, and both spawners are one-shot in the\n * lane that matters: `vo-claude-plugin/hooks/session-state-tracker-auto-spawn.mjs`\n * fires a FRESH `node vo-spawn-successor.mjs` per successor, so any counter held\n * in a variable, or written back into `process.env`, dies with the process and\n * the next one restarts at zero. A directory of exclusively-created slot files\n * is a counter that outlives the process, is shared by BOTH spawners on the\n * host, and is race-free without a lock: `openSync(path, 'wx')` is O_EXCL, so\n * exactly one concurrent claimant can win a given slot.\n *\n * The ceiling is written ONCE per swarm, by whoever claims first, and is read \u2014\n * never re-proposed \u2014 by everyone after. A descendant therefore cannot inflate\n * the fan-out it was admitted into.\n *\n * ## What this bounds, precisely (and what it does not)\n *\n * BOUNDS: the TOTAL number of spawns performed under one `swarm_id` on one host,\n * across every generation and every sibling, to the ceiling recorded at\n * admission. Combined with `childBinding`'s decrement (depth) that is a real\n * ceiling, not a per-process quota.\n *\n * DOES NOT BOUND: a fan-out spread across hosts (the ledger is host-local), or a\n * caller that FORGES `VO_SWARM_TIER_BINDING` in its own environment. The latter\n * is out of scope by design and always has been \u2014 this module is a CONSISTENCY\n * mechanism, not an authorization one (see tier-binding.ts's header). The defect\n * being closed is a far lower bar than forging env: a number the model typed\n * into a declared tool input.\n *\n * RESIDUE, stated rather than discovered later: the ledger is append-only and\n * has no reaper. One directory per swarm, at most `MAX_BOUND_SUBAGENTS + 1`\n * files of ~80 bytes inside it \u2014 so 10,000 fan-outs is ~17 MB. Deliberately no\n * cleanup path: a reaper is a deletion path, and a deletion path that races a\n * live claim would hand out a slot twice. Bounded residue beats an unbounded\n * fan-out.\n *\n * FAILS CLOSED: if the ledger cannot be read or written, the spawn is REFUSED.\n * An unbounded fan-out on the platform owner's key is the highest-consequence\n * defect in this programme, and this repo has a prior host-resource-exhaustion\n * incident behind its anti-swarm doctrine.\n *\n * ## The ledger also DEBITS the spend cap (slice W5A)\n *\n * #9312 shipped `partitionSpendCap`, a PURE function of the parent's cap\n * (`child = floor(cents/2)`), and asserted that \"the caps carried by ALL\n * descendants of a binding sum to at most the root cap\". That was measured over\n * a CHAIN \u2014 12 generations summing to $49.95 of $50 \u2014 and it is TRUE over depth.\n * A swarm is a TREE, and over WIDTH the claim is FALSE: the function holds no\n * state, so a $50 parent that calls it three times hands each child $25 = $75.\n * Re-measured against the shipped twin, a root cap of $50 fanned 3 wide by 3\n * deep distributed **$356.25 \u2014 7.1x the cap**, on the one field that guards the\n * platform owner's money.\n *\n * The fix is the reason this file exists: the remaining allowance must be\n * PERSISTED and DEBITED per spawn, never recomputed from the parent's untouched\n * cap. The pool (`cap_cents`) is recorded ONCE next to the ceiling, and each\n * exclusive slot claim draws exactly one share of it \u2014 so the debit is atomic\n * with the claim, and a sibling physically cannot re-read an undebited cap\n * because it must win a DIFFERENT slot.\n */\nimport { mkdirSync, openSync, closeSync, readFileSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { MAX_BOUND_SUBAGENTS, isPositiveCap } from './tier-binding.js';\n\n/**\n * Where the ledger lives. Overridable so tests never touch the real home dir.\n *\n * Listed in SAFE_ENV_NAMES in\n * scripts/virtual-office/code-runner/agent-process-env.mjs: if a parent's\n * override were stripped at the process boundary, the child would ledger into a\n * DIFFERENT directory and the shared ceiling would silently become per-process\n * again \u2014 the exact defect this file exists to close.\n */\nexport const SWARM_LEDGER_DIR_ENV = 'VO_SWARM_LEDGER_DIR';\n\n/** Default ledger root, alongside the handoff and successor-log directories. */\nexport function resolveLedgerDir(env: Record<string, string | undefined>): string {\n const override = env[SWARM_LEDGER_DIR_ENV];\n if (typeof override === 'string' && override.trim().length > 0) return override.trim();\n return join(homedir(), '.vo', 'swarm-ledger');\n}\n\n/**\n * A swarm id is safe as a directory name, or it is not usable as a ledger key.\n *\n * `swarm_id` arrives from the environment and is interpolated into a PATH. A\n * `..` or a separator would let a binding write outside the ledger root, so\n * anything outside `[A-Za-z0-9._-]` is refused rather than escaped: a spawn we\n * cannot account for is a spawn we do not perform.\n */\nexport function sanitizeSwarmId(raw: unknown): string | null {\n if (typeof raw !== 'string') return null;\n const id = raw.trim();\n if (id.length === 0 || id.length > 128) return null;\n if (!/^[A-Za-z0-9._-]+$/u.test(id)) return null;\n if (id === '.' || id === '..') return null;\n return id;\n}\n\nexport interface SpawnSlotInput {\n readonly swarmId: unknown;\n /** The ceiling this caller proposes, used ONLY if no ceiling is recorded yet. */\n readonly proposedCeiling: number;\n /**\n * The spend-cap POOL this caller proposes, USD \u2014 recorded ONLY if no pool is\n * recorded yet, exactly like the ceiling. Absent / null / non-positive means\n * \"this fan-out has no cap to account\" (tier1 and tier2 spend nobody else's\n * money), which is NOT the same as \"the pool is unknown\": an unknown pool\n * refuses a capped spawn, see `claimSpawnSlot`.\n */\n readonly proposedCapUsd?: number | null;\n readonly dir: string;\n readonly nowIso: string;\n}\n\nexport type SpawnSlotResult =\n | {\n readonly ok: true;\n readonly slot: number;\n readonly ceiling: number;\n readonly remaining: number;\n /**\n * The share of the pool this claim DEBITED, USD \u2014 the cap the child may\n * carry. null when the fan-out has no pool (tier1/tier2).\n */\n readonly capUsd: number | null;\n /** Pool left after this debit, USD. Strictly decreases with every claim. */\n readonly capRemainingUsd: number | null;\n }\n | { readonly ok: false; readonly reason: string };\n\nexport type ClaimSpawnSlot = (input: SpawnSlotInput) => SpawnSlotResult;\n\nconst CEILING_FILE = 'ceiling.json';\n\n/** The recorded head of one swarm's ledger: its ceiling and its spend pool. */\ninterface LedgerHead {\n readonly ceiling: number;\n /** Integer cents. 0 means \"no cap recorded\" \u2014 see the read rule below. */\n readonly capCents: number;\n}\n\n/** Create `path` only if it does not exist. Returns false on EEXIST. */\nfunction createExclusive(path: string, contents: string): boolean {\n let fd: number | undefined;\n try {\n fd = openSync(path, 'wx');\n } catch {\n return false;\n }\n try {\n writeFileSync(fd, contents, 'utf8');\n } finally {\n closeSync(fd);\n }\n return true;\n}\n\n/** A cap in USD as integer cents. Non-positive / non-numeric reads as 0. */\nexport function capToCents(cap: number | null | undefined): number {\n return isPositiveCap(cap) ? Math.round(cap * 100) : 0;\n}\n\n/**\n * The head of this swarm's ledger: the first claimant's ceiling AND spend pool,\n * recorded once, read \u2014 never re-proposed \u2014 by everyone after.\n *\n * Returns null when the ledger is unreadable or the recorded ceiling is not\n * usable; both refuse the spawn upstream rather than inventing one.\n *\n * An ABSENT `cap_cents` (a head written before slice W5A, or a hand-edited one)\n * reads as ZERO POOL, not as an unknown to be filled in from the caller. Zero\n * pool costs a tier1/tier2 fan-out nothing \u2014 those tiers carry no cap \u2014 and\n * REFUSES a tier3 one, which is the hard rule pointed the only direction it may\n * point: absent input never resolves toward more of someone else's money.\n */\nfunction readOrRecordLedgerHead(\n swarmDir: string,\n proposedCeiling: number,\n proposedCapCents: number,\n nowIso: string,\n): LedgerHead | null {\n const path = join(swarmDir, CEILING_FILE);\n const head = JSON.stringify({\n ceiling: proposedCeiling,\n cap_cents: proposedCapCents,\n recorded_at: nowIso,\n });\n if (createExclusive(path, head)) {\n return { ceiling: proposedCeiling, capCents: proposedCapCents };\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(path, 'utf8'));\n } catch {\n return null;\n }\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;\n const record = parsed as Record<string, unknown>;\n const recorded = record['ceiling'];\n if (typeof recorded !== 'number' || !Number.isFinite(recorded) || recorded < 1) return null;\n const recordedCap = record['cap_cents'];\n const capCents =\n typeof recordedCap === 'number' && Number.isFinite(recordedCap) && recordedCap > 0\n ? Math.floor(recordedCap)\n : 0;\n // Clamped on READ too: a hand-edited ledger cannot raise the platform limit.\n return { ceiling: Math.min(Math.floor(recorded), MAX_BOUND_SUBAGENTS), capCents };\n}\n\n/**\n * Claim the next spawn slot for `swarmId`, or refuse with a named reason.\n *\n * The claim is the counter. It is taken AFTER every other refusal so a refused\n * spawn does not consume allowance, and it is NOT released if the spawn then\n * fails: a spawner that can retry indefinitely is the unbounded loop this file\n * exists to prevent, and consuming the slot is the fail-closed direction.\n *\n * ## The claim is also the spend-cap DEBIT\n *\n * Every spawn under this swarm \u2014 every generation, every sibling, both twins on\n * this host \u2014 wins a DISTINCT slot from this one directory, and each slot draws\n * exactly one equal share of the recorded pool:\n *\n * share = floor(cap_cents / ceiling) total = claims * share <= cap_cents\n *\n * That is the conservation the pure `partitionSpendCap` could not give: it is a\n * function of durable SHARED state, so three siblings get three different slots\n * and three separate draws, not three copies of the same halving. Conservation\n * therefore holds over the TREE, not merely over a chain.\n *\n * Two properties stated rather than discovered later:\n *\n * - Equal shares, not halving-from-remaining. Halving would make a child's cap\n * depend on the ORDER claims happen to arrive in, and would starve the tail\n * (slot 12 of a $50 pool gets 0 cents and is refused while the ceiling still\n * has allowance). Equal shares are deterministic and fund every admitted\n * spawn identically.\n * - Unclaimed allowance is NOT redistributed. A narrow fan-out under a wide\n * ceiling leaves headroom unspent. Redistribution needs a release path, and\n * a release path is a path that can hand the same allowance out twice \u2014\n * exactly the reason this file has no reaper. Under-allocating is the\n * fail-closed direction; the cap is a ceiling on exposure, not an\n * entitlement.\n *\n * The ACCOUNTING is the integer-cent fields written to each slot file; the USD\n * numbers returned here are a rendering of them. Stated because IEEE754 can make\n * a USD sum read a hair over its pool (3 x $0.21 is 0.6300000000000001, not\n * 0.63) while the cents it renders \u2014 3 x 21 <= 63 \u2014 are exact. Assert\n * conservation against `cap_cents_debited`, not against summed floats.\n *\n * ## What the pool does NOT bound (found by attacking this file, not later)\n *\n * A ledger head PRE-FORGED on disk before the first legitimate claim \u2014 a\n * `ceiling.json` carrying an inflated `cap_cents` \u2014 makes each share large, and\n * `childBinding` then clamps each child to its PARENT's cap. So no descendant\n * ever exceeds its parent, but the SUM across the fan-out is bounded by the\n * forged pool rather than by the root cap. This sits inside the threat model\n * this module has carried since #9312: a caller that can write the ledger can\n * equally forge `VO_SWARM_TIER_BINDING` itself, and this is a CONSISTENCY\n * mechanism, not an authorization one. It is not a regression either \u2014 before\n * this slice the cap ignored the ledger completely. Named here so the next\n * reader does not have to rediscover it.\n *\n * The tempting mitigation \u2014 also dividing the CALLER's own cap by the ceiling \u2014\n * was rejected: a legitimate descendant's cap is already one share, so dividing\n * it again shrinks allocations with depth and reintroduces exactly the\n * order-dependence the equal-share rule exists to remove.\n */\nexport const claimSpawnSlot: ClaimSpawnSlot = ({ swarmId, proposedCeiling, proposedCapUsd, dir, nowIso }) => {\n const id = sanitizeSwarmId(swarmId);\n if (id === null) {\n return {\n ok: false,\n reason:\n `swarm id ${JSON.stringify(swarmId)} is absent or unusable as a ledger key \u2014 ` +\n 'refusing a spawn that cannot be counted against a fan-out ceiling',\n };\n }\n const proposed = Number.isFinite(proposedCeiling) ? Math.floor(proposedCeiling) : 0;\n if (proposed < 1) {\n return { ok: false, reason: `swarm '${id}' proposes a ceiling of ${proposed} \u2014 no allowance to claim` };\n }\n const swarmDir = join(dir, id);\n try {\n mkdirSync(swarmDir, { recursive: true });\n } catch (err) {\n return {\n ok: false,\n reason: `swarm '${id}' ledger directory is unwritable (${err instanceof Error ? err.message : String(err)}) \u2014 refusing rather than spawning uncounted`,\n };\n }\n const wantedCents = capToCents(proposedCapUsd);\n const head = readOrRecordLedgerHead(swarmDir, Math.min(proposed, MAX_BOUND_SUBAGENTS), wantedCents, nowIso);\n if (head === null) {\n return { ok: false, reason: `swarm '${id}' ledger carries no readable ceiling \u2014 refusing rather than spawning uncounted` };\n }\n const { ceiling, capCents } = head;\n // One equal share of the RECORDED pool. Integer cents throughout: a pool that\n // does not add back up is not an accounting of anything.\n const shareCents = capCents > 0 ? Math.floor(capCents / ceiling) : 0;\n // A caller that needs a cap and cannot be given one is REFUSED. Handing it\n // `null` would degrade the child to an uncapped tier3, which the parser and\n // `admitSubagentSpawn` both already refuse \u2014 but refusing HERE names the real\n // reason instead of letting it surface one hop later as \"no spend cap\".\n if (wantedCents > 0 && shareCents < 1) {\n return {\n ok: false,\n reason:\n `swarm '${id}' has no spend allowance left to debit (recorded pool $${(capCents / 100).toFixed(2)} ` +\n `across a ceiling of ${ceiling} leaves under one cent per spawn) \u2014 refusing a platform-billed spawn it cannot fund`,\n };\n }\n for (let slot = 0; slot < ceiling; slot++) {\n const debitedCents = shareCents;\n const remainingCents = capCents > 0 ? capCents - (slot + 1) * shareCents : 0;\n const claimed = createExclusive(\n join(swarmDir, `slot-${slot}.json`),\n JSON.stringify({\n slot,\n ceiling,\n pid: process.pid,\n claimed_at: nowIso,\n // The debit record. Durable and atomic with the claim: this file is\n // created with O_EXCL, so exactly one claimant ever writes this line.\n cap_cents_pool: capCents,\n cap_cents_debited: debitedCents,\n cap_cents_remaining: remainingCents,\n }),\n );\n if (claimed) {\n return {\n ok: true,\n slot,\n ceiling,\n remaining: ceiling - slot - 1,\n capUsd: debitedCents > 0 ? debitedCents / 100 : null,\n capRemainingUsd: capCents > 0 ? remainingCents / 100 : null,\n };\n }\n }\n return {\n ok: false,\n reason: `swarm '${id}' has spent its whole fan-out ceiling (${ceiling} spawns across every generation) \u2014 refusing`,\n };\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 * 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 * Tools: private cloud knowledge for Claude/Codex/Cursor/cowork clients.\n *\n * Uses the same auth-token source as `vo_sync_config` (`vo-mcp login` preferred).\n * The control-plane binds all calls to the authenticated operator and returns\n * metadata/snippets/context only \u2014 no raw corpus download surface.\n */\nimport type { ToolDeps } from '../../types.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\nimport type { FetchLike } from './sync-config.js';\n\nexport const UPSERT_TOOL_NAME = 'vo_private_knowledge_upsert';\nexport const CONTEXT_TOOL_NAME = 'vo_private_knowledge_context';\nexport const INVALIDATE_TOOL_NAME = 'vo_private_knowledge_invalidate';\nexport const STALE_TOOL_NAME = 'vo_private_knowledge_stale';\n\nconst KNOWLEDGE_CLASSES = ['memory', 'skill', 'doctrine', 'hook', 'command'] as const;\ntype KnowledgeClass = (typeof KNOWLEDGE_CLASSES)[number];\n\n/**\n * ~3 pages of dense text. Advisory only \u2014 oversized entries still upsert, but\n * retrieval surfaces whole entries, so tight focused entries beat bulk dumps.\n * Verified against the knowledge-base precision findings of 2026-07-11\n * (small corpora load whole into context; precision beats volume).\n */\nexport const PRECISION_CHAR_BUDGET = 12_000;\n\nexport const upsertInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n knowledge_class: { type: 'string', enum: KNOWLEDGE_CLASSES },\n source_path: { type: 'string', description: 'Stable private source identifier; not exposed to other users.' },\n title: { type: 'string', description: 'Descriptive, retrieval-friendly title (e.g. \"AlgoTax OCR redaction architecture\", not \"notes\") \u2014 retrieval matches on it.' },\n content: { type: 'string', description: 'Private knowledge text to store server-side. Keep each entry tight and focused (~1-3 pages, under ~12k chars); split larger corpora into separate entries.' },\n },\n required: ['knowledge_class', 'source_path', 'title', 'content'],\n additionalProperties: false,\n};\n\nexport const contextInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n query: { type: 'string' },\n limit: { type: 'number', minimum: 1, maximum: 50 },\n knowledge_class: { type: 'string', enum: KNOWLEDGE_CLASSES },\n },\n required: ['query'],\n additionalProperties: false,\n};\n\nexport const invalidateInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n knowledge_class: { type: 'string', enum: KNOWLEDGE_CLASSES },\n source_path: { type: 'string', minLength: 1, maxLength: 400, description: 'Stable private source identifier of the entry to invalidate \u2014 must match the source_path used at upsert.' },\n },\n required: ['knowledge_class', 'source_path'],\n additionalProperties: false,\n};\n\nexport const staleInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n days: { type: 'number', minimum: 1, maximum: 3650, description: 'Window in days (default 90): entries at least this old that were never recalled into an agent context, or not within the window.' },\n limit: { type: 'number', minimum: 1, maximum: 500 },\n },\n additionalProperties: false,\n};\n\nexport const staleDescription =\n 'The FORGETTING REPORT: lists the authenticated operator\u2019s live private-knowledge entries that are at least N days old and have never been recalled into an agent context (or not within N days). Metadata only. ' +\n 'SURFACES ONLY \u2014 never auto-invalidates or auto-merges: two memories that disagree may both have been right in different contexts, so you decide. ' +\n `Act on a candidate deliberately with ${INVALIDATE_TOOL_NAME}; recall counts come from ${CONTEXT_TOOL_NAME} reads that actually placed the entry into returned context.`;\n\nexport const upsertDescription =\n 'Uploads or refreshes the authenticated operator\u2019s private cloud knowledge. Works for Claude, Codex, Cursor, and cowork clients via the same vo-mcp login credential. Returns metadata only, not raw stored content. ' +\n 'PRECISION DISCIPLINE: keep each entry tight and focused (~1-3 pages) with a descriptive retrieval-friendly title \u2014 retrieval surfaces whole entries, so small dense entries beat bulk dumps. Split large corpora into focused entries, then run a retrieval self-test via vo_private_knowledge_context before relying on the knowledge.';\n\nexport const contextDescription =\n 'Retrieves prompt-ready private knowledge context for the authenticated operator. Returns snippets/context only; no raw corpus download. ' +\n 'Also the retrieval self-test surface: after upserting critical knowledge, query for it here and confirm the entry surfaces before trusting it in downstream work.';\n\nexport const invalidateDescription =\n 'Soft-deletes one private-knowledge entry for the authenticated operator: closes the live entry\u2019s validity window (bi-temporal) so it stops surfacing in retrieval. Never destroys data \u2014 invalidated versions remain queryable server-side via include_invalidated. ' +\n 'Identify the entry by the same { knowledge_class, source_path } used at upsert; a not_found response means no live entry matches. ' +\n `After invalidating, self-test via ${CONTEXT_TOOL_NAME} to confirm the entry no longer surfaces.`;\n\ninterface UpsertInput {\n readonly knowledge_class: KnowledgeClass;\n readonly source_path: string;\n readonly title: string;\n readonly content: string;\n}\n\ninterface InvalidateInput {\n readonly knowledge_class: KnowledgeClass;\n readonly source_path: string;\n}\n\ninterface ContextInput {\n readonly query: string;\n readonly limit?: number;\n readonly knowledge_class?: KnowledgeClass;\n}\n\ninterface StaleInput {\n readonly days?: number;\n readonly limit?: number;\n}\n\nfunction isStaleInput(value: unknown): value is StaleInput {\n if (value === undefined || value === null) return true;\n if (typeof value !== 'object') return false;\n const input = value as Record<string, unknown>;\n if (input['days'] !== undefined && typeof input['days'] !== 'number') return false;\n if (input['limit'] !== undefined && typeof input['limit'] !== 'number') return false;\n return true;\n}\n\nfunction isKnowledgeClass(value: unknown): value is KnowledgeClass {\n return typeof value === 'string' && (KNOWLEDGE_CLASSES as readonly string[]).includes(value);\n}\n\nfunction isUpsertInput(value: unknown): value is UpsertInput {\n if (typeof value !== 'object' || value === null) return false;\n const input = value as Record<string, unknown>;\n return isKnowledgeClass(input['knowledge_class']) &&\n typeof input['source_path'] === 'string' &&\n typeof input['title'] === 'string' &&\n typeof input['content'] === 'string';\n}\n\nfunction isInvalidateInput(value: unknown): value is InvalidateInput {\n if (typeof value !== 'object' || value === null) return false;\n const input = value as Record<string, unknown>;\n return isKnowledgeClass(input['knowledge_class']) &&\n typeof input['source_path'] === 'string';\n}\n\nfunction isContextInput(value: unknown): value is ContextInput {\n if (typeof value !== 'object' || value === null) return false;\n const input = value as Record<string, unknown>;\n if (typeof input['query'] !== 'string') return false;\n if (input['limit'] !== undefined && typeof input['limit'] !== 'number') return false;\n if (input['knowledge_class'] !== undefined && !isKnowledgeClass(input['knowledge_class'])) return false;\n return true;\n}\n\nasync function getCloudAuth(fetchFn: FetchLike) {\n const controlPlaneUrl = process.env['VO_CONTROL_PLANE_URL']?.replace(/\\/+$/, '');\n if (!controlPlaneUrl) {\n return { ok: false as const, reason: 'VO_CONTROL_PLANE_URL not set \u2014 cloud mode disabled' };\n }\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) return { ok: false as const, reason: 'No auth configured. Run `vo-mcp login`.' };\n const token = await tokenSource.getToken();\n if (!token) return { ok: false as const, reason: 'Failed to obtain auth token. Run `vo-mcp login` again.' };\n return { ok: true as const, controlPlaneUrl, token };\n}\n\nasync function callPrivateKnowledge(\n path: string,\n body: unknown,\n fetchFn: FetchLike,\n) {\n const auth = await getCloudAuth(fetchFn);\n if (!auth.ok) return { ok: false, reason: auth.reason };\n const response = await fetchFn(`${auth.controlPlaneUrl}${path}`, {\n method: 'POST',\n headers: {\n authorization: `Bearer ${auth.token}`,\n 'content-type': 'application/json',\n },\n body: JSON.stringify(body),\n });\n const text = await response.text();\n // A proxy 502/503 can carry an HTML body \u2014 surface it as a structured\n // failure rather than escaping as an uncaught SyntaxError.\n let parsed: unknown;\n try {\n parsed = text ? JSON.parse(text) as unknown : null;\n } catch {\n parsed = null;\n }\n if (response.status < 200 || response.status >= 300) {\n return { ok: false, status: response.status, response: parsed ?? text };\n }\n return parsed;\n}\n\nexport async function handlePrivateKnowledgeUpsert(\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 (!isUpsertInput(rawInput)) {\n throw invalidParams(UPSERT_TOOL_NAME, 'expected { knowledge_class, source_path, title, content }.');\n }\n const payload = await callPrivateKnowledge('/api/v1/knowledge/private', rawInput, fetchFn);\n const envelope: { tool: string; schema_version: number; payload: unknown; precision_note?: string } = {\n tool: UPSERT_TOOL_NAME,\n schema_version: 1,\n payload,\n };\n if (rawInput.content.length > PRECISION_CHAR_BUDGET) {\n // Additive optional field \u2014 schema_version stays 1 (adding optional fields\n // is allowed under the lane's schema-v1 lock; required shape unchanged).\n envelope.precision_note =\n `content is ${rawInput.content.length} chars (> ${PRECISION_CHAR_BUDGET}). ` +\n `Tight 1-3 page entries retrieve better \u2014 consider splitting into focused entries, ` +\n `then re-test retrieval via ${CONTEXT_TOOL_NAME}.`;\n }\n return jsonContent(envelope);\n}\n\nexport async function handlePrivateKnowledgeInvalidate(\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 (!isInvalidateInput(rawInput)) {\n throw invalidParams(INVALIDATE_TOOL_NAME, 'expected { knowledge_class, source_path }.');\n }\n const payload = await callPrivateKnowledge('/api/v1/knowledge/private/invalidate', rawInput, fetchFn);\n return jsonContent({ tool: INVALIDATE_TOOL_NAME, schema_version: 1, payload });\n}\n\nexport async function handlePrivateKnowledgeStale(\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 (!isStaleInput(rawInput)) {\n throw invalidParams(STALE_TOOL_NAME, 'expected { optional days, optional limit }.');\n }\n const auth = await getCloudAuth(fetchFn);\n if (!auth.ok) return jsonContent({ tool: STALE_TOOL_NAME, schema_version: 1, payload: { ok: false, reason: auth.reason } });\n const params = new URLSearchParams();\n if (rawInput?.days !== undefined) params.set('days', String(Math.trunc(rawInput.days)));\n if (rawInput?.limit !== undefined) params.set('limit', String(Math.trunc(rawInput.limit)));\n const qs = params.toString();\n const response = await fetchFn(`${auth.controlPlaneUrl}/api/v1/knowledge/private/stale${qs ? `?${qs}` : ''}`, {\n method: 'GET',\n headers: { authorization: `Bearer ${auth.token}` },\n });\n const text = await response.text();\n let parsed: unknown;\n try { parsed = text ? JSON.parse(text) as unknown : null; } catch { parsed = null; }\n const payload = response.status < 200 || response.status >= 300\n ? { ok: false, status: response.status, response: parsed ?? text }\n : parsed;\n return jsonContent({ tool: STALE_TOOL_NAME, schema_version: 1, payload });\n}\n\nexport async function handlePrivateKnowledgeContext(\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 (!isContextInput(rawInput)) {\n throw invalidParams(CONTEXT_TOOL_NAME, 'expected { query, optional limit, optional knowledge_class }.');\n }\n const payload = await callPrivateKnowledge('/api/v1/knowledge/private/context', rawInput, fetchFn);\n return jsonContent({ tool: CONTEXT_TOOL_NAME, schema_version: 1, payload });\n}\n", "import type { ToolDeps } from '../../types.js';\nimport { createAuthTokenSourceFromEnv } from '../../cloud/auth-token-source.js';\nimport { readStoredCredential } from '../../cloud/credential-store.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\n\nexport const POST_TOOL_NAME = 'hq_whiteboard_post';\nexport const READ_TOOL_NAME = 'hq_whiteboard_read';\n\nexport const postDescription =\n 'Post an append-only coordination note to the live AlgoHQ whiteboard. Uses the scoped credential from vo-mcp login; operator and tenant ownership are derived by the server.';\nexport const readDescription =\n 'Read recent coordination notes from the caller\\'s live AlgoHQ whiteboard. Uses the scoped credential from vo-mcp login and cannot widen tenant scope.';\n\nexport const postInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n from: { type: 'string', minLength: 1, maxLength: 100, description: 'Agent/session display name.' },\n type: { type: 'string', minLength: 1, maxLength: 64, description: 'Message kind, such as intent, worklog, blocker, or completion.' },\n content: { type: 'string', minLength: 1, maxLength: 500, description: 'Short coordination note.' },\n targetAgent: { type: 'string', maxLength: 100 },\n tester: { type: 'string', maxLength: 100 },\n tier: { type: 'string', maxLength: 32 },\n },\n required: ['from', 'type', 'content'],\n additionalProperties: false,\n};\n\nexport const readInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n limit: { type: 'integer', minimum: 1, maximum: 100, default: 25 },\n since: { type: 'string', description: 'Optional ISO-8601 lower bound.' },\n type: { type: 'string', minLength: 1, maxLength: 64 },\n },\n additionalProperties: false,\n};\n\ninterface PostInput {\n readonly from: string;\n readonly type: string;\n readonly content: string;\n readonly targetAgent?: string;\n readonly tester?: string;\n readonly tier?: string;\n}\n\ninterface ReadInput {\n readonly limit?: number;\n readonly since?: string;\n readonly type?: string;\n}\n\ntype FetchLike = typeof fetch;\n\nfunction resolveTimeoutMs(): number {\n const parsed = Number(process.env['HQ_WHITEBOARD_TIMEOUT_MS']);\n return Number.isFinite(parsed) && parsed >= 10 && parsed <= 120_000 ? parsed : 10_000;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction onlyKeys(value: Record<string, unknown>, allowed: readonly string[]): boolean {\n return Object.keys(value).every((key) => allowed.includes(key));\n}\n\nfunction isBoundedString(value: unknown, min: number, max: number): value is string {\n return typeof value === 'string' && value.trim().length >= min && value.trim().length <= max;\n}\n\nfunction parsePostInput(value: unknown): PostInput | null {\n if (!isRecord(value) || !onlyKeys(value, ['from', 'type', 'content', 'targetAgent', 'tester', 'tier'])) return null;\n if (!isBoundedString(value['from'], 1, 100)) return null;\n if (!isBoundedString(value['type'], 1, 64) || !/^[a-zA-Z0-9_-]+$/.test(value['type'].trim())) return null;\n if (!isBoundedString(value['content'], 1, 500)) return null;\n for (const [key, max] of [['targetAgent', 100], ['tester', 100], ['tier', 32]] as const) {\n if (value[key] !== undefined && !isBoundedString(value[key], 0, max)) return null;\n }\n return {\n from: value['from'].trim(),\n type: value['type'].trim(),\n content: value['content'].trim(),\n ...(typeof value['targetAgent'] === 'string' ? { targetAgent: value['targetAgent'].trim() } : {}),\n ...(typeof value['tester'] === 'string' ? { tester: value['tester'].trim() } : {}),\n ...(typeof value['tier'] === 'string' ? { tier: value['tier'].trim() } : {}),\n };\n}\n\nfunction parseReadInput(value: unknown): ReadInput | null {\n if (!isRecord(value) || !onlyKeys(value, ['limit', 'since', 'type'])) return null;\n if (value['limit'] !== undefined && (!Number.isInteger(value['limit']) || Number(value['limit']) < 1 || Number(value['limit']) > 100)) return null;\n if (value['since'] !== undefined && (typeof value['since'] !== 'string' || Number.isNaN(Date.parse(value['since'])))) return null;\n if (value['type'] !== undefined && !isBoundedString(value['type'], 1, 64)) return null;\n return {\n ...(typeof value['limit'] === 'number' ? { limit: value['limit'] } : {}),\n ...(typeof value['since'] === 'string' ? { since: value['since'] } : {}),\n ...(typeof value['type'] === 'string' ? { type: value['type'].trim() } : {}),\n };\n}\n\nasync function resolveCloud(fetchFn: FetchLike): Promise<{ url: string; token: string } | null> {\n const url = process.env['VO_CONTROL_PLANE_URL']?.trim().replace(/\\/$/, '');\n if (!url) return null;\n try {\n const source = createAuthTokenSourceFromEnv(process.env, fetchFn, () => readStoredCredential(process.env));\n const token = await source?.getToken();\n return token ? { url, token } : null;\n } catch {\n return null;\n }\n}\n\nasync function callWhiteboard(\n method: 'GET' | 'POST',\n bodyOrQuery: PostInput | ReadInput,\n signal?: AbortSignal,\n fetchFn: FetchLike = fetch,\n): Promise<unknown> {\n const cloud = await resolveCloud(fetchFn);\n if (!cloud) {\n return {\n ok: false,\n error: 'hq_whiteboard_not_configured',\n message: 'Set VO_CONTROL_PLANE_URL and run vo-mcp login to install a scoped HQ credential.',\n };\n }\n const timeoutSignal = AbortSignal.timeout(resolveTimeoutMs());\n const requestSignal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;\n const query = new URLSearchParams();\n if (method === 'GET') {\n const input = bodyOrQuery as ReadInput;\n query.set('limit', String(input.limit ?? 25));\n if (input.since) query.set('since', input.since);\n if (input.type) query.set('type', input.type);\n }\n try {\n const response = await fetchFn(\n `${cloud.url}/api/v1/hq/whiteboard/messages${query.size ? `?${query}` : ''}`,\n {\n method,\n headers: {\n Authorization: `Bearer ${cloud.token}`,\n ...(method === 'POST' ? { 'Content-Type': 'application/json' } : {}),\n },\n ...(method === 'POST' ? { body: JSON.stringify(bodyOrQuery) } : {}),\n signal: requestSignal,\n },\n );\n const text = await response.text();\n let payload: unknown;\n try { payload = JSON.parse(text); } catch { payload = { ok: false, error: 'invalid_response', message: text.slice(0, 200) }; }\n if (!response.ok) {\n return { ok: false, error: 'hq_whiteboard_http_error', status: response.status, response: payload };\n }\n return payload;\n } catch (error) {\n return {\n ok: false,\n error: signal?.aborted\n ? 'cancelled'\n : timeoutSignal.aborted\n ? 'hq_whiteboard_timeout'\n : 'hq_whiteboard_unreachable',\n message: error instanceof Error ? error.message.slice(0, 200) : String(error).slice(0, 200),\n };\n }\n}\n\nexport async function handleHqWhiteboardPost(\n _deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<{ content: Array<{ type: 'text'; text: string }> }> {\n const input = parsePostInput(rawInput);\n if (!input) throw invalidParams(POST_TOOL_NAME, 'requires from, type, and 1-500 character content; unknown fields are rejected');\n return jsonContent(await callWhiteboard('POST', input, signal));\n}\n\nexport async function handleHqWhiteboardRead(\n _deps: ToolDeps,\n rawInput: unknown,\n signal?: AbortSignal,\n): Promise<{ content: Array<{ type: 'text'; text: string }> }> {\n const input = parseReadInput(rawInput);\n if (!input) throw invalidParams(READ_TOOL_NAME, 'limit must be 1-100, since must be ISO-8601, and unknown fields are rejected');\n return jsonContent(await callWhiteboard('GET', input, signal));\n}\n\nexport const __test = { callWhiteboard, parsePostInput, parseReadInput, resolveTimeoutMs };\n", "/**\n * Skill-corpus tools \u2014 cross-vendor access to the Algosuite skill library.\n *\n * Wires `@algosuite/skill-registry` (the vendor-neutral serializer over\n * `.claude/skills/<name>/SKILL.md`) into the MCP surface so every MCP\n * client \u2014 Claude Code, Codex, Cursor, Continue, Gemini \u2014 can discover\n * and load the SAME skill corpus Claude Code reads natively. Closes the\n * parity gap where the registry package had zero consumers (2026-07-20\n * cross-vendor parity audit) and skills were Claude-only.\n *\n * Two tools, not 28: registering every skill as its own MCP tool would\n * double the server's tool list and tax every session's context. The\n * catalog/get pair gives the same capability (discover \u2192 load) at a\n * fixed 2-tool cost. Per-skill tool registration (the registry's\n * `toMcpToolDefinitions` batch path) remains available for a future\n * opt-in mode if a client wants native per-skill tools.\n *\n * Corpus resolution order:\n * 1. `VO_SKILLS_DIR` env var (absolute path to a skills directory)\n * 2. Walk up from cwd looking for `.claude/skills` (max 8 levels)\n * Fail-open: when no corpus is found the tools stay registered and\n * return `corpus_available: false` with the reason \u2014 an installed\n * vo-mcp outside any repo must not crash `tools/list`.\n */\nimport { existsSync, statSync } from 'node:fs';\nimport { dirname, isAbsolute, join, resolve } from 'node:path';\nimport {\n loadSkillsFromDir,\n type Skill,\n} from '@algosuite/skill-registry';\nimport type { ToolDeps } from '../../types.js';\nimport { invalidParams, jsonContent, type ToolInputSchema } from '../common.js';\n\nexport const LIST_TOOL_NAME = 'vo_skill_list';\nexport const GET_TOOL_NAME = 'vo_skill_get';\n\nexport const listDescription =\n 'List the Algosuite skill corpus (name + trigger description for every skill). ' +\n 'Call once near session start to learn which skills exist; then fetch the full ' +\n 'instructions for a relevant skill with vo_skill_get. This is the same corpus ' +\n 'Claude Code loads natively from .claude/skills \u2014 served over MCP so every ' +\n 'vendor works from identical playbooks. Pass refresh:true to re-scan from disk.';\n\nexport const getDescription =\n 'Fetch the full markdown instructions of one Algosuite skill by name. ' +\n 'Follow the returned instructions for the current task the same way a ' +\n 'native skill invocation would. Use vo_skill_list to discover skill names.';\n\nexport const listInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n refresh: {\n type: 'boolean',\n description: 'Re-scan the skills directory instead of using the cached corpus.',\n },\n },\n required: [],\n};\n\nexport const getInputSchema: ToolInputSchema = {\n type: 'object',\n properties: {\n name: {\n type: 'string',\n description: 'Skill name exactly as returned by vo_skill_list.',\n },\n },\n required: ['name'],\n};\n\nconst MAX_WALK_UP_LEVELS = 8;\n\ninterface CorpusState {\n readonly skills: readonly Skill[];\n readonly skillsDir: string | null;\n /** Populated when the corpus could not be loaded; surfaced to callers. */\n readonly unavailableReason: string | null;\n}\n\nlet cachedCorpus: CorpusState | null = null;\n\n/** Locate a skills directory: env override first, then upward scan from cwd. */\nexport function resolveSkillsDir(\n env: NodeJS.ProcessEnv = process.env,\n startDir: string = process.cwd(),\n): string | null {\n const override = env.VO_SKILLS_DIR;\n if (typeof override === 'string' && override.length > 0) {\n const abs = isAbsolute(override) ? override : resolve(startDir, override);\n return existsSync(abs) && statSync(abs).isDirectory() ? abs : null;\n }\n let dir = resolve(startDir);\n for (let i = 0; i < MAX_WALK_UP_LEVELS; i += 1) {\n const candidate = join(dir, '.claude', 'skills');\n if (existsSync(candidate) && statSync(candidate).isDirectory()) return candidate;\n const parent = dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n}\n\nfunction loadCorpus(): CorpusState {\n const skillsDir = resolveSkillsDir();\n if (skillsDir === null) {\n return {\n skills: [],\n skillsDir: null,\n unavailableReason:\n 'No skills directory found. Set VO_SKILLS_DIR or run inside a repo with .claude/skills.',\n };\n }\n try {\n return { skills: loadSkillsFromDir(skillsDir), skillsDir, unavailableReason: null };\n } catch (err) {\n // Loader is fail-loud on malformed frontmatter by contract. Surface the\n // error to the caller instead of crashing the server \u2014 a broken skill\n // file must be visible, not fatal to every other tool on the server.\n const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);\n return { skills: [], skillsDir, unavailableReason: message };\n }\n}\n\nfunction getCorpus(refresh: boolean): CorpusState {\n if (refresh || cachedCorpus === null) {\n cachedCorpus = loadCorpus();\n }\n return cachedCorpus;\n}\n\n/** Test hook \u2014 drop the module-level corpus cache. */\nexport function resetCorpusCacheForTests(): void {\n cachedCorpus = null;\n}\n\nexport async function handleSkillList(\n _deps: ToolDeps,\n rawInput: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }> }> {\n const input = (rawInput ?? {}) as { refresh?: unknown };\n const refresh = input.refresh === true;\n const corpus = getCorpus(refresh);\n return jsonContent({\n corpus_available: corpus.unavailableReason === null,\n skills_dir: corpus.skillsDir,\n unavailable_reason: corpus.unavailableReason,\n skill_count: corpus.skills.length,\n skills: corpus.skills.map((s) => ({ name: s.name, description: s.description })),\n });\n}\n\nexport async function handleSkillGet(\n _deps: ToolDeps,\n rawInput: unknown,\n): Promise<{ content: Array<{ type: 'text'; text: string }> }> {\n const input = (rawInput ?? {}) as { name?: unknown };\n if (typeof input.name !== 'string' || input.name.trim().length === 0) {\n throw invalidParams(GET_TOOL_NAME, 'input field \"name\" (non-empty string) is required');\n }\n const requested = input.name.trim();\n const corpus = getCorpus(false);\n if (corpus.unavailableReason !== null) {\n return jsonContent({\n corpus_available: false,\n unavailable_reason: corpus.unavailableReason,\n skill: null,\n });\n }\n const skill = corpus.skills.find((s) => s.name === requested);\n if (skill === undefined) {\n throw invalidParams(\n GET_TOOL_NAME,\n `unknown skill \"${requested}\". Known skills: ${corpus.skills.map((s) => s.name).join(', ')}`,\n );\n }\n return jsonContent({\n corpus_available: true,\n skill: {\n name: skill.name,\n description: skill.description,\n instructions: skill.body,\n source_path: skill.sourcePath,\n },\n });\n}\n", "/**\n * Skill loader \u2014 fs-scan a directory of skills + parse each SKILL.md.\n *\n * Expects the directory layout used by `.claude/skills/`:\n *\n * <skillsDir>/\n * <skill-a>/\n * SKILL.md \u2190 required, with YAML frontmatter\n * <skill-b>/\n * SKILL.md\n * README.md \u2190 ignored (no subdirectory)\n *\n * The frontmatter parser is intentionally minimal \u2014 accepts just\n * `name` and `description` fields. Adding fields is a V1 task and\n * should be done by extending `Skill` in types.ts first.\n */\nimport { readdirSync, readFileSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Skill } from './types.js';\n\n/** Thrown when a skill directory lacks a SKILL.md file. */\nexport class MissingSkillFileError extends Error {\n override readonly name = 'MissingSkillFileError';\n constructor(public readonly skillDir: string) {\n super(`Skill directory has no SKILL.md: ${skillDir}`);\n }\n}\n\n/** Thrown when a SKILL.md has malformed or absent frontmatter. */\nexport class InvalidSkillFrontmatterError extends Error {\n override readonly name = 'InvalidSkillFrontmatterError';\n constructor(public readonly skillFile: string, public readonly reason: string) {\n super(`Invalid frontmatter in ${skillFile}: ${reason}`);\n }\n}\n\nconst FRONTMATTER_DELIMITER = '---';\n\nfunction parseFrontmatter(rawInput: string, sourcePath: string): { name: string; description: string; body: string } {\n // Normalize line endings \u2014 SKILL.md files on Windows often have CRLF,\n // but the parser logic is LF-only. This is a tolerant normalization\n // that doesn't affect file content semantics.\n const raw = rawInput.replace(/\\r\\n/g, '\\n');\n if (!raw.startsWith(`${FRONTMATTER_DELIMITER}\\n`)) {\n throw new InvalidSkillFrontmatterError(sourcePath, 'file does not start with frontmatter delimiter \"---\"');\n }\n const afterFirst = raw.slice(FRONTMATTER_DELIMITER.length + 1);\n const closingIdx = afterFirst.indexOf(`\\n${FRONTMATTER_DELIMITER}\\n`);\n if (closingIdx === -1) {\n throw new InvalidSkillFrontmatterError(sourcePath, 'missing closing frontmatter delimiter \"---\"');\n }\n const frontmatterText = afterFirst.slice(0, closingIdx);\n const body = afterFirst.slice(closingIdx + `\\n${FRONTMATTER_DELIMITER}\\n`.length);\n\n let name = '';\n let description = '';\n for (const line of frontmatterText.split('\\n')) {\n const trimmed = line.trim();\n if (trimmed.length === 0) continue;\n const colonIdx = trimmed.indexOf(':');\n if (colonIdx === -1) continue;\n const key = trimmed.slice(0, colonIdx).trim();\n const value = trimmed.slice(colonIdx + 1).trim();\n if (key === 'name') name = value;\n else if (key === 'description') description = value;\n }\n if (name.length === 0) {\n throw new InvalidSkillFrontmatterError(sourcePath, 'missing required field \"name\"');\n }\n if (description.length === 0) {\n throw new InvalidSkillFrontmatterError(sourcePath, 'missing required field \"description\"');\n }\n return { name, description, body };\n}\n\n/**\n * Load and parse every SKILL.md in the given directory.\n *\n * Top-level files (e.g. README.md) and non-directory entries are\n * skipped. Subdirectories without SKILL.md are skipped silently\n * (allows scaffolding-in-progress without breaking the registry).\n *\n * Throws `InvalidSkillFrontmatterError` if ANY skill has malformed\n * frontmatter \u2014 the loader is fail-loud on bad data because skills\n * are surfaced to model panels and silent skips hide real bugs.\n */\nexport function loadSkillsFromDir(skillsDir: string): readonly Skill[] {\n const entries = readdirSync(skillsDir);\n const skills: Skill[] = [];\n for (const entry of entries) {\n const entryPath = join(skillsDir, entry);\n let stat;\n try {\n stat = statSync(entryPath);\n } catch {\n continue;\n }\n if (!stat.isDirectory()) continue;\n const skillFile = join(entryPath, 'SKILL.md');\n let raw: string;\n try {\n raw = readFileSync(skillFile, 'utf8');\n } catch {\n // Subdirectory without SKILL.md \u2014 silently skip\n continue;\n }\n const { name, description, body } = parseFrontmatter(raw, skillFile);\n skills.push({ name, description, body, sourcePath: skillFile });\n }\n // Stable sort by name for deterministic output.\n return [...skills].sort((a, b) => a.name.localeCompare(b.name));\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 * 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 { getVoMcpConsensusPanel } from './consensus-panel.js';\nimport {\n resolveAgreementGate,\n resolveMinResponders,\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 /** Per-call inference tokens, when the adapter reported usage (additive). */\n input_tokens?: number;\n output_tokens?: number;\n /** Exact attested upstream cost in micro-USD, when a receipt provided it. */\n cost_micro_usd?: number;\n error?: { category: string; message: string };\n }>;\n /** Engine-computed cumulative usage across EVERY round (B44-3). */\n token_usage?: {\n per_model_tokens_in: Readonly<Record<string, number>>;\n per_model_tokens_out: Readonly<Record<string, number>>;\n cost_micro_usd: number | null;\n rounds_counted: number;\n };\n degraded: boolean;\n /** Engine min_responders quorum \u2014 true when the verdict was floored to 'uncertain' below quorum. */\n quorum_failed?: 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 /** Critique-uptake visibility report (engine gates/critique-uptake.ts) \u2014 passed through opaquely. */\n critique_uptake?: Readonly<Record<string, unknown>>;\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 /** Quorum floor \u2014 below this many responders the verdict floors to 'uncertain'. */\n min_responders?: number;\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 // Quorum floor (engine min_responders). DEFAULT ON at 2 \u2014 a panel that\n // degrades to a single responder must never ship a badged verdict (the\n // engine's single-survivor >=0.8 agreement floor). Override with\n // VO_CONSENSUS_MIN_RESPONDERS=<n>; '0' disables entirely.\n const minResponders = resolveMinResponders(options.env);\n const engineOptions: {\n panel: readonly ConsensusModelAdapter[];\n per_model_timeout_ms?: number;\n agreement_gate?: EngineAgreementGateShape;\n shadow_synthesis?: { enabled: boolean };\n min_responders?: number;\n } = {\n panel,\n ...(options.per_model_timeout_ms !== undefined\n ? { per_model_timeout_ms: options.per_model_timeout_ms }\n : {}),\n ...(minResponders !== undefined ? { min_responders: minResponders } : {}),\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 ...(response.quorum_failed === true ? { quorum_failed: true } : {}),\n duration_ms: response.duration_ms,\n engine_version: response.engine_version,\n // Cumulative cross-round inference usage (B44-3). Absent when no panel\n // member reported usage; forwarded verbatim \u2014 the aggregator prefers it\n // over summing final-round verdicts (which under-reports deliberation).\n ...(response.token_usage !== undefined ? { token_usage: response.token_usage } : {}),\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 // Critique-uptake (2026-07-20 red-team fix) \u2014 verifier-critique\n // visibility report; previously computed by the engine on every\n // call but dropped at this boundary.\n ...(response.critique_uptake !== undefined\n ? { critique_uptake: response.critique_uptake }\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 per provider \u2014 the registered `voMcp` consensus panel.\n// Formalized out of inline literals here into `consensus-panel.ts` (roadmap\n// Track D / PR-T) so upstream model deprecations flow through the model-ops\n// codegen instead of silently breaking the panel. See that module's header\n// for why the panel is registered in-package rather than in functions-shared.\nconst DEFAULT_MODELS = getVoMcpConsensusPanel();\n\ntype ProviderKey = 'anthropic' | 'openai' | 'google';\n\ntype ConsensusModelCaller = (\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\ninterface EngineFactoryShape {\n createAdapter(\n providerId: ProviderKey,\n options: {\n readonly model: string;\n readonly caller: ConsensusModelCaller;\n readonly envSource?: Readonly<Record<string, string | undefined>>;\n },\n ): ConsensusModelAdapter;\n}\n\ninterface FunctionsSharedShape {\n callAnthropicWithMetrics: ConsensusModelCaller;\n callOpenAIWithMetrics: ConsensusModelCaller;\n callGeminiWithMetrics: ConsensusModelCaller;\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 // Restricted providers never join the local direct panel merely because a\n // key is present. DeepSeek and Muse use sanitized Model Firewall capsules.\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, ConsensusModelCaller>> = {\n anthropic: loaded.shared.callAnthropicWithMetrics,\n openai: loaded.shared.callOpenAIWithMetrics,\n google: loaded.shared.callGeminiWithMetrics,\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 };\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 /** Engine min_responders quorum \u2014 true when the verdict was floored to 'uncertain' below quorum. */\n readonly quorum_failed?: 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 * 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", "/** Muse Spark is restricted to sanitized Model Firewall task capsules. */\n\n// MODEL-OPS-INTENTIONAL: catalog identity retained for health and migration UI.\nexport const META_CONSENSUS_MODEL = 'muse-spark-1.1';\nexport const META_MODEL_API_KEY_ENV = 'MODEL_API_KEY';\nexport const META_MODEL_API_KEY_ALIAS = 'META_API';\n\ntype EnvSource = Readonly<Record<string, string | undefined>>;\ntype FetchImpl = typeof fetch;\n\nexport interface MetaModelCallerOptions {\n readonly fetchImpl?: FetchImpl;\n readonly envSource?: EnvSource;\n readonly reasoningEffort?: 'low' | 'medium' | 'high' | 'xhigh';\n}\n\nexport function createMetaModelCaller(options: MetaModelCallerOptions = {}) {\n void options;\n return async function callMetaWithMetrics(): Promise<never> {\n throw new Error(\n 'Muse Spark direct consensus is disabled. Use an explicit sanitized task capsule through the AlgoSuite Model Firewall.',\n );\n };\n}\n\nexport const callMetaWithMetrics = createMetaModelCaller();\n", "/**\n * vo-mcp consensus panel \u2014 the registered `voMcp` judgment panel.\n *\n * AlgoHQ roadmap Track D / PR-T (`docs/vo/vo-roadmap-2026-05-26.md` \u00A73.3) calls\n * for formalizing the previously-inline consensus model IDs in\n * `engine-client.ts` into a single registered panel so upstream model\n * deprecations can't silently break alpha customers.\n *\n * RED-LINE-safe location. The roadmap's literal wording pointed at\n * `functions-shared/src/shared-model-resolvers.ts::CONSENSUS_PANELS`, but ANY\n * non-test edit to `functions-shared/src/*` auto-escalates the deploy scope to\n * every function codebase (~24 codebases / ~1,132 instances / ~24h full\n * deploy \u2014 the operator's #1 operational RED LINE). No Cloud Function reads\n * this panel: vo-mcp is a local stdio MCP server (`node dist/cli.js`), never\n * deployed to Firebase. So the panel is registered HERE, colocated with its\n * only consumer, and protected by the SAME model-ops mechanism as the\n * functions-shared panels: an allowlist entry in\n * `scripts/ci/.model-pin-allowlist.json` marks this file as a MODEL_REWRITE\n * target, so the model-ops codegen rewrites these IDs on deprecation exactly\n * as it does for CONSENSUS_PANELS.\n *\n * Keyed by consensus-engine provider key (NOT persona), to match the adapter\n * construction path in `engine-client.ts` (`createAdapter(provider, ...)`).\n */\n\nimport { META_CONSENSUS_MODEL } from './meta-model-caller.js';\n\n/** Providers the vo-mcp consensus panel can draw from. */\nexport type VoMcpPanelProvider = 'anthropic' | 'openai' | 'google' | 'deepseek' | 'meta';\n\n/**\n * Registered default model IDs for the vo-mcp consensus panel, one per\n * provider. Callers may override any slot at construction time\n * (`EngineFromEnvOptions.models`); these are the fallbacks.\n */\nexport const VO_MCP_CONSENSUS_PANEL: Readonly<Record<VoMcpPanelProvider, string>> = {\n // claude-opus-5 (2026-07-24). Opus 4.7 was STRICTLY DOMINATED, not merely old:\n // Opus 5 is $5/$25 per MTok vs Opus 4.7's $15/$75 \u2014 a 3x cost cut on this slot,\n // corroborated by our own catalog (constants/pricing/sciencePricing.ts prices\n // claude-opus-5 at 0.010 vs claude-opus-4-7 at 0.030) \u2014 AND the same 2026-07-24\n // release note REMOVED fast mode from Opus 4.7 outright: `speed: \"fast\"` now\n // returns an error there rather than degrading, unlike the Opus 4.6 removal.\n // Verified served: GET /v1/models/claude-opus-5 -> HTTP 200 (2026-07-30).\n //\n // Claude-5 API safety checked before this swap: Opus 5 rejects `temperature` /\n // `top_p` / `top_k` and manual `thinking.budget_tokens` with HTTP 400. Neither\n // the consensus-engine Anthropic adapter nor functions-shared `callAnthropic`\n // sends any of them, and buildAdaptiveThinking emits `thinking: {type:'adaptive'}`\n // (the supported form) \u2014 so this swap cannot 400.\n anthropic: 'claude-opus-5',\n // gpt-5.6-terra (GA 2026-07-09; \u221220% price cut 2026-07-30). NOTE the real IDs\n // are tiered \u2014 `gpt-5.6-sol` / `-terra` / `-luna`; there is NO bare `gpt-5.6`\n // alias (verified against the served model list, 2026-07-30). Terra is the\n // cost/capability balance point and the right default for a judgment panel;\n // Sol is available if verdict quality ever needs it.\n openai: 'gpt-5.6-terra',\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 // Muse Spark identity is owned by meta-model-caller.ts (single source of\n // truth for the meta slot); re-exported here so the panel stays complete.\n meta: META_CONSENSUS_MODEL,\n} as const;\n\n/**\n * Return the vo-mcp consensus panel. Throws on any missing / blank model ID so\n * a bad model-ops codegen rewrite fails loud at module load instead of\n * producing a silently mis-routed adapter \u2014 the \"don't silently break alpha\n * customers\" guarantee PR-T exists to deliver. Mirrors `getConsensusPanel` in\n * `functions-shared/src/shared-model-resolvers.ts`.\n *\n * `panel` is injectable purely so the validation path is unit-testable; callers\n * should use the default.\n */\nexport function getVoMcpConsensusPanel(\n panel: Readonly<Record<VoMcpPanelProvider, string>> = VO_MCP_CONSENSUS_PANEL,\n): Readonly<Record<VoMcpPanelProvider, string>> {\n for (const [provider, modelId] of Object.entries(panel)) {\n if (typeof modelId !== 'string' || modelId.trim().length === 0) {\n throw new Error(\n `getVoMcpConsensusPanel: panel slot \"${provider}\" has a missing or blank model ID`,\n );\n }\n }\n return panel;\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\nexport const MIN_RESPONDERS_ENV_VAR = 'VO_CONSENSUS_MIN_RESPONDERS';\n\n/**\n * Quorum floor is opt-OUT (DEFAULT 2): a panel that degrades to a single\n * responder must never ship a badged verdict \u2014 the engine's strict-consensus\n * single-survivor path clamps confidence UP to >=0.8 while agreement metrics\n * vanish below 2 responders. Override with `VO_CONSENSUS_MIN_RESPONDERS=<n>`;\n * `0` (or any non-positive/invalid value) disables the floor entirely.\n * Returns undefined when disabled so the engine option is simply omitted.\n */\nexport function resolveMinResponders(\n env: Readonly<Record<string, string | undefined>> | undefined,\n): number | undefined {\n const raw = (env ?? {})[MIN_RESPONDERS_ENV_VAR];\n if (raw === undefined || raw.trim() === '') return 2;\n const parsed = Number.parseInt(raw.trim(), 10);\n if (!Number.isFinite(parsed) || parsed <= 0) return undefined;\n return parsed;\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", "/**\n * Cloud consensus client \u2014 repoints the consensus tools at the LIVE\n * `vo-moat-plane` `/api/v1/verify` endpoint (ADR-002 \"fat secret server, thin\n * client\"). Increment 2 of the AlgoHQ Command Center unification\n * (`docs/vo/vo-command-center-unification-spec-2026-06-05.md` \u00A75): removes the\n * 4 model API keys (`ANTHROPIC/OPENAI/GOOGLE/DEEPSEEK_API_KEY`) from the local\n * install \u2014 the local process no longer calls model providers directly.\n *\n * Only a SYNTHESIZED verdict crosses the wire (never prompts / thresholds /\n * panel composition \u2014 ADR-002), so `per_model_verdicts` is `[]` in cloud mode.\n * The consensus tools already tolerate that (`toEventPerModelVerdicts([])`,\n * `check-assertion-strength.ts` \"empty by design\"); the synthesized verdict\n * carries the result.\n *\n * FAIL-CLOSED: every unconfigured / non-2xx / malformed / network / aborted\n * path returns `{ ok: false, reason }` \u2014 NEVER a fake-green `pass`. Mirrors the\n * proven `cloud-run/vo-agent-executor/src/cloud/moat-client.ts`.\n */\nimport { randomUUID } from 'node:crypto';\nimport {\n CANCELLED_REASON,\n type ConsensusEngineClient,\n type ConsensusRequest,\n type ConsensusResult,\n} from './client.js';\n\n/** Default live moat endpoint (ADR-002 Inc 2). Override via `VO_MOAT_ENDPOINT`. */\nexport const DEFAULT_VO_MOAT_ENDPOINT = 'https://vo-moat-plane-620873201932.us-central1.run.app';\n\n/** Engine-version identifier stamped onto cloud verdicts (event-log grouping). */\nexport const MOAT_ENGINE_VERSION = 'moat-cloud-v1';\n\n/** Server-enforced excerpt cap (bytes); pre-checked client-side for a clear diagnostic. */\nexport const MOAT_EXCERPT_MAX_BYTES = 50_000;\n\n/** Stable cloud-mode unavailable reasons (pinned for event-log / dashboard grouping). */\nexport const MOAT_CLIENT_REASONS = {\n /** `available:false` from the moat (quota exhausted / <2 server-side keys / not entitled). */\n UNAVAILABLE: 'consensus-cloud-unavailable',\n /** Non-2xx HTTP response (status appended, e.g. `...:401`). */\n HTTP_ERROR: 'consensus-cloud-http-error',\n /** 2xx but the body is missing/invalid verdict fields (per the server contract). */\n MALFORMED: 'consensus-cloud-malformed-verdict',\n /** Network failure / local timeout (not the caller's cancellation). */\n NETWORK: 'consensus-cloud-network-error',\n /** Excerpt exceeds the server's 50KB cap \u2014 refused locally with a clear reason. */\n EXCERPT_TOO_LARGE: 'consensus-cloud-excerpt-too-large',\n} as const;\n\ntype FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface MoatConsensusClientOptions {\n readonly moatEndpoint: string;\n readonly entitlementToken: string;\n /** Local request timeout; default 15s. Independent of the caller's `signal`. */\n readonly timeoutMs?: number;\n /** Injectable fetch (tests); defaults to the global `fetch`. */\n readonly fetchFn?: FetchLike;\n}\n\n/** The wire verdict returned by `vo-moat-plane` `/api/v1/verify` (mirror of its contract). */\ninterface VerifyVerdict {\n readonly ok?: boolean;\n readonly available: boolean;\n readonly approved: boolean;\n readonly confidence?: number;\n readonly reason: string;\n readonly models_agreeing?: number;\n readonly degraded?: boolean;\n /** Moat decision ledger id (UUID) \u2014 surfaced to callers as the verification receipt. */\n readonly decision_id?: string;\n}\n\nfunction isVerdict(v: unknown): v is VerifyVerdict {\n if (!v || typeof v !== 'object') return false;\n const r = v as Record<string, unknown>;\n return (\n typeof r['available'] === 'boolean' &&\n typeof r['approved'] === 'boolean' &&\n // Contract: reason is a high-level rationale, min 10 chars. Reject empty/short\n // reasons as malformed (matches the proven vo-agent-executor client) so a verdict\n // never lands in the audit trail without a rationale.\n typeof r['reason'] === 'string' &&\n r['reason'].trim().length >= 10\n );\n}\n\n/** Clamp confidence to the contract's 0..1; non-finite/out-of-range \u21D2 0.5 (fail-safe). */\nfunction normalizeConfidence(raw: unknown): number {\n return typeof raw === 'number' && Number.isFinite(raw) && raw >= 0 && raw <= 1 ? raw : 0.5;\n}\n\n/** Accept models_agreeing only as a non-negative integer; anything else \u21D2 undefined. */\nfunction normalizeAgreeing(raw: unknown): number | undefined {\n return typeof raw === 'number' && Number.isInteger(raw) && raw >= 0 ? raw : undefined;\n}\n\n/**\n * Build a cloud-backed `ConsensusEngineClient`. Drop-in for the local engine\n * client \u2014 same interface, no model keys, verdicts computed server-side.\n */\nexport function createMoatConsensusClient(opts: MoatConsensusClientOptions): ConsensusEngineClient {\n const endpoint = opts.moatEndpoint.replace(/\\/+$/, '');\n const token = opts.entitlementToken;\n const timeoutMs = opts.timeoutMs ?? 15_000;\n const fetchFn: FetchLike = opts.fetchFn ?? ((input, init) => fetch(input, init));\n\n return {\n async run(request: ConsensusRequest): Promise<ConsensusResult> {\n // Honor the caller's cancellation up front.\n if (request.signal?.aborted) return { ok: false, reason: CANCELLED_REASON };\n\n // The server caps the excerpt at 50KB; refuse oversize locally with a clear\n // reason rather than burning a round-trip for an opaque 400/413.\n if (Buffer.byteLength(request.prompt, 'utf8') > MOAT_EXCERPT_MAX_BYTES) {\n return { ok: false, reason: MOAT_CLIENT_REASONS.EXCERPT_TOO_LARGE };\n }\n\n const startedAt = Date.now();\n const controller = new AbortController();\n const onAbort = (): void => controller.abort();\n request.signal?.addEventListener('abort', onAbort, { once: true });\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n const body = JSON.stringify({\n task_id: randomUUID(),\n gate_type: request.gate_type,\n excerpt: request.prompt,\n ...(request.system_prompt ? { question: request.system_prompt } : {}),\n ...(request.caller_context ? { context: request.caller_context } : {}),\n });\n\n try {\n const res = await fetchFn(`${endpoint}/api/v1/verify`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n // SECURITY: entitlement token \u2014 never logged.\n Authorization: `Bearer ${token}`,\n },\n body,\n signal: controller.signal,\n });\n\n if (!res.ok) {\n return { ok: false, reason: `${MOAT_CLIENT_REASONS.HTTP_ERROR}:${res.status}` };\n }\n\n const parsed: unknown = await res.json().catch(() => null);\n if (!isVerdict(parsed)) {\n return { ok: false, reason: MOAT_CLIENT_REASONS.MALFORMED };\n }\n\n // available:false \u21D2 quota exhausted / not entitled / <2 server keys.\n // Map to the engine's \"unavailable\" envelope so the tools fall back\n // exactly as they do for a missing local engine.\n if (!parsed.available) {\n return { ok: false, reason: parsed.reason || MOAT_CLIENT_REASONS.UNAVAILABLE };\n }\n\n const agreeing = normalizeAgreeing(parsed.models_agreeing);\n const reasoning =\n agreeing !== undefined ? `${parsed.reason} (${agreeing} models agreeing)` : parsed.reason;\n\n const receiptId = typeof parsed.decision_id === 'string' && parsed.decision_id.trim().length > 0\n ? parsed.decision_id.trim().slice(0, 120)\n : null;\n return {\n ok: true,\n ...(receiptId ? { receipt_id: receiptId } : {}),\n synthesized_verdict: {\n verdict: parsed.approved ? 'pass' : 'fail',\n confidence: normalizeConfidence(parsed.confidence),\n reasoning_excerpt: reasoning,\n synthesizer: 'cloud-moat',\n dissent_summary: null,\n },\n // Per-model detail stays server-side by design (ADR-002).\n per_model_verdicts: [],\n degraded: parsed.degraded === true,\n duration_ms: Date.now() - startedAt,\n engine_version: MOAT_ENGINE_VERSION,\n };\n } catch (err) {\n // Distinguish the caller's cancellation from a local timeout / network error.\n if (request.signal?.aborted) return { ok: false, reason: CANCELLED_REASON };\n if (err instanceof Error && err.name === 'AbortError') {\n return { ok: false, reason: `${MOAT_CLIENT_REASONS.NETWORK}:timeout` };\n }\n return { ok: false, reason: MOAT_CLIENT_REASONS.NETWORK };\n } finally {\n clearTimeout(timeoutId);\n request.signal?.removeEventListener('abort', onAbort);\n }\n },\n };\n}\n\n/**\n * Factory: returns a cloud consensus client when `VO_ENTITLEMENT_TOKEN` is set\n * (endpoint defaults to the live moat), else `null` so the caller falls back to\n * the local engine. The token's PRESENCE is the cloud-mode switch \u2014 identical\n * to `vo-agent-executor` + the control-plane gate, so a user who sets the token\n * (and drops the 4 model keys) gets cloud consensus with zero other config.\n */\nexport function tryCreateMoatConsensusClientFromEnv(\n env: Readonly<Record<string, string | undefined>> = process.env,\n fetchFn?: FetchLike,\n): ConsensusEngineClient | null {\n const token = env['VO_ENTITLEMENT_TOKEN']?.trim();\n if (!token) return null;\n const endpoint = env['VO_MOAT_ENDPOINT']?.trim() || DEFAULT_VO_MOAT_ENDPOINT;\n const timeoutRaw = env['VO_MOAT_TIMEOUT_MS'];\n const timeoutMs =\n timeoutRaw && Number.isFinite(Number(timeoutRaw)) && Number(timeoutRaw) > 0\n ? Number(timeoutRaw)\n : undefined;\n return createMoatConsensusClient({\n moatEndpoint: endpoint,\n entitlementToken: token,\n ...(timeoutMs !== undefined ? { timeoutMs } : {}),\n ...(fetchFn ? { fetchFn } : {}),\n });\n}\n", "/**\n * Consensus engine client interface \u2014 the boundary between the open\n * MCP server and the closed `@algosuite/consensus-engine` package.\n *\n * Per handoff \u00A7C-1 (\"open shell, closed content\"):\n * - This file defines the SHAPE the open shell consumes.\n * - `null-client.ts` provides a \"not configured\" implementation that\n * returns the unimplemented envelope shape; used when credentials\n * are absent or the engine is intentionally disabled.\n * - Production wires in a real client that calls `runConsensus` from\n * `@algosuite/consensus-engine`.\n *\n * Same shape as the ratchets boundary \u2014 narrow public interface, full\n * implementation lives in the closed package.\n */\n\n/** Verdict emitted by the consensus engine. */\nexport type ConsensusVerdict = 'pass' | 'fail' | 'uncertain';\n\nexport interface ConsensusPerModelVerdict {\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 /**\n * Per-call inference tokens, forwarded by value from the engine verdict when\n * the adapter reported usage (additive; absent on mocks/usage-less callers and\n * on the error path). Aggregated into the telemetry event's per_model_tokens\n * maps \u2014 the raw fuel for the cost-instrumentation dataset (Base1 lesson).\n */\n readonly input_tokens?: number;\n readonly output_tokens?: number;\n /** Exact attested upstream cost (micro-USD) when a broker receipt provided it. */\n readonly cost_micro_usd?: number;\n readonly error?: {\n readonly category: string;\n readonly message: string;\n };\n}\n\n/**\n * Discrete calibrated-confidence badge (engine Feature 2; arXiv:2606.13591).\n * Mirrors the engine's `ConfidenceBadge`. Surfaced so UI consumers can render\n * the band directly (UI-trust \"Confidence\" pillar) without re-deriving it.\n */\nexport type ConsensusConfidenceBadge = 'high' | 'medium' | 'low';\n\nexport interface ConsensusSynthesizedVerdict {\n readonly verdict: ConsensusVerdict;\n readonly confidence: number;\n readonly reasoning_excerpt: string;\n readonly synthesizer: string;\n readonly dissent_summary: string | null;\n /**\n * Engine Feature 2 (calibrated-confidence) \u2014 ADDITIVE cross-model calibrated\n * confidence. Does NOT replace `confidence` (which stays the synthesizer's\n * own value). Present whenever the engine's confidence-aggregation layer ran.\n * For the `runConsensus` path the engine attaches this UNCONDITIONALLY, so\n * VO gates get it ON by default (it only ADDS a field, never changes a verdict).\n */\n readonly calibrated_confidence?: number;\n /** Engine Feature 2 \u2014 discrete badge derived from `calibrated_confidence`. Present iff that is. */\n readonly confidence_badge?: ConsensusConfidenceBadge;\n}\n\n/**\n * Engine Feature 1 (agreement-gate) \u2014 fan-out cost accounting. Pure telemetry;\n * never alters the verdict. Present only when the agreement-gated early-exit\n * fan-out ran for this call. Mirrors the engine's `FanOutDiagnostics`.\n */\nexport interface ConsensusFanOutDiagnostics {\n readonly models_called: number;\n readonly panel_size: number;\n readonly early_exit: boolean;\n readonly refused: number;\n}\n\n/**\n * Stage A7-shadow comparison \u2014 the ADAPTIVE verdict (log-odds pooling, skill-weighted,\n * optionally calibrated) computed ALONGSIDE the live incumbent verdict, for grading.\n * PII-free: verdict labels + confidences only (no prompt, no model reasoning). Present\n * iff shadow mode is enabled. Pure observability \u2014 the live verdict is never affected.\n */\nexport interface ConsensusShadowSynthesis {\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 * Engine source-grounded Feature 1 (citation-grader) \u2014 deterministic Tier-4\n * citation / governed-fact grade. Mirrors the engine's `CitationGradeResult`,\n * projected to the open shell. Present only on the source-grounded path with\n * the grader enabled.\n */\nexport interface ConsensusCitationGrade {\n /** Gate decision \u2014 false means at least one claim was unsupported or low-confidence. */\n readonly ok: boolean;\n readonly unsupported_count: number;\n readonly uncertain_count: number;\n /** Per-claim grading rows (claim text, label, confidence). */\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/**\n * Behaviour-changing source-grounded feature toggles. ALL default OFF so the\n * production gate behaviour is unchanged until the operator opts in.\n */\nexport interface ConsensusSourceGroundedConfig {\n /**\n * Engine source-grounded Feature 1 \u2014 deterministic citation grader (Tier-4).\n * BEHAVIOUR-CHANGING: a failing grade raises an escalation signal. Default OFF.\n *\n * Requires an injected `ClaimClassifier` (a cheap model in prod, a fake in\n * tests). No cheap engine-backed classifier is auto-constructed \u2014 the engine's\n * `createAdapter` produces verdict-style adapters, not claim classifiers \u2014 so\n * this stays OFF until an operator/caller injects one. See engine-client.ts.\n */\n readonly citation_grader?: {\n readonly enabled: boolean;\n /**\n * Injected semantic grader (engine `ClaimClassifier` shape). MUST NOT throw.\n * OPTIONAL: when omitted (and `enabled === true`) the engine-client supplies\n * a real DEFAULT classifier built from its own panel adapter\n * (`createClaimClassifier` over the first live model) \u2014 so a caller that has\n * no classifier of its own (e.g. the `vo_consensus_judgment` tool) can still\n * turn grading on. A caller MAY inject its own to override the default.\n */\n readonly classifier?: ClaimClassifierShape;\n /** Uncertain-claim escalation floor; default 0.85 in the engine. */\n readonly escalate_below?: number;\n };\n /** Optional per-call URL allowlist function (engine `SourceUrlAllowFn`). */\n readonly allow_url?: (url: string) => boolean;\n}\n\n/**\n * Structural mirror of the engine's `ClaimClassifier`. Declared here so the\n * open shell can accept an injected classifier without importing the closed\n * engine's types. MUST NOT throw \u2014 return `{ label:'uncertain', confidence:0 }`.\n */\nexport interface ClaimClassifierShape {\n classify(args: {\n readonly claim: string;\n readonly sources: string;\n }): Promise<{ readonly label: 'supported' | 'unsupported' | 'uncertain'; readonly confidence: number }>;\n}\n\nexport interface ConsensusRequest {\n readonly gate_type: string;\n readonly prompt: string;\n readonly system_prompt?: string;\n readonly caller_context?: Record<string, unknown>;\n /**\n * Cancellation signal. When the SDK receives `notifications/cancelled`\n * for the request, this fires; the engine client races the engine call\n * against this signal and returns `{ ok: false, reason: 'cancelled' }`\n * promptly. Per-model API calls also receive the signal via the\n * wrapped adapter panel so in-flight fetch() calls are aborted at the\n * provider level. Added 2026-05-25 (audit HIGH \u2014 MCP cancellation).\n */\n readonly signal?: AbortSignal;\n /**\n * Optional source URLs. When NON-EMPTY, the engine-client routes the call to\n * the engine's source-grounded path (`runSourceGroundedConsensus`) instead of\n * the plain `runConsensus` fan-out, which activates the Tier-4 source-grounded\n * features (retrieval-confidence flagging + optional citation grading). When\n * absent/empty the call stays on the unchanged `runConsensus` path.\n *\n * NOTE: the `vo_consensus_judgment` tool does not currently populate this \u2014\n * the source-grounded features are wired-but-dormant until a caller passes\n * sources. See engine-client.ts and the dispatch report.\n */\n readonly source_urls?: readonly string[];\n /** Behaviour-changing source-grounded toggles (citation grader, allowlist). All default OFF. */\n readonly source_grounded?: ConsensusSourceGroundedConfig;\n}\n\n/** Stable reason string emitted when a consensus run is aborted via the signal. */\nexport const CANCELLED_REASON = 'cancelled';\n\nexport interface ConsensusSuccess {\n readonly ok: true;\n readonly synthesized_verdict: ConsensusSynthesizedVerdict;\n /**\n * The verification receipt (2026-08-15): the moat plane's `decision_id`\n * (UUID) for a cloud-verified call. This is what an agent pastes into its\n * PR body as `receipt id: <uuid>` and what the runner persists as the\n * task's `consensus_receipt_id` \u2014 the outcome ledger's proof that the\n * consensus stage actually ran. Absent on local-engine and test runs.\n */\n readonly receipt_id?: string;\n readonly per_model_verdicts: readonly ConsensusPerModelVerdict[];\n /**\n * CUMULATIVE inference usage across EVERY round of the call (engine-computed).\n * `per_model_verdicts` carries only the final round, so on a deliberating call\n * this is the honest total. Preferred by the telemetry aggregator when present.\n */\n readonly token_usage?: {\n readonly per_model_tokens_in: Readonly<Record<string, number>>;\n readonly per_model_tokens_out: Readonly<Record<string, number>>;\n readonly cost_micro_usd: number | null;\n readonly rounds_counted: number;\n };\n readonly degraded: boolean;\n /** Engine min_responders quorum \u2014 true when the verdict was floored to 'uncertain' below quorum. */\n readonly quorum_failed?: boolean;\n readonly duration_ms: number;\n readonly engine_version: string;\n /**\n * Phase 2 Lane D-1 \u2014 additive escalation signal from the `human-tiebreak`\n * synthesizer. Optional so the existing strict-consensus / majority-vote\n * paths stay unchanged. When present and `required: true`, surfaces a\n * human-review recommendation to the caller (e.g. `vo_architecture_review`).\n */\n readonly escalation_required?: boolean;\n readonly escalation_reason?: string;\n /**\n * Engine Feature 1 (agreement-gate) \u2014 fan-out cost accounting. Present only\n * when the agreement-gated early-exit fan-out ran for this call (opt-in via\n * `VO_CONSENSUS_AGREEMENT_GATE`). Pure telemetry; never alters the verdict.\n */\n readonly fan_out_diagnostics?: ConsensusFanOutDiagnostics;\n /**\n * Engine source-grounded Feature 1 (citation-grader) \u2014 present only on the\n * source-grounded path with the grader enabled. `ok === false` means the\n * answer's claims were not all supported by their cited sources.\n */\n readonly citation_grade?: ConsensusCitationGrade;\n /**\n * Engine source-grounded Feature 2 (retrieval-confidence) \u2014 1-based ordinals\n * of fetched sources the retrieval-confidence heuristic flagged\n * `low_confidence`. Present only on the source-grounded path. Empty array\n * means all retrievals were high-confidence.\n */\n readonly low_confidence_sources?: readonly number[];\n /** True when the source-grounded path ran (sources were provided). Absent on the plain path. */\n readonly source_grounded?: boolean;\n /**\n * Stage A7-shadow \u2014 the adaptive-vs-incumbent comparison (PII-free). Present when\n * shadow mode is enabled (default ON for VO; kill via `VO_CONSENSUS_SHADOW=0`).\n * Pure observability for grading the adaptive path; never alters the live verdict.\n */\n readonly shadow_synthesis?: ConsensusShadowSynthesis;\n /**\n * Critique-uptake visibility report \u2014 passed through from the engine so\n * callers can see whether verifier critiques changed the verdict or were\n * silently outvoted. Pure observability; never alters the live verdict.\n */\n readonly critique_uptake?: Readonly<Record<string, unknown>>;\n}\n\nexport interface ConsensusUnavailable {\n readonly ok: false;\n readonly reason: string;\n}\n\nexport type ConsensusResult = ConsensusSuccess | ConsensusUnavailable;\n\nexport interface ConsensusEngineClient {\n /** Returns `{ ok: false }` when the engine cannot run (no credentials, no panel, etc). */\n run(request: ConsensusRequest): Promise<ConsensusResult>;\n}\n", "import {\n CANCELLED_REASON,\n type ConsensusEngineClient,\n type ConsensusRequest,\n type ConsensusResult,\n} from './client.js';\n\nexport interface ConsensusFallbackOptions {\n readonly onFallback?: (reason: string) => void;\n}\n\nconst MIN_VALID_LOCAL_VERDICTS = 2;\nconst INSUFFICIENT_LOCAL_VERDICTS_REASON = 'local-panel-insufficient-valid-verdicts';\n\n/** Try the user's local panel first, then the paid cloud moat if local consensus is unavailable. */\nexport function createConsensusFallbackClient(\n primary: ConsensusEngineClient,\n fallback: ConsensusEngineClient,\n options: ConsensusFallbackOptions = {},\n): ConsensusEngineClient {\n return {\n async run(request: ConsensusRequest): Promise<ConsensusResult> {\n const primaryResult = await primary.run(request);\n if (request.signal?.aborted || (!primaryResult.ok && primaryResult.reason === CANCELLED_REASON)) {\n return primaryResult;\n }\n if (primaryResult.ok) {\n const validVerdicts = primaryResult.per_model_verdicts.filter(\n (verdict) => verdict.verdict !== 'error',\n );\n if (validVerdicts.length >= MIN_VALID_LOCAL_VERDICTS) return primaryResult;\n options.onFallback?.(INSUFFICIENT_LOCAL_VERDICTS_REASON);\n return fallback.run(request);\n }\n options.onFallback?.(primaryResult.reason);\n return fallback.run(request);\n },\n };\n}\n", "import { createRequire } from 'node:module';\n\nconst require = createRequire(import.meta.url);\nconst KEY_SERVICE = 'algosuite-vo';\n\ninterface KeyringEntry {\n getPassword(): string | null;\n}\n\ntype KeyringEntryCtor = new (service: string, account: string) => KeyringEntry;\n\n/** Keychain account `vo-mcp set-key --provider moat` writes (agent-key-store `accountFor('moat')`). */\nexport const MOAT_ENTITLEMENT_KEYCHAIN_ACCOUNT = 'moat-api-key';\n\nconst KEYCHAIN_TARGETS = [\n { account: 'anthropic-api-key', envVar: 'ANTHROPIC_API_KEY' },\n { account: 'openai-api-key', envVar: 'OPENAI_API_KEY' },\n { account: 'meta-api-key', envVar: 'MODEL_API_KEY' },\n // The cloud-consensus entitlement (ADR-002 moat). Read here so an npm-installed\n // runner \u2014 where the local engine package never exists \u2014 still has a working\n // consensus path without an operator plumbing a secret into the daemon's env.\n { account: MOAT_ENTITLEMENT_KEYCHAIN_ACCOUNT, envVar: 'VO_ENTITLEMENT_TOKEN' },\n] as const;\n\nfunction loadEntryCtor(): KeyringEntryCtor | null {\n try {\n return (require('@napi-rs/keyring') as { Entry?: KeyringEntryCtor }).Entry ?? null;\n } catch {\n return null;\n }\n}\n\nfunction readKey(EntryCtor: KeyringEntryCtor, account: string): string | null {\n try {\n return new EntryCtor(KEY_SERVICE, account).getPassword()?.trim() || null;\n } catch {\n return null;\n }\n}\n\nexport interface LocalCredentialEnvOptions {\n readonly EntryCtor?: KeyringEntryCtor | null;\n}\n\n/**\n * Hermetic opt-out: when set to '1' the OS keychain is never consulted, so a\n * spawned vo-mcp (integration tests, probes) cannot pick up the operator's real\n * provider keys or moat entitlement and make paid calls behind a test.\n */\nexport const SKIP_KEYCHAIN_ENV = 'VO_MCP_SKIP_KEYCHAIN';\n\n/**\n * Hydrate a fresh env view from credentials stored by `vo-mcp set-key`.\n * Explicit env vars always win and key material never leaves this process.\n */\nexport function withLocalConsensusCredentials(\n baseEnv: Readonly<Record<string, string | undefined>> = process.env,\n options: LocalCredentialEnvOptions = {},\n): Readonly<Record<string, string | undefined>> {\n const env = { ...baseEnv };\n if (!env.OPENAI_API_KEY?.trim() && env.CODEX_API_KEY?.trim()) {\n env.OPENAI_API_KEY = env.CODEX_API_KEY;\n }\n if (env[SKIP_KEYCHAIN_ENV]?.trim() === '1') return env;\n const EntryCtor = options.EntryCtor === undefined ? loadEntryCtor() : options.EntryCtor;\n if (!EntryCtor) return env;\n\n for (const target of KEYCHAIN_TARGETS) {\n if (env[target.envVar]?.trim()) continue;\n const key = readKey(EntryCtor, target.account);\n if (key) env[target.envVar] = key;\n }\n return env;\n}\n", "/**\n * `vo-mcp login` \u2014 the thin-client loopback browser-callback flow (Increment 3b,\n * Option A; `docs/vo/vo-command-center-inc3b-login-design-2026-06-05.md`).\n *\n * The standard CLI-auth pattern (`gcloud auth login`, `gh auth login`): start a\n * loopback HTTP server on 127.0.0.1, open the browser to the dashboard's\n * `/cli-login` page (which runs the live Firebase Google/GitHub SSO), and capture\n * the user's refresh token when the page redirects back to the loopback. The\n * captured token (the user's OWN credential \u2014 never the god-token, never model\n * keys) is stored locally; Inc 3a's source auto-refreshes ID tokens from it.\n *\n * FROZEN loopback contract v1 (the dashboard `/cli-login` page builds against this):\n * CLI \u2192 browser: <dashboard>/cli-login?port=<loopbackPort>&state=<csrf>\n * page \u2192 browser: redirect to http://127.0.0.1:<port>/callback#state=..&refresh_token=..&api_key=..&email=..\n * (data in the URL FRAGMENT \u2014 never sent to a server log; the loopback /callback\n * page reads the fragment and POSTs it same-origin to /capture as JSON.)\n * CLI /capture: validate `state`, store {refresh_token, api_key, email}, 200, close.\n */\nimport { createServer, type IncomingMessage, type ServerResponse } from 'node:http';\nimport { randomBytes } from 'node:crypto';\nimport { spawn } from 'node:child_process';\nimport { writeStoredCredential, type StoredCredential } from './credential-store.js';\n\n// The /cli-login page lives in the MAIN app (algosuite.ai) per the 2026-06-08\n// surface pivot (unification-spec \u00A711) \u2014 the standalone vo-dashboard never\n// shipped the page and its deploy lane is operator-dispatch-only. Override with\n// VO_DASHBOARD_URL (env) or opts.dashboardUrl.\nexport const DEFAULT_DASHBOARD_URL = 'https://algosuite.ai';\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst MAX_BODY_BYTES = 16_384;\n\nexport interface LoginResult {\n readonly email?: string;\n readonly credentialPath: string;\n}\n\nexport interface CaptureOutcome {\n readonly ok: boolean;\n readonly httpStatus: number;\n readonly result?: LoginResult;\n readonly error?: string;\n /**\n * The captured Firebase refresh credential (Inc 3b.4b). The caller may exchange\n * it for a scoped `vocred_` and re-store, dropping the raw refresh token.\n */\n readonly captured?: { readonly refresh_token: string; readonly api_key: string; readonly email?: string };\n}\n\n/**\n * Pure core of the /capture handler \u2014 validate the CSRF state + required fields,\n * then persist via the injected `store`. Fully unit-testable; the http server is\n * a thin shell around this.\n */\nexport function processCapture(\n rawBody: string,\n expectedState: string,\n store: (cred: StoredCredential) => string,\n): CaptureOutcome {\n let data: { state?: unknown; refresh_token?: unknown; api_key?: unknown; email?: unknown };\n try {\n data = JSON.parse(rawBody);\n } catch {\n return { ok: false, httpStatus: 400, error: 'invalid JSON body' };\n }\n if (!data || typeof data !== 'object') return { ok: false, httpStatus: 400, error: 'invalid body' };\n // CSRF: the state echoed by the browser MUST match the one we generated.\n if (typeof data.state !== 'string' || data.state !== expectedState) {\n return { ok: false, httpStatus: 403, error: 'state mismatch (possible CSRF) \u2014 login aborted' };\n }\n const refresh = typeof data.refresh_token === 'string' ? data.refresh_token.trim() : '';\n const apiKey = typeof data.api_key === 'string' ? data.api_key.trim() : '';\n if (!refresh || !apiKey) {\n return { ok: false, httpStatus: 400, error: 'login response missing refresh_token / api_key' };\n }\n const email = typeof data.email === 'string' && data.email.trim() ? data.email.trim() : undefined;\n const path = store({ refresh_token: refresh, api_key: apiKey, ...(email ? { email } : {}) });\n return {\n ok: true,\n httpStatus: 200,\n result: { ...(email ? { email } : {}), credentialPath: path },\n captured: { refresh_token: refresh, api_key: apiKey, ...(email ? { email } : {}) },\n };\n}\n\n/** The tiny page served at /callback: read the fragment, POST it same-origin to /capture. */\nfunction captureHtml(): string {\n return `<!doctype html><html><head><meta charset=\"utf-8\"><title>AlgoHQ login</title></head>\n<body style=\"font-family:system-ui;max-width:32rem;margin:4rem auto;text-align:center\">\n<h2 id=\"m\">Completing sign-in\u2026</h2>\n<script>\n(function(){\n var h=location.hash.replace(/^#/,''), p=new URLSearchParams(h), b={};\n ['state','refresh_token','api_key','email'].forEach(function(k){ if(p.get(k)) b[k]=p.get(k); });\n fetch('/capture',{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify(b)})\n .then(function(r){ document.getElementById('m').textContent = r.ok ? 'Sign-in complete \u2014 you can close this tab.' : 'Sign-in failed \u2014 check the terminal.'; })\n .catch(function(){ document.getElementById('m').textContent = 'Sign-in failed \u2014 check the terminal.'; });\n})();\n</script></body></html>`;\n}\n\n/** Cross-platform browser open (best-effort; the URL is also printed for manual open). */\nexport function defaultOpenBrowser(url: string): void {\n const platform = process.platform;\n if (platform === 'win32') {\n // NOT `cmd /c start`: cmd re-parses the joined command line, so an\n // unquoted `&` truncates the URL at the first query separator \u2014 the\n // browser opened with `?port=` but no `state`, and the CLI login page\n // correctly rejected the pair (observed live, 2026-07-21). rundll32's\n // FileProtocolHandler passes the URL to the default browser with no\n // shell parsing, so `&` survives intact.\n spawn('rundll32', ['url.dll,FileProtocolHandler', url], { detached: true, stdio: 'ignore' }).unref();\n } else if (platform === 'darwin') {\n spawn('open', [url], { detached: true, stdio: 'ignore' }).unref();\n } else {\n spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref();\n }\n}\n\nexport interface RunLoginOptions {\n readonly dashboardUrl?: string;\n readonly timeoutMs?: number;\n readonly env?: Readonly<Record<string, string | undefined>>;\n readonly openBrowser?: (url: string) => void;\n readonly log?: (msg: string) => void;\n readonly nowIso?: () => string;\n /**\n * Inc 3b.4b: exchange the captured Firebase refresh credential for a scoped\n * `vocred_`. When provided AND it succeeds, the vocred_ is stored INSTEAD of the\n * raw refresh token (spec \u00A76). Fail-open: a null/throwing exchange keeps the\n * refresh credential. Production wires `exchangeForVoCredential` when a\n * `VO_CONTROL_PLANE_URL` is configured.\n */\n readonly exchange?: (\n refreshToken: string,\n apiKey: string,\n label?: string,\n ) => Promise<{ vo_credential: string; expires_at: string } | null>;\n}\n\n/**\n * Run the interactive loopback login. Resolves with the stored credential path on\n * success; rejects on timeout / CSRF mismatch / malformed response. FAIL-CLOSED:\n * nothing is stored unless the state matches and the fields are present.\n */\nexport async function runLogin(opts: RunLoginOptions = {}): Promise<LoginResult> {\n const env = opts.env ?? process.env;\n const dashboardUrl = (opts.dashboardUrl ?? env['VO_DASHBOARD_URL']?.trim() ?? DEFAULT_DASHBOARD_URL)\n .replace(/\\/+$/, '') || DEFAULT_DASHBOARD_URL;\n const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const log = opts.log ?? ((m: string) => console.error(m));\n const nowIso = opts.nowIso ?? (() => new Date().toISOString());\n const openBrowser = opts.openBrowser ?? defaultOpenBrowser;\n const state = randomBytes(32).toString('base64url');\n\n return new Promise<LoginResult>((resolve, reject) => {\n let settled = false;\n const finish = (err: Error | null, result?: LoginResult): void => {\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n server.close();\n if (err) reject(err);\n else resolve(result as LoginResult);\n };\n\n const server = createServer((req: IncomingMessage, res: ServerResponse) => {\n const url = new URL(req.url ?? '/', 'http://127.0.0.1');\n if (req.method === 'GET' && url.pathname === '/callback') {\n res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });\n res.end(captureHtml());\n return;\n }\n if (req.method === 'POST' && url.pathname === '/capture') {\n let body = '';\n req.on('data', (chunk: Buffer) => {\n body += chunk.toString('utf8');\n if (body.length > MAX_BODY_BYTES) req.destroy();\n });\n req.on('end', () => {\n void (async () => {\n // Guard against a dribble that crossed the cap before destroy() took effect.\n if (body.length > MAX_BODY_BYTES) {\n res.writeHead(413, { 'content-type': 'text/plain' });\n res.end('payload too large');\n finish(new Error('login request body exceeded the size cap'));\n return;\n }\n // Inc 3b.4b: when an exchange is configured, VALIDATE without writing the\n // raw refresh token first \u2014 then write exactly ONE credential (the\n // vocred_ on success, else the refresh on fail-open). This avoids a\n // window where the raw Firebase refresh token persists on disk during\n // the exchange round-trip (\u00A76: the raw token must not persist at all).\n const writeNow = (cred: StoredCredential): string => writeStoredCredential(cred, nowIso(), env);\n const outcome = processCapture(body, state, opts.exchange ? () => 'pending' : writeNow);\n let result = outcome.result;\n if (outcome.ok && outcome.captured && opts.exchange) {\n const capt = outcome.captured;\n let cred: StoredCredential = {\n refresh_token: capt.refresh_token,\n api_key: capt.api_key,\n ...(capt.email ? { email: capt.email } : {}),\n };\n try {\n const voc = await opts.exchange(capt.refresh_token, capt.api_key);\n if (voc && voc.vo_credential) {\n // Store ONLY the vocred_ \u2014 the raw refresh token never lands on disk.\n cred = {\n vo_credential: voc.vo_credential,\n vo_credential_expires_at: voc.expires_at,\n ...(capt.email ? { email: capt.email } : {}),\n };\n }\n } catch {\n /* keep the refresh credential \u2014 fail-open */\n }\n const path = writeNow(cred);\n result = { ...(capt.email ? { email: capt.email } : {}), credentialPath: path };\n }\n res.writeHead(outcome.httpStatus, { 'content-type': 'text/html; charset=utf-8' });\n res.end(outcome.ok ? '<h2>AlgoHQ login complete \u2014 you can close this tab.</h2>' : `<h2>Login failed: ${outcome.error}</h2>`);\n finish(outcome.ok ? null : new Error(outcome.error ?? 'login failed'), result);\n })();\n });\n return;\n }\n res.writeHead(404);\n res.end('not found');\n });\n\n const timer = setTimeout(\n () => finish(new Error(`login timed out after ${timeoutMs}ms \u2014 no sign-in captured`)),\n timeoutMs,\n );\n\n server.on('error', (e: Error) => finish(e));\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address();\n const port = addr && typeof addr === 'object' ? addr.port : 0;\n if (!port) {\n finish(new Error('failed to bind a loopback port'));\n return;\n }\n const loginUrl = `${dashboardUrl}/cli-login?port=${port}&state=${encodeURIComponent(state)}`;\n log(`[vo-mcp] Opening your browser to sign in:\\n ${loginUrl}`);\n log('[vo-mcp] If it did not open, paste that URL into your browser. Waiting for sign-in\u2026');\n try {\n openBrowser(loginUrl);\n } catch {\n /* user opens manually */\n }\n });\n });\n}\n", "/**\n * Increment 3b.4b \u2014 exchange a captured Firebase refresh credential for a scoped,\n * revocable VO credential. The thin client mints ONCE via the control-plane\n * `POST /api/v1/auth/vo-credential` (authenticated with a fresh Firebase ID token\n * derived from the refresh token), then presents the `vocred_` instead of the raw\n * Firebase token thereafter \u2014 so the raw refresh token need not persist on disk\n * (spec \u00A76: kill the local secret).\n *\n * FAIL-OPEN to the refresh credential: ANY failure (an older control-plane without\n * the mint endpoint, a network error, a non-operator principal, a malformed\n * response) returns `null`, and the caller keeps using the Firebase refresh flow.\n * Never throws.\n */\nimport { createFirebaseRefreshTokenSource } from './auth-token-source.js';\n\n/** Minimal fetch shape (matches `auth-token-source`'s injectable fetch). */\ntype ExchangeFetch = (\n url: string,\n init?: { method?: string; headers?: Record<string, string>; body?: string },\n) => Promise<{ status: number; text: () => Promise<string> }>;\n\nexport interface VoCredentialResult {\n readonly vo_credential: string;\n readonly expires_at: string;\n}\n\nexport interface ExchangeOptions {\n readonly refreshToken: string;\n readonly apiKey: string;\n /** Control-plane base URL (e.g. `https://vo-control-plane-\u2026run.app`). */\n readonly controlPlaneUrl: string;\n /** Operator-friendly label stored with the credential (e.g. host + client). */\n readonly label?: string;\n /** Injectable fetch (tests); defaults to the global `fetch`. */\n readonly fetchFn?: ExchangeFetch;\n}\n\n/**\n * Mint a `vocred_` from a Firebase refresh credential. Returns the credential on\n * success, else `null` (fail-open). Never throws.\n */\nexport async function exchangeForVoCredential(opts: ExchangeOptions): Promise<VoCredentialResult | null> {\n const base = opts.controlPlaneUrl.replace(/\\/+$/, '');\n if (!base) return null;\n const fetchFn: ExchangeFetch = opts.fetchFn ?? (globalThis.fetch as unknown as ExchangeFetch);\n try {\n // 1. Derive a fresh Firebase ID token from the refresh credential (Inc 3a flow).\n const idToken = await createFirebaseRefreshTokenSource({\n refreshToken: opts.refreshToken,\n apiKey: opts.apiKey,\n ...(opts.fetchFn ? { fetchFn: opts.fetchFn } : {}),\n }).getToken();\n if (!idToken) return null;\n\n // 2. Mint the scoped credential with the ID token.\n const res = await fetchFn(`${base}/api/v1/auth/vo-credential`, {\n method: 'POST',\n headers: { 'content-type': 'application/json', authorization: `Bearer ${idToken}` },\n body: JSON.stringify(opts.label ? { label: opts.label } : {}),\n });\n if (res.status !== 200) return null;\n const parsed = JSON.parse(await res.text()) as { token?: unknown; expires_at?: unknown };\n const token = typeof parsed.token === 'string' ? parsed.token : '';\n const expiresAt = typeof parsed.expires_at === 'string' ? parsed.expires_at : '';\n if (!token.startsWith('vocred_') || !expiresAt) return null;\n return { vo_credential: token, expires_at: expiresAt };\n } catch {\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["z", "existsSync", "readFileSync", "join", "path", "path", "MAX_EXCERPT", "readdirSync", "readFileSync", "statSync", "homedir", "dirname", "join", "createHash", "readFileSync", "dirname", "join", "fileURLToPath", "homedir", "join", "dirname", "existsSync", "mkdirSync", "readFileSync", "writeFileSync", "chmodSync", "statSync", "readFileSync", "path", "closeSync", "mkdirSync", "openSync", "readFileSync", "unlinkSync", "writeFileSync", "join", "randomUUID", "createExclusive", "path", "resolve", "createHash", "readFileSync", "writeFileSync", "join", "existsSync", "mkdirSync", "readFileSync", "writeFileSync", "readdirSync", "existsSync", "readFileSync", "homedir", "join", "readFile", "existsSync", "readdirSync", "readFileSync", "description", "TOOL_NAME", "description", "inputSchema", "existsSync", "homedir", "join", "isToolInput", "createAuthTokenSourceFromEnv", "readStoredCredential", "result", "upsertMemoryFilesAsKnowledge", "homedir", "hostname", "randomUUID", "join", "randomUUID", "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", "openSync", "readFileSync", "readdirSync", "statSync", "mkdirSync", "readFileSync", "writeFileSync", "homedir", "join", "join", "homedir", "path", "writeFileSync", "readFileSync", "mkdirSync", "TOOL_NAME", "inputSchema", "description", "isToolInput", "join", "homedir", "readdirSync", "statSync", "resolved", "existsSync", "readFileSync", "mkdirSync", "openSync", "TOOL_NAME", "CALLABLE_NAME", "ADMIN_PATH", "inputSchema", "description", "isToolInput", "createAuthTokenSourceFromEnv", "readStoredCredential", "path", "existsSync", "statSync", "dirname", "join", "resolve", "readdirSync", "readFileSync", "statSync", "join", "description", "readdirSync", "join", "statSync", "readFileSync", "resolve", "existsSync", "statSync", "join", "dirname", "TOOL_NAME", "description", "inputSchema", "randomUUID", "createHash", "chmodSync", "mkdirSync", "dirname", "mkdirSync", "dirname", "chmodSync", "createHash", "clip", "buildSummary", "randomUUID", "callMetaWithMetrics", "randomUUID", "randomUUID", "createRequire", "require", "createServer", "spawn", "path", "resolve", "join", "homedir", "hostname", "randomUUID", "appendFileSync", "mkdirSync", "runMemorySync", "isNoopSyncReason"]
|
|
7
7
|
}
|