nexus-agents 8.131.1 → 8.131.3
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/{chunk-NEB2FSBW.js → chunk-5XMXBSPY.js} +3 -3
- package/dist/{chunk-2B55IL7S.js → chunk-AAE7F4CB.js} +7 -4
- package/dist/chunk-AAE7F4CB.js.map +1 -0
- package/dist/{chunk-QGXA3QXW.js → chunk-CGKENW3F.js} +93 -75
- package/dist/chunk-CGKENW3F.js.map +1 -0
- package/dist/{chunk-DFAT7RB7.js → chunk-FB4ZIOYS.js} +11 -12
- package/dist/chunk-FB4ZIOYS.js.map +1 -0
- package/dist/{chunk-ETJY6TMK.js → chunk-RWWPMTJK.js} +2 -2
- package/dist/{chunk-45AUQYWF.js → chunk-UETCXCAS.js} +193 -144
- package/dist/{chunk-45AUQYWF.js.map → chunk-UETCXCAS.js.map} +1 -1
- package/dist/cli.js +7 -7
- package/dist/cli.js.map +1 -1
- package/dist/{consensus-vote-K4SNLVKO.js → consensus-vote-6ISHSQ73.js} +4 -4
- package/dist/{improvement-review-UYDDYXIB.js → improvement-review-S65JP2NY.js} +4 -4
- package/dist/index.d.ts +3 -26
- package/dist/index.js +6 -6
- package/dist/{setup-data-dir-IYJGONN3.js → setup-data-dir-EVVWRHY2.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-2B55IL7S.js.map +0 -1
- package/dist/chunk-DFAT7RB7.js.map +0 -1
- package/dist/chunk-QGXA3QXW.js.map +0 -1
- /package/dist/{chunk-NEB2FSBW.js.map → chunk-5XMXBSPY.js.map} +0 -0
- /package/dist/{chunk-ETJY6TMK.js.map → chunk-RWWPMTJK.js.map} +0 -0
- /package/dist/{consensus-vote-K4SNLVKO.js.map → consensus-vote-6ISHSQ73.js.map} +0 -0
- /package/dist/{improvement-review-UYDDYXIB.js.map → improvement-review-S65JP2NY.js.map} +0 -0
- /package/dist/{setup-data-dir-IYJGONN3.js.map → setup-data-dir-EVVWRHY2.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mcp/middleware/validation.ts","../src/mcp/middleware/rate-limiter.ts","../src/mcp/middleware/policy-types.ts","../src/mcp/middleware/policy-helpers.ts","../src/mcp/middleware/policy-rules.ts","../src/mcp/middleware/policy.ts","../src/audit/secure-handler-audit.ts","../src/mcp/tools/tool-result.ts","../src/mcp/error-envelope.ts","../src/mcp/middleware/secure-handler-rate-limit.ts","../src/mcp/middleware/secure-handler-tier.ts","../src/mcp/middleware/tool-input-sanitizer.ts","../src/mcp/middleware/policy-registry.ts","../src/mcp/middleware/would-deny-sampler.ts","../src/mcp/middleware/policy-audit-emit.ts","../src/mcp/middleware/policy-check.ts","../src/mcp/middleware/install-state.ts","../src/mcp/middleware/secure-handler.ts","../src/mcp/mcp-notifier.ts","../src/mcp/middleware/tool-wrapper.ts","../src/mcp/middleware/timeout-guard.ts","../src/mcp/middleware/middleware-chain.ts","../src/mcp/tools/tool-annotations.ts","../src/mcp/tool-annotations.ts","../src/consensus/decision/thresholds.ts","../src/consensus/types-core.ts","../src/consensus/decision/quorum.ts"],"sourcesContent":["/**\n * nexus-agents/mcp - Validation Middleware\n *\n * Input validation helper using Zod schemas.\n * All tool inputs must be validated at the boundary.\n *\n * (Source: MCP Protocol 2025-11-25, Zod Documentation)\n */\n\nimport type { ZodType } from 'zod';\n\nimport { type Result, ok, err, ValidationError, formatZodError } from '../../core/index.js';\n\n// Re-export isZodError for backward compatibility\nexport { isZodError } from '../../core/index.js';\n\n/**\n * Validates tool input against a Zod schema.\n *\n * This function should be called at the start of every tool handler\n * to validate incoming arguments before processing.\n *\n * @template T - The expected type after validation\n * @param schema - The Zod schema to validate against\n * @param args - The unknown input to validate\n * @returns Result containing validated data or a ValidationError\n *\n * @example\n * ```typescript\n * const InputSchema = z.object({\n * task: z.string().min(1),\n * context: z.record(z.string(), z.unknown()).optional(),\n * });\n *\n * server.tool('my_tool', InputSchema.shape, async (args) => {\n * const result = validateToolInput(InputSchema, args);\n * if (!result.ok) {\n * return toolStructuredError({ errorCategory: 'validation', message: result.error.message });\n * }\n * const { task, context } = result.value;\n * // Process validated input...\n * });\n * ```\n */\nexport function validateToolInput<T>(\n schema: ZodType<T>,\n args: unknown\n): Result<T, ValidationError> {\n const parsed = schema.safeParse(args);\n\n if (parsed.success) {\n return ok(parsed.data);\n }\n\n const message = formatZodError(parsed.error);\n const validationError = new ValidationError(`Invalid tool input: ${message}`, {\n context: {\n issues: parsed.error.issues,\n receivedType: typeof args,\n },\n });\n\n return err(validationError);\n}\n\n/**\n * Creates a validation function bound to a specific schema.\n *\n * Useful for reusing the same schema across multiple tools.\n *\n * @template T - The expected type after validation\n * @param schema - The Zod schema to bind\n * @returns A validation function for the schema\n *\n * @example\n * ```typescript\n * const validateTask = createValidator(TaskSchema);\n *\n * // Later in tool handlers:\n * const result = validateTask(args);\n * ```\n */\nexport function createValidator<T>(\n schema: ZodType<T>\n): (args: unknown) => Result<T, ValidationError> {\n return (args: unknown) => validateToolInput(schema, args);\n}\n\n// `validateToolOutput` and `createOutputValidator` (Issue #547 sibling of\n// `validateToolInput` / `createValidator`) removed in #3022 — no MCP tool\n// ever called the output-validation path; every tool returns its result\n// without schema-validating first. If output validation comes back as a\n// real requirement, reintroduce alongside the per-tool wiring in the same\n// PR (activate-or-delete YAGNI — #2937, #2938, #2939, #2940, #3018, #3022).\n","/**\n * nexus-agents/mcp - Rate Limiter Middleware\n *\n * Token bucket implementation for rate limiting MCP tool calls.\n * Prevents abuse and ensures fair resource usage.\n *\n * (Source: Token Bucket Algorithm, RFC 6585)\n */\n\nimport { createLogger, type ILogger, getTimeProvider } from '../../core/index.js';\n\n/**\n * Configuration for the token bucket rate limiter.\n */\nexport interface RateLimiterConfig {\n /** Maximum number of tokens in the bucket */\n readonly capacity: number;\n /** Number of tokens added per interval */\n readonly refillRate: number;\n /** Interval in milliseconds between token refills (default: 1000ms) */\n readonly refillIntervalMs?: number;\n /** Optional logger instance */\n readonly logger?: ILogger;\n /** Optional identifier for logging */\n readonly name?: string;\n}\n\n/**\n * Current state of the rate limiter.\n */\nexport interface RateLimiterState {\n /** Current number of available tokens */\n readonly tokens: number;\n /** Capacity of the bucket */\n readonly capacity: number;\n /** Time until next token is available (0 if tokens available) */\n readonly nextTokenMs: number;\n}\n\n// Canonical source: config/timeouts.ts (Issue #1046)\nimport { CACHE_TIMEOUTS } from '../../config/timeouts.js';\n\nconst DEFAULT_REFILL_INTERVAL_MS = CACHE_TIMEOUTS.rateLimitRefillMs;\n\n/**\n * Token bucket rate limiter implementation.\n *\n * The token bucket algorithm allows for bursting up to the capacity,\n * while maintaining a steady-state rate equal to the refill rate.\n *\n * @example\n * ```typescript\n * const limiter = new RateLimiter({\n * capacity: 100,\n * refillRate: 10,\n * refillIntervalMs: 1000,\n * });\n *\n * if (limiter.tryAcquire()) {\n * // Proceed with operation\n * } else {\n * // Rate limited, reject or queue\n * }\n * ```\n */\nexport class RateLimiter {\n private tokens: number;\n private readonly capacity: number;\n private readonly refillRate: number;\n private readonly refillIntervalMs: number;\n private lastRefillTime: number;\n private readonly logger: ILogger;\n private readonly name: string;\n\n constructor(config: RateLimiterConfig) {\n this.capacity = config.capacity;\n this.refillRate = config.refillRate;\n this.refillIntervalMs = config.refillIntervalMs ?? DEFAULT_REFILL_INTERVAL_MS;\n this.tokens = this.capacity;\n this.lastRefillTime = getTimeProvider().now();\n this.name = config.name ?? 'rate-limiter';\n this.logger = config.logger ?? createLogger({ component: this.name });\n\n this.logger.debug('Rate limiter initialized', {\n capacity: this.capacity,\n refillRate: this.refillRate,\n refillIntervalMs: this.refillIntervalMs,\n });\n }\n\n /**\n * Refills tokens based on elapsed time.\n * Called automatically before each acquire attempt.\n */\n private refill(): void {\n const now = getTimeProvider().now();\n const elapsed = now - this.lastRefillTime;\n const intervals = Math.floor(elapsed / this.refillIntervalMs);\n\n if (intervals > 0) {\n const tokensToAdd = intervals * this.refillRate;\n this.tokens = Math.min(this.capacity, this.tokens + tokensToAdd);\n this.lastRefillTime = now - (elapsed % this.refillIntervalMs);\n\n if (tokensToAdd > 0) {\n this.logger.debug('Tokens refilled', {\n added: tokensToAdd,\n current: this.tokens,\n });\n }\n }\n }\n\n /**\n * Attempts to acquire a token.\n *\n * @param count - Number of tokens to acquire (default: 1)\n * @returns True if tokens were acquired, false if rate limited\n */\n tryAcquire(count = 1): boolean {\n this.refill();\n\n if (this.tokens >= count) {\n this.tokens -= count;\n this.logger.debug('Token acquired', {\n requested: count,\n remaining: this.tokens,\n });\n return true;\n }\n\n this.logger.warn('Rate limit exceeded', {\n requested: count,\n available: this.tokens,\n });\n return false;\n }\n\n /**\n * Gets the current state of the rate limiter.\n *\n * @returns The current rate limiter state\n */\n getState(): RateLimiterState {\n this.refill();\n\n const nextTokenMs =\n this.tokens > 0 ? 0 : this.refillIntervalMs - (getTimeProvider().now() - this.lastRefillTime);\n\n return {\n tokens: this.tokens,\n capacity: this.capacity,\n nextTokenMs: Math.max(0, nextTokenMs),\n };\n }\n\n /**\n * Resets the rate limiter to full capacity.\n * Useful for testing or after configuration changes.\n */\n reset(): void {\n this.tokens = this.capacity;\n this.lastRefillTime = getTimeProvider().now();\n this.logger.debug('Rate limiter reset', { tokens: this.tokens });\n }\n}\n\n/**\n * Creates a rate limiter with default settings suitable for MCP tools.\n *\n * Default configuration:\n * - Capacity: 100 tokens\n * - Refill rate: 10 tokens per second\n *\n * @param name - Optional name for the rate limiter\n * @param logger - Optional logger instance\n * @returns A configured RateLimiter instance\n */\nexport function createDefaultRateLimiter(name?: string, logger?: ILogger): RateLimiter {\n const config: RateLimiterConfig = {\n capacity: 100,\n refillRate: 10,\n refillIntervalMs: 1000,\n };\n if (name !== undefined) {\n (config as { name?: string }).name = name;\n }\n if (logger !== undefined) {\n (config as { logger?: ILogger }).logger = logger;\n }\n return new RateLimiter(config);\n}\n","/**\n * nexus-agents/mcp - Policy Firewall Types\n *\n * Type definitions for the authorization layer of MCP tool calls.\n *\n * (Source: OWASP ASVS 4.0, Authorization Controls)\n */\n\nimport { z } from 'zod';\n\nimport { SecurityError, type ILogger } from '../../core/index.js';\nimport { DEFAULT_EXECUTION_MODE } from '../../config/schemas-security.js';\n\n// =============================================================================\n// Core Types\n// =============================================================================\n\n/**\n * Artifact type for policy context.\n * Artifacts are resources that can be referenced in policy decisions.\n */\nexport interface Artifact<T = unknown> {\n readonly id: string;\n readonly type: string;\n readonly value: T;\n readonly createdAt: Date;\n}\n\n/**\n * Execution mode for tool operations.\n * - 'read-only': Only read operations allowed — the operator's opt-in lock\n * - 'read-write': Both read and write operations allowed (default since #6431;\n * `DEFAULT_EXECUTION_MODE` in config/schemas-security.ts)\n */\nexport type ExecutionMode = 'read-only' | 'read-write';\n\n/**\n * Policy enforcement mode.\n * - 'enforce': Block denied operations\n * - 'warn': Log denials but allow execution (for migration)\n */\nexport type PolicyMode = 'enforce' | 'warn';\n\n/**\n * Result of a policy evaluation.\n */\nexport interface PolicyDecision {\n readonly allowed: boolean;\n readonly reason: string;\n readonly requiredArtifact?: string;\n readonly ruleName?: string;\n /**\n * A rule denied this call and warn mode overrode the denial, so `allowed` is\n * `true` but the operation WOULD have been blocked in enforce mode (#4991).\n *\n * Set explicitly by the evaluator rather than inferred downstream. The first\n * implementation deduced it from `allowed === true && ruleName !== undefined`\n * — true of today's code, because `allowWithReason` never sets `ruleName` —\n * and a consensus panel rejected that: naming which rule *permitted* an\n * action (`admin-override` vs `default-allow`) is ordinary access-control\n * practice, so the day an allow rule sets `ruleName`, every authorized call\n * it covers would be silently recorded as a near-miss. Deriving a verdict\n * from the absence of an unrelated field is not a signal, it is a\n * coincidence.\n */\n readonly overriddenByWarnMode?: boolean;\n}\n\n/**\n * Context provided to policy rules for evaluation.\n */\nexport interface PolicyContext {\n readonly toolName: string;\n readonly args: unknown;\n readonly mode: ExecutionMode;\n readonly artifacts?: Map<string, Artifact>;\n readonly workflowId?: string;\n readonly allowedPaths?: readonly string[];\n}\n\n/**\n * A single policy rule that can approve or deny operations.\n */\nexport interface PolicyRule {\n readonly name: string;\n readonly description: string;\n check(ctx: PolicyContext): PolicyDecision;\n}\n\n/**\n * Interface for the policy firewall.\n */\nexport interface IPolicyFirewall {\n evaluate(ctx: PolicyContext): PolicyDecision;\n addRule(rule: PolicyRule): void;\n removeRule(name: string): boolean;\n getRules(): readonly PolicyRule[];\n setMode(mode: PolicyMode): void;\n getMode(): PolicyMode;\n}\n\n/**\n * Configuration for the policy firewall.\n */\nexport interface PolicyFirewallConfig {\n /** Enforcement mode (default: 'enforce') */\n readonly mode?: PolicyMode;\n /** Logger instance */\n readonly logger?: ILogger;\n /** Initial rules to register */\n readonly rules?: readonly PolicyRule[];\n}\n\n// =============================================================================\n// Error Types\n// =============================================================================\n\n/**\n * Policy error for authorization failures.\n */\nexport class PolicyError extends SecurityError {\n readonly decision: PolicyDecision;\n\n constructor(message: string, decision: PolicyDecision) {\n super(message, {\n context: {\n allowed: decision.allowed,\n reason: decision.reason,\n ruleName: decision.ruleName,\n requiredArtifact: decision.requiredArtifact,\n },\n });\n this.name = 'PolicyError';\n this.decision = decision;\n }\n}\n\n// =============================================================================\n// Zod Schemas for Configuration\n// =============================================================================\n\n/**\n * Schema for policy configuration. Same default as\n * `config/schemas-security.ts` — one constant, not two (#6431).\n */\nexport const PolicyConfigSchema = z.object({\n defaultMode: z.enum(['read-only', 'read-write']).default(DEFAULT_EXECUTION_MODE),\n policyMode: z.enum(['enforce', 'warn']).default('enforce'),\n allowedPaths: z.array(z.string()).default(['./']),\n});\n\nexport type PolicyConfig = z.infer<typeof PolicyConfigSchema>;\n","/**\n * nexus-agents/mcp - Policy Firewall Helpers\n *\n * Utility functions for path validation and argument extraction.\n *\n * (Source: OWASP ASVS 4.0, Authorization Controls)\n */\n\nimport { realpathSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { resolve, sep } from 'node:path';\nimport { resolveInsideRoot } from '../../security/safe-path.js';\n\n// =============================================================================\n// Path Utility Functions\n// =============================================================================\n\n/**\n * Validates a path against allowed roots.\n *\n * @param targetPath - The path to validate\n * @param allowedPaths - Array of allowed root paths\n * @returns True if the path is within an allowed root\n */\nexport function isPathSafe(targetPath: string, allowedPaths: readonly string[]): boolean {\n // #5025: this used `normalizePath`, which collapses the DEFAULT allowlist\n // entry `'./'` to `'/'` — so `startsWith` was true for every absolute path\n // and the rule admitted `/etc/shadow` and `~/.ssh/id_ed25519`. A raw string\n // prefix also has no separator boundary, so a root of `/work` admitted\n // `/work-secrets`. Resolve both sides against cwd and require either an\n // exact match or a path-separator boundary. The containment check follows\n // symlinks, so a link inside a root whose target is outside it is refused.\n const resolvedTarget = resolve(targetPath);\n return allowedPaths.some((allowed) => resolveInsideRoot(resolvedTarget, allowed) !== null);\n}\n\n// Common path field names in priority order\nconst KNOWN_PATH_FIELDS: readonly string[] = [\n 'path',\n 'filePath',\n 'file_path',\n 'targetPath',\n 'target_path',\n 'targetFile',\n 'target_file',\n 'sourcePath',\n 'source_path',\n 'sourceFile',\n 'source_file',\n 'destinationPath',\n 'destination_path',\n 'destPath',\n 'dest_path',\n 'destFile',\n 'dest_file',\n 'outputPath',\n 'output_path',\n 'outputFile',\n 'output_file',\n 'outputDir',\n 'output_dir',\n 'inputPath',\n 'input_path',\n 'inputFile',\n 'input_file',\n 'inputDir',\n 'input_dir',\n 'planFile',\n 'plan_file',\n 'specFile',\n 'spec_file',\n 'feedAPath',\n 'feedBPath',\n 'projectDir',\n 'project_dir',\n 'workingDir',\n 'working_dir',\n 'workingDirectory',\n 'working_directory',\n 'baseDir',\n 'base_dir',\n 'rootDir',\n 'root_dir',\n 'relPath',\n 'rel_path',\n 'directory',\n 'dir',\n 'folder',\n 'file',\n 'filename',\n 'fileName',\n 'target',\n 'paths',\n 'files',\n 'targetFiles',\n];\n\n/** Non-filesystem keys that end with path/file/dir but represent data models or other concepts. */\nconst NON_FS_PATH_KEYS = new Set([\n 'keyPath',\n 'key_path',\n 'urlPath',\n 'url_path',\n 'jsonPath',\n 'json_path',\n 'xpath',\n 'actionPath',\n 'action_path',\n]);\n\n/** Pattern matching argument keys that indicate filesystem paths. */\nconst PATH_KEY_PATTERN = /(?:[pP]ath|[fF]ile|[dD]ir(?:ectory)?)$/;\n\nfunction appendPathsFromValue(value: unknown, result: string[], seen: Set<string>): void {\n if (typeof value === 'string' && value.length > 0) {\n if (!seen.has(value)) {\n seen.add(value);\n result.push(value);\n }\n } else if (Array.isArray(value)) {\n for (const item of value) {\n if (typeof item === 'string' && item.length > 0 && !seen.has(item)) {\n seen.add(item);\n result.push(item);\n }\n }\n }\n}\n\n/**\n * Extracts all filesystem paths from tool arguments.\n *\n * Checks known path field names in priority order as well as any argument\n * keys matching path/file/directory naming conventions.\n */\nexport function extractPathsFromArgs(args: unknown): string[] {\n if (args === null || typeof args !== 'object') {\n return [];\n }\n\n const argsObj = args as Record<string, unknown>;\n const result: string[] = [];\n const seen = new Set<string>();\n\n // Check known fields in priority order first\n for (const field of KNOWN_PATH_FIELDS) {\n if (field in argsObj) {\n appendPathsFromValue(argsObj[field], result, seen);\n }\n }\n\n // Also discover any additional path-like keys on the argument object\n for (const key of Object.keys(argsObj)) {\n if (!NON_FS_PATH_KEYS.has(key) && PATH_KEY_PATTERN.test(key)) {\n appendPathsFromValue(argsObj[key], result, seen);\n }\n }\n\n return result;\n}\n\n/**\n * Extracts path from tool arguments if present.\n * Returns the first extracted path in priority order, or undefined.\n */\nexport function extractPathFromArgs(args: unknown): string | undefined {\n const paths = extractPathsFromArgs(args);\n return paths.length > 0 ? paths[0] : undefined;\n}\n\n// =============================================================================\n// Secret paths (#5108)\n// =============================================================================\n\n/**\n * File-path globs that are denied regardless of `allowedPaths`. Moved\n * verbatim from the access-constraint deriver's `UNBYPASSABLE_PATH_PATTERNS`\n * (#5108, panel option B): there they sat behind `checkAccess`, which had no\n * production caller, so none of them ever gated a real tool call.\n *\n * Glob-style: `**` spans path segments, `*` stays within one, `~/` is a\n * home-anchor prefix. Matching is case-insensitive to catch `~/.SSH`.\n */\nconst SECRET_PATH_PATTERNS: readonly string[] = [\n // Environment files\n '.env',\n '.env.*',\n '**/.env',\n '**/.env.*',\n\n // SSH credentials\n '~/.ssh/**',\n '**/ssh/id_*',\n '**/*_rsa',\n '**/*_ed25519',\n '**/*.pem',\n\n // Cloud credentials\n '~/.aws/**',\n '~/.azure/**',\n '~/.gcp/**',\n '~/.config/gcloud/**',\n '~/.kube/config',\n\n // Unix secret files\n '/etc/shadow',\n '/etc/sudoers',\n '/etc/sudoers.d/**',\n\n // Common secret file patterns\n '**/secrets.*',\n '**/credentials.*',\n '**/private_key.*',\n '**/id_rsa*',\n];\n\n/**\n * Compiles one secret-path glob to an anchored regex at module load.\n *\n * `~/` and bare relative globs anchor at a segment boundary `(^|/)`, so\n * `~/.ssh/**` names ANY `.ssh/` directory, not only the current user's —\n * the deriver's semantics, kept on purpose: broader is the fail-closed side.\n * Absolute globs anchor at `^`. Inputs are this module's constants, never\n * user text, so the regexes are ReDoS-safe by construction.\n */\nfunction compileSecretGlob(pattern: string): RegExp {\n const escaped = pattern\n .toLowerCase()\n .replace(/[\\\\.+^$()|[\\]{}]/g, '\\\\$&')\n .replace(/\\*\\*/g, '__DOUBLESTAR__')\n .replace(/\\*/g, '[^/]*')\n .replace(/__DOUBLESTAR__/g, '.*');\n if (escaped.startsWith('~/')) return new RegExp(`(^|/)${escaped.slice(2)}$`);\n if (escaped.startsWith('/')) return new RegExp(`^${escaped}$`);\n return new RegExp(`(^|/)${escaped}$`);\n}\n\nconst COMPILED_SECRET_PATTERNS: ReadonlyArray<{\n readonly pattern: string;\n readonly regex: RegExp;\n}> = SECRET_PATH_PATTERNS.map((pattern) => ({ pattern, regex: compileSecretGlob(pattern) }));\n\n/**\n * Canonicalizes a tool-argument path before it is matched (#5108, contrarian\n * amendment): `~` → the home directory, `path.resolve` against cwd (collapses\n * `..`), then `realpath` when the file exists so a symlink is judged by where\n * it points. A missing file cannot be realpath'd; the resolved spelling is\n * matched instead — \"cannot canonicalize\" is never read as \"allow\".\n */\nexport function canonicalizeToolPath(raw: string): string {\n const expanded =\n raw === '~' ? homedir() : raw.startsWith('~/') ? homedir() + sep + raw.slice(2) : raw;\n const resolved = resolve(expanded);\n try {\n return realpathSync(resolved);\n } catch {\n return resolved;\n }\n}\n\n/**\n * The first secret-path glob a CANONICAL path matches, or `undefined`.\n * Callers canonicalize first ({@link canonicalizeToolPath}); matching a raw\n * spelling here is the defect the #5108 amendment exists to prevent.\n */\nexport function findSecretPathPattern(canonicalPath: string): string | undefined {\n const lowered = canonicalPath.toLowerCase();\n return COMPILED_SECRET_PATTERNS.find((c) => c.regex.test(lowered))?.pattern;\n}\n","/**\n * nexus-agents/mcp - Policy Firewall Rules\n *\n * Default policy rules and constants for the authorization layer.\n *\n * (Source: OWASP ASVS 4.0, Authorization Controls)\n */\n\nimport type { PolicyContext, PolicyDecision, PolicyRule } from './policy-types.js';\nimport {\n isPathSafe,\n extractPathsFromArgs,\n canonicalizeToolPath,\n findSecretPathPattern,\n} from './policy-helpers.js';\nimport { classifyRegisteredTool, type ToolExecutionClass } from '../tools/tool-manifest.js';\n\n// =============================================================================\n// Tool Classification Constants\n// =============================================================================\n\n/**\n * GENERIC agent/filesystem tool names that are write/mutation operations.\n *\n * Not nexus tools: a registered tool is classified by its manifest entry's\n * `readOnlyHint` (#5114), and a test keeps this set disjoint from the manifest\n * so no name has two answers. These names exist for callers that evaluate the\n * firewall against tools this server does not register (proxied or upstream).\n */\nexport const MUTATION_TOOLS = new Set([\n 'write_file',\n 'edit_file',\n 'delete_file',\n 'create_directory',\n 'remove_directory',\n 'execute_command',\n 'run_shell',\n 'bash',\n]);\n\n/**\n * GENERIC agent/filesystem tool names that are read-only operations. Same\n * scope rule as {@link MUTATION_TOOLS}: never a registered nexus tool.\n */\nexport const READ_ONLY_TOOLS = new Set([\n 'read_file',\n 'list_directory',\n 'search_files',\n 'get_status',\n]);\n\n// =============================================================================\n// Tool Classification Functions\n// =============================================================================\n\n/**\n * Classifies a tool for the mutation rule: the manifest answers for every\n * registered tool, the generic sets answer for the handful of foreign names,\n * and anything else is reported as `unclassified` rather than guessed (#5114).\n */\nfunction classifyToolExecution(toolName: string): ToolExecutionClass {\n const registered = classifyRegisteredTool(toolName);\n if (registered !== 'unclassified') return registered;\n if (MUTATION_TOOLS.has(toolName)) return 'mutation';\n if (READ_ONLY_TOOLS.has(toolName)) return 'read-only';\n return 'unclassified';\n}\n\n/**\n * Checks if a tool is a mutation operation. Fail-closed boolean view of\n * {@link classifyToolExecution}: an unclassified tool counts as a mutation.\n * The rule itself uses the three-way class so its verdict can say which.\n */\nexport function isMutationTool(toolName: string): boolean {\n return classifyToolExecution(toolName) !== 'read-only';\n}\n\n// =============================================================================\n// Default Policy Rules\n// =============================================================================\n\n/**\n * Policy rule that denies mutation operations when mode is 'read-only'.\n *\n * This ensures that write operations are only allowed when explicitly\n * enabled via the 'read-write' mode. Two inputs: `ctx.mode` is the permission\n * the operator granted; the tool's class comes from the manifest (#5114). An\n * unclassified tool is denied too, but the verdict SAYS it was unclassified —\n * a rollout needs to tell \"a write the mode forbids\" from \"nobody classified\n * this\", and a boolean cannot.\n */\nexport const denyMutationsWithoutModeRule: PolicyRule = {\n name: 'deny-mutations-without-mode',\n description: 'Blocks write operations unless mode is read-write',\n check(ctx: PolicyContext): PolicyDecision {\n // If mode is read-write, allow all operations\n if (ctx.mode === 'read-write') {\n return { allowed: true, reason: 'Read-write mode enabled' };\n }\n\n switch (classifyToolExecution(ctx.toolName)) {\n case 'mutation':\n return {\n allowed: false,\n reason: `Tool '${ctx.toolName}' is a mutation operation but mode is '${ctx.mode}'. Set mode to 'read-write' to enable.`,\n };\n case 'unclassified':\n return {\n allowed: false,\n reason: `Tool '${ctx.toolName}' is unclassified (no TOOL_MANIFEST readOnlyHint and not a known generic tool); denied fail-closed because mode is '${ctx.mode}'. Classify it in tool-manifest.ts or set mode to 'read-write'.`,\n };\n case 'read-only':\n return { allowed: true, reason: 'Read-only operation allowed' };\n }\n },\n};\n\n/**\n * Policy rule that validates paths against allowed roots.\n *\n * Prevents path traversal attacks by ensuring all file operations\n * target paths within configured allowed directories.\n */\nexport const safePathsRule: PolicyRule = {\n name: 'safe-paths',\n description: 'Validates paths against allowed root directories',\n check(ctx: PolicyContext): PolicyDecision {\n // Extract paths from arguments\n const targetPaths = extractPathsFromArgs(ctx.args);\n\n // If no path in args, allow (not a file operation)\n if (targetPaths.length === 0) {\n return { allowed: true, reason: 'No path argument found' };\n }\n\n const allowedPaths = ctx.allowedPaths ?? ['./'];\n\n for (const targetPath of targetPaths) {\n // Check for obvious path traversal attempts\n if (targetPath.includes('..')) {\n return {\n allowed: false,\n reason: `Path contains '..' which may indicate path traversal: ${targetPath}`,\n };\n }\n\n // Validate path is within allowed roots\n if (!isPathSafe(targetPath, allowedPaths)) {\n return {\n allowed: false,\n reason: `Path '${targetPath}' is outside allowed directories: ${allowedPaths.join(', ')}`,\n };\n }\n }\n\n return { allowed: true, reason: 'Path is within allowed directories' };\n },\n};\n\n// =============================================================================\n// Secret paths (#5108)\n// =============================================================================\n\n/**\n * Policy rule that denies access to secret-bearing paths (SSH keys, cloud\n * credentials, `.env`, `/etc/shadow`, …) whatever `allowedPaths` says.\n *\n * Composes AND-deny with {@link safePathsRule}: that rule is containment\n * against the allowlist, this one is a denylist inside it. A caller who widens\n * `allowedPaths` to `$HOME` keeps `~/.ssh` closed, and `.env` inside the repo\n * root is refused even though it passes containment. No path argument → the\n * rule abstains (an allow with that reason), because absence of a path is not\n * a file operation and must not be recorded as \"no secret\".\n */\nexport const secretPathsRule: PolicyRule = {\n name: 'secret-paths',\n description: 'Denies access to secret-bearing paths regardless of allowed roots',\n check(ctx: PolicyContext): PolicyDecision {\n const targetPaths = extractPathsFromArgs(ctx.args);\n if (targetPaths.length === 0) {\n return { allowed: true, reason: 'No path argument found' };\n }\n\n for (const targetPath of targetPaths) {\n const canonical = canonicalizeToolPath(targetPath);\n const hit = findSecretPathPattern(canonical);\n if (hit !== undefined) {\n return {\n allowed: false,\n reason: `Path '${canonical}' matches secret-path pattern '${hit}'`,\n };\n }\n }\n\n return { allowed: true, reason: 'Path matches no secret-path pattern' };\n },\n};\n","/**\n * nexus-agents/mcp - Policy Firewall Middleware\n *\n * Authorization layer for MCP tool calls. Evaluates policy rules\n * to determine whether operations should be allowed or denied.\n *\n * This is separate from validation - validation checks if input is well-formed,\n * policy checks if the operation is authorized.\n *\n * (Source: OWASP ASVS 4.0, Authorization Controls)\n */\n\nimport { createLogger, type ILogger, type Result, ok, err } from '../../core/index.js';\n\nimport type {\n Artifact,\n ExecutionMode,\n PolicyMode,\n PolicyDecision,\n PolicyContext,\n PolicyRule,\n IPolicyFirewall,\n PolicyFirewallConfig,\n} from './policy-types.js';\nimport { PolicyError } from './policy-types.js';\nimport { denyMutationsWithoutModeRule, safePathsRule, secretPathsRule } from './policy-rules.js';\n\n// =============================================================================\n// PolicyFirewall Implementation\n// =============================================================================\n\n/**\n * Policy firewall that evaluates rules to authorize or deny operations.\n *\n * Rules are evaluated in order. The first rule that denies the operation\n * stops evaluation and returns the denial. If all rules pass, the operation\n * is allowed.\n *\n * @example\n * ```typescript\n * const firewall = new PolicyFirewall({ mode: 'enforce' });\n *\n * // Add rules\n * firewall.addRule(denyMutationsWithoutModeRule);\n * firewall.addRule(safePathsRule);\n *\n * // Evaluate\n * const decision = firewall.evaluate({\n * toolName: 'write_file',\n * args: { path: '/etc/passwd' },\n * mode: 'read-only',\n * });\n *\n * if (!decision.allowed) {\n * console.error(`Denied: ${decision.reason}`);\n * }\n * ```\n */\nexport class PolicyFirewall implements IPolicyFirewall {\n private readonly rules: PolicyRule[] = [];\n private mode: PolicyMode;\n private readonly logger: ILogger;\n\n constructor(config?: PolicyFirewallConfig) {\n this.mode = config?.mode ?? 'enforce';\n this.logger = config?.logger ?? createLogger({ component: 'policy-firewall' });\n\n // Register initial rules if provided\n if (config?.rules) {\n for (const rule of config.rules) {\n this.rules.push(rule);\n }\n }\n\n this.logger.debug('Policy firewall initialized', {\n mode: this.mode,\n ruleCount: this.rules.length,\n });\n }\n\n /**\n * Evaluates all policy rules against the given context.\n *\n * Rules are evaluated in order. The first rule that denies stops\n * evaluation and returns the denial decision.\n *\n * @param ctx - The policy context to evaluate\n * @returns The policy decision\n */\n evaluate(ctx: PolicyContext): PolicyDecision {\n this.logger.debug('Evaluating policy', {\n toolName: ctx.toolName,\n mode: ctx.mode,\n ruleCount: this.rules.length,\n });\n\n // If no rules, allow by default\n if (this.rules.length === 0) {\n return this.allowWithReason(ctx, 'No policy rules configured');\n }\n\n // Evaluate each rule in order\n for (const rule of this.rules) {\n const decision = rule.check(ctx);\n\n if (!decision.allowed) {\n return this.handleDenial(ctx, rule, decision);\n }\n }\n\n // All rules passed\n return this.allowWithReason(ctx, 'All policy rules passed');\n }\n\n /**\n * Creates an allow decision with the given reason and logs it.\n */\n private allowWithReason(ctx: PolicyContext, reason: string): PolicyDecision {\n const decision: PolicyDecision = { allowed: true, reason };\n this.logDecision(ctx, decision);\n return decision;\n }\n\n /**\n * Handles a rule denial, respecting warn mode if configured.\n */\n private handleDenial(\n ctx: PolicyContext,\n rule: PolicyRule,\n decision: PolicyDecision\n ): PolicyDecision {\n const denialDecision: PolicyDecision = {\n ...decision,\n ruleName: rule.name,\n };\n\n this.logDecision(ctx, denialDecision);\n\n // In warn mode, log but still allow\n if (this.mode === 'warn') {\n this.logger.warn('Policy denial overridden by warn mode', {\n toolName: ctx.toolName,\n ruleName: rule.name,\n reason: decision.reason,\n });\n return {\n allowed: true,\n reason: `[WARN MODE] Would be denied: ${decision.reason}`,\n ruleName: rule.name,\n // The evaluator is the only place that KNOWS this was a denial the mode\n // overrode; saying so explicitly is what lets the audit path record it\n // without inferring the fact from other fields (#4991).\n overriddenByWarnMode: true,\n };\n }\n\n return denialDecision;\n }\n\n /**\n * Adds a policy rule to the firewall.\n *\n * @param rule - The rule to add\n */\n addRule(rule: PolicyRule): void {\n // Prevent duplicate rules\n const existingIndex = this.rules.findIndex((r) => r.name === rule.name);\n if (existingIndex >= 0) {\n this.logger.warn('Replacing existing policy rule', { ruleName: rule.name });\n this.rules[existingIndex] = rule;\n } else {\n this.rules.push(rule);\n this.logger.debug('Policy rule added', { ruleName: rule.name });\n }\n }\n\n /**\n * Removes a policy rule by name.\n *\n * @param name - The name of the rule to remove\n * @returns True if the rule was found and removed\n */\n removeRule(name: string): boolean {\n const index = this.rules.findIndex((r) => r.name === name);\n if (index >= 0) {\n this.rules.splice(index, 1);\n this.logger.debug('Policy rule removed', { ruleName: name });\n return true;\n }\n return false;\n }\n\n /**\n * Gets all registered policy rules.\n *\n * @returns A readonly array of policy rules\n */\n getRules(): readonly PolicyRule[] {\n return [...this.rules];\n }\n\n /**\n * Sets the policy enforcement mode.\n *\n * @param mode - The new enforcement mode\n */\n setMode(mode: PolicyMode): void {\n const previousMode = this.mode;\n this.mode = mode;\n this.logger.info('Policy mode changed', { from: previousMode, to: mode });\n }\n\n /**\n * Gets the current policy enforcement mode.\n *\n * @returns The current mode\n */\n getMode(): PolicyMode {\n return this.mode;\n }\n\n /**\n * Logs a policy decision for audit purposes.\n */\n private logDecision(ctx: PolicyContext, decision: PolicyDecision): void {\n const logData = {\n toolName: ctx.toolName,\n mode: ctx.mode,\n workflowId: ctx.workflowId,\n allowed: decision.allowed,\n reason: decision.reason,\n ruleName: decision.ruleName,\n };\n\n if (decision.allowed) {\n this.logger.debug('Policy decision: ALLOWED', logData);\n } else {\n this.logger.warn('Policy decision: DENIED', logData);\n }\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Creates a policy firewall with default rules.\n *\n * Default rules included, in evaluation order:\n * - deny-mutations-without-mode\n * - secret-paths (#5108) — ahead of safe-paths so a secret is reported as a\n * secret, not as a `..` or an out-of-root path\n * - safe-paths\n *\n * @param config - Optional configuration\n * @returns A configured PolicyFirewall instance\n */\nexport function createDefaultPolicyFirewall(config?: PolicyFirewallConfig): PolicyFirewall {\n const firewall = new PolicyFirewall(config);\n\n // Add default rules\n firewall.addRule(denyMutationsWithoutModeRule);\n firewall.addRule(secretPathsRule);\n firewall.addRule(safePathsRule);\n\n return firewall;\n}\n\n/**\n * Evaluates a policy context and returns a Result.\n *\n * This is a convenience function that wraps the firewall evaluation\n * in a Result type for easier error handling.\n *\n * @param firewall - The policy firewall to use\n * @param ctx - The policy context to evaluate\n * @returns Result containing void on success or PolicyError on denial\n */\nexport function evaluatePolicy(\n firewall: IPolicyFirewall,\n ctx: PolicyContext\n): Result<void, PolicyError> {\n const decision = firewall.evaluate(ctx);\n\n if (decision.allowed) {\n return ok(undefined);\n }\n\n return err(new PolicyError(`Policy denied: ${decision.reason}`, decision));\n}\n\n/**\n * Creates a policy context from tool invocation parameters.\n *\n * @param toolName - Name of the tool being invoked\n * @param args - Tool arguments\n * @param options - Additional context options\n * @returns A PolicyContext object\n */\n/**\n * The mode a context built with no granted mode is evaluated under.\n *\n * Deliberately NOT `DEFAULT_EXECUTION_MODE` (`read-write`, #6431): that is the\n * OPERATOR'S default, carried to every secure handler by the policy registry.\n * A caller that reaches this helper without saying which mode it was granted\n * has not been granted one, and a rule input that is absent must fail closed.\n */\nconst FAIL_CLOSED_MODE: ExecutionMode = 'read-only';\n\nexport function createPolicyContext(\n toolName: string,\n args: unknown,\n options?: {\n mode?: ExecutionMode;\n artifacts?: Map<string, Artifact>;\n workflowId?: string;\n allowedPaths?: readonly string[];\n }\n): PolicyContext {\n // Build base context with required properties\n const base = {\n toolName,\n args,\n mode: options?.mode ?? FAIL_CLOSED_MODE,\n };\n\n // Use Object.assign to build result, only adding optional properties\n // when they are actually defined (to satisfy exactOptionalPropertyTypes)\n const result: Record<string, unknown> = { ...base };\n\n if (options?.artifacts !== undefined) {\n result['artifacts'] = options.artifacts;\n }\n if (options?.workflowId !== undefined) {\n result['workflowId'] = options.workflowId;\n }\n if (options?.allowedPaths !== undefined) {\n result['allowedPaths'] = options.allowedPaths;\n }\n\n return result as unknown as PolicyContext;\n}\n\n// =============================================================================\n// Re-exports for backward compatibility\n// =============================================================================\n\nexport * from './policy-types.js';\nexport * from './policy-rules.js';\nexport * from './policy-helpers.js';\n","/**\n * nexus-agents/audit - SecureHandler Audit Integration\n *\n * Integration helper to add audit logging to SecureHandler middleware.\n *\n * (Source: Issue #193 - Phase 3 structured audit logging)\n *\n * @module audit/secure-handler-audit\n */\n\nimport type { IAuditLogger, AuditActor, AuditOutcome } from './audit-types.js';\nimport type { RequestContext } from '../mcp/middleware/request-context.js';\n\n/**\n * Configuration for audit-enabled secure handler.\n */\nexport interface AuditHandlerConfig {\n /** Audit logger instance */\n auditLogger: IAuditLogger;\n /** Default actor for requests without caller info */\n defaultActor?: AuditActor | undefined;\n}\n\n/**\n * Creates an AuditActor from RequestContext.\n */\nexport function actorFromContext(ctx: RequestContext, fallback?: AuditActor): AuditActor {\n const caller = ctx.caller;\n const clientId = caller.clientId;\n if (clientId !== undefined && clientId.length > 0) {\n return {\n type: clientId.includes('api') ? 'external' : clientId.includes('cli') ? 'agent' : 'user',\n id: clientId,\n name: caller.userAgent,\n };\n }\n return fallback ?? { type: 'system', id: 'unknown', name: 'Unknown Caller' };\n}\n\n/**\n * Maps tool result to audit outcome.\n */\nexport function resultToOutcome(\n isError: boolean | undefined,\n isPolicyDenied: boolean\n): AuditOutcome {\n if (isPolicyDenied) return 'denied';\n if (isError === true) return 'failure';\n return 'success';\n}\n\n/** Options for logging tool invocation audit */\nexport interface LogToolInvocationOpts {\n auditLogger: IAuditLogger;\n toolName: string;\n outcome: AuditOutcome;\n actor: AuditActor;\n requestId: string;\n durationMs?: number | undefined;\n errorMessage?: string | undefined;\n}\n\n/**\n * Logs tool invocation to audit logger.\n */\nexport function logToolInvocationAudit(opts: LogToolInvocationOpts): void {\n opts.auditLogger.logToolInvocation({\n toolName: opts.toolName,\n outcome: opts.outcome,\n actor: opts.actor,\n requestId: opts.requestId,\n durationMs: opts.durationMs,\n errorMessage: opts.errorMessage,\n });\n}\n\n/** Options for logging policy audit */\nexport interface LogPolicyAuditOpts {\n auditLogger: IAuditLogger;\n policyName: string;\n decision: 'allow' | 'deny';\n reason: string;\n toolName: string;\n actor: AuditActor;\n requestId: string;\n}\n\n/**\n * Logs policy decision to audit logger.\n */\nexport function logPolicyAudit(opts: LogPolicyAuditOpts): void {\n opts.auditLogger.logPolicyDecision({\n policyName: opts.policyName,\n decision: opts.decision,\n reason: opts.reason,\n toolName: opts.toolName,\n actor: opts.actor,\n requestId: opts.requestId,\n });\n}\n\n/** Options for logging rate limit audit */\nexport interface LogRateLimitAuditOpts {\n auditLogger: IAuditLogger;\n toolName: string;\n actor: AuditActor;\n currentRate: number;\n limitRate: number;\n requestId: string;\n}\n\n/**\n * Logs rate limit violation to audit logger.\n */\nexport function logRateLimitAudit(opts: LogRateLimitAuditOpts): void {\n opts.auditLogger.logRateLimitViolation({\n toolName: opts.toolName,\n actor: opts.actor,\n currentRate: opts.currentRate,\n limitRate: opts.limitRate,\n requestId: opts.requestId,\n });\n}\n","/**\n * nexus-agents/mcp - Tool Result Helpers\n *\n * Canonical type and factory functions for MCP tool results.\n * Extracted from index.ts to allow tool implementations to import\n * without circular dependencies.\n *\n * @module mcp/tools/tool-result\n */\n\nimport { z } from 'zod';\nimport type {\n McpServer,\n RegisteredTool,\n ToolCallback,\n} from '@modelcontextprotocol/sdk/server/mcp.js';\nimport type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport type { ILogger } from '../../core/index.js';\nimport type { RateLimiter } from '../middleware/rate-limiter.js';\nimport type { SecurityConfig } from '../../config/schemas.js';\nimport {\n defaultRetryable,\n ERROR_ENVELOPE_META_KEY,\n type ErrorCategory,\n type ToolErrorEnvelope,\n} from '../error-envelope.js';\n\n// ============================================================================\n// Base Dependencies\n// ============================================================================\n\n/**\n * Common dependency interface shared by all MCP tool handlers.\n *\n * Tool-specific deps interfaces should extend this base.\n * (Source: Issue #1439 — DRY extraction of 25 duplicated Deps interfaces)\n */\nexport interface BaseMcpToolDeps {\n /** Optional logger */\n logger?: ILogger;\n /** Rate limiter for throttling tool calls (required) */\n rateLimiter: RateLimiter;\n /** Security configuration (includes timeout settings) */\n security?: SecurityConfig | undefined;\n}\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * MCP tool content types.\n */\nexport interface TextContent {\n type: 'text';\n text: string;\n}\n\n/**\n * MCP tool result.\n *\n * Uses mutable properties for compatibility with secure-handler\n * sanitization (which rewrites `text` in-place).\n */\nexport interface ToolResult {\n content: Array<TextContent>;\n isError?: boolean;\n /** Structured output for SDK outputSchema validation (Issue #1117) */\n structuredContent?: Record<string, unknown>;\n /**\n * Out-of-band metadata, never validated against `outputSchema`. The\n * structured error envelope (#2649) is carried here under\n * `ERROR_ENVELOPE_META_KEY`.\n */\n _meta?: Record<string, unknown>;\n}\n\n// ============================================================================\n// Factory Functions\n// ============================================================================\n\n/**\n * Creates a successful tool result.\n *\n * @param text - The result text\n * @returns A ToolResult with the text content\n *\n * @example\n * ```typescript\n * return toolSuccess(JSON.stringify({ status: 'ok', data: result }));\n * ```\n */\nexport function toolSuccess(text: string): ToolResult {\n return {\n content: [{ type: 'text', text }],\n };\n}\n\n/** Structured success data is serialized without mutating readonly domain responses. */\ntype ReadonlyOutput<T> = T extends readonly (infer Item)[]\n ? readonly ReadonlyOutput<Item>[]\n : T extends object\n ? { readonly [Key in keyof T]: ReadonlyOutput<T[Key]> }\n : T;\n\n/**\n * Creates a successful tool result with structured content for outputSchema validation.\n *\n * When a tool is registered with outputSchema, the SDK validates structuredContent\n * against the schema. This helper returns both text (for display) and structured data.\n *\n * @param _schema - The tool's declared output schema\n * @param data - The structured result data inferred from the schema\n * @returns A ToolResult with both text content and structuredContent\n *\n * @example\n * ```typescript\n * return toolSuccessStructured(z.object({ count: z.number() }), { count: 10 });\n * ```\n */\n\nexport function structuredToolSuccess<\n Schema extends z.ZodType<Record<string, unknown>>,\n Data extends ReadonlyOutput<z.output<NoInfer<Schema>>>,\n>(\n _schema: Schema,\n data: Data & Record<Exclude<keyof Data, keyof z.output<Schema>>, never>\n): ToolResult {\n // The schema binds the data type; the SDK performs runtime validation.\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n structuredContent: data,\n };\n}\n\n/**\n * Untyped structured success, kept with its original signature for external\n * callers of the published API. In-tree tools use {@link structuredToolSuccess},\n * which binds the data to the declared output schema (#7042).\n */\nexport function toolSuccessStructured(data: Record<string, unknown>): ToolResult {\n return {\n content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],\n structuredContent: data,\n };\n}\n\n/**\n * Input for {@link toolStructuredError}. `isRetryable` is optional — when\n * omitted it is derived from the category via `defaultRetryable()`.\n */\nexport interface ToolStructuredErrorInput {\n errorCategory: ErrorCategory;\n message: string;\n isRetryable?: boolean;\n detail?: Record<string, unknown>;\n}\n\n/**\n * Creates a structured error tool result (#2649). The envelope is carried\n * in `_meta` (under `ERROR_ENVELOPE_META_KEY`) — NOT `structuredContent`,\n * which the MCP client validates against the tool's `outputSchema` even\n * on error results. `message` is mirrored into `content[].text` for\n * display.\n *\n * @example\n * ```typescript\n * if (!validated.success) {\n * return toolStructuredError({\n * errorCategory: 'validation',\n * message: `Validation error: ${formatZodError(validated.error)}`,\n * });\n * }\n * ```\n */\nexport function toolStructuredError(input: ToolStructuredErrorInput): ToolResult {\n const envelope: ToolErrorEnvelope = {\n errorCategory: input.errorCategory,\n isRetryable: input.isRetryable ?? defaultRetryable(input.errorCategory),\n message: input.message,\n ...(input.detail !== undefined ? { detail: input.detail } : {}),\n };\n return {\n isError: true,\n content: [{ type: 'text', text: envelope.message }],\n _meta: { [ERROR_ENVELOPE_META_KEY]: envelope },\n };\n}\n\n/**\n * Creates an error tool result.\n *\n * Back-compat alias for {@link toolStructuredError} — maps to the\n * conservative `internal` / non-retryable envelope. New code should call\n * `toolStructuredError` directly with the correct category; this alias\n * exists so the ~64 legacy call sites keep working during the #2649\n * migration sweep.\n *\n * @param message - The error message\n * @returns A ToolResult with isError set to true and an `internal` envelope\n */\nexport function toolError(message: string): ToolResult {\n return toolStructuredError({ errorCategory: 'internal', message });\n}\n\n/** Registers a declared output shape with strict SDK server-side validation (#7042). */\nexport function registerStructuredTool(\n server: McpServer,\n name: string,\n config: {\n description: string;\n inputSchema: z.ZodRawShape;\n outputSchema: z.ZodRawShape;\n annotations?: ToolAnnotations;\n },\n callback: ToolCallback<z.ZodRawShape>\n): RegisteredTool {\n return server.registerTool(\n name,\n { ...config, outputSchema: z.strictObject(config.outputSchema) },\n callback\n );\n}\n","/**\n * nexus-agents/mcp - Structured Tool Error Envelope\n *\n * Caller-facing error contract for MCP tools. Replaces the opaque\n * `{ isError: true, content: [{ text }] }` string shape with a structured\n * envelope so callers (other tools, voter panels, the Claude/Codex/Gemini/\n * OpenCode harnesses) can reason about retry-safety and recovery path\n * instead of string-matching arbitrary text.\n *\n * SCOPE — this envelope is caller-facing ONLY. The routing/circuit-breaker\n * layer classifies adapter subprocess failures through its own\n * `categorizeOutcomeError()` path (orchestration/outcomes/outcome-types.ts)\n * and never reads this envelope. `coarsenFailureCategory()` is a one-way\n * convenience for the rare tool that internally catches an\n * `OutcomeFailureCategory`-classified error and wants to surface it to its\n * caller — it is not, and must not become, a routing input. The two\n * taxonomies serve different layers; this is the single authoritative\n * projection between them.\n *\n * @module mcp/error-envelope\n * @see Issue #2649\n */\n\nimport { z } from 'zod';\nimport type { OutcomeFailureCategory } from '../orchestration/outcomes/outcome-types.js';\n\n// ============================================================================\n// Schema\n// ============================================================================\n\n/**\n * Caller-facing error category. Deliberately coarser than the routing\n * layer's 11-value `OutcomeFailureCategory` — a tool's caller only needs\n * enough resolution to choose a recovery path:\n *\n * - `transient` — network blip, rate limit, timeout. Retry is safe.\n * - `validation` — input shape/values wrong. Caller must fix its args.\n * - `permission` — auth / authorization / sandbox / access-policy denial.\n * - `business` — domain-logic refusal (dedup hit, precondition not met).\n * An expected, non-bug outcome — not a failure to retry.\n * - `internal` — unexpected, bug-class. Not retry-class; escalate.\n */\nexport const ErrorCategorySchema = z.enum([\n 'transient',\n 'validation',\n 'permission',\n 'business',\n 'internal',\n]);\n\nexport type ErrorCategory = z.infer<typeof ErrorCategorySchema>;\n\n/**\n * Structured error envelope returned by MCP tools. Carried in the tool\n * result's `structuredContent` under the `error` key; the human-readable\n * `message` is also mirrored into `content[].text` for display.\n */\nexport const ToolErrorEnvelopeSchema = z.object({\n errorCategory: ErrorCategorySchema,\n /** Whether retrying the same call could succeed without caller changes. */\n isRetryable: z.boolean(),\n /** Human-readable summary. Bounded to keep stack traces out of results. */\n message: z.string().min(1).max(2000),\n /**\n * Optional structured context. MUST NOT carry secrets, credentials,\n * absolute filesystem paths, or raw Error/response objects — those are\n * an information-disclosure risk (this field is not output-sanitized).\n */\n detail: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport type ToolErrorEnvelope = z.infer<typeof ToolErrorEnvelopeSchema>;\n\n/**\n * `_meta` key the envelope is carried under on a tool result. It lives in\n * `_meta` — NOT `structuredContent` — because the MCP client validates\n * `structuredContent` against the tool's `outputSchema` even on error\n * results (SDK `client/index.js`: it only guards on presence, not on\n * `isError`), so an envelope in `structuredContent` breaks every tool\n * that has an `outputSchema`. `_meta` is the spec's out-of-band metadata\n * channel and is never schema-validated. Namespaced to avoid collisions.\n */\nexport const ERROR_ENVELOPE_META_KEY = 'nexus-agents/error';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Default retry-safety for a category. Only `transient` errors are\n * retry-safe by default; callers can override per-call when a specific\n * `internal` or `business` error is known to be retryable.\n */\nexport function defaultRetryable(category: ErrorCategory): boolean {\n return category === 'transient';\n}\n\n/**\n * The single authoritative projection from the routing layer's 11-value\n * `OutcomeFailureCategory` down to the 5-value caller-facing\n * `ErrorCategory`. A `Record` over every key — adding a 12th\n * `OutcomeFailureCategory` value without extending this map is a compile\n * error, which is the drift safeguard.\n *\n * One-way only: there is no `un-coarsen`, by design — the routing layer\n * keeps its own granular classification and never round-trips through here.\n */\nconst FAILURE_CATEGORY_COARSENING: Record<OutcomeFailureCategory, ErrorCategory> = {\n timeout: 'transient',\n rate_limit: 'transient',\n connection: 'transient',\n authentication: 'permission',\n validation: 'validation',\n parse: 'validation',\n crash: 'internal',\n adapter_unavailable: 'internal',\n execution: 'internal',\n generic: 'internal',\n unknown: 'internal',\n};\n\n/**\n * Coarsen a routing-layer `OutcomeFailureCategory` to a caller-facing\n * `ErrorCategory`. Use this only when a tool has caught an error already\n * classified by the routing layer and wants to surface it in its envelope.\n */\nexport function coarsenFailureCategory(category: OutcomeFailureCategory): ErrorCategory {\n return FAILURE_CATEGORY_COARSENING[category];\n}\n\n/**\n * Extract and validate a `ToolErrorEnvelope` from a tool result's `_meta`\n * object. Returns `null` when `_meta` is absent or does not carry a\n * parseable envelope under {@link ERROR_ENVELOPE_META_KEY}. Used by\n * envelope-aware callers.\n */\nexport function parseToolErrorEnvelope(meta: unknown): ToolErrorEnvelope | null {\n if (meta === null || typeof meta !== 'object') {\n return null;\n }\n const candidate = (meta as Record<string, unknown>)[ERROR_ENVELOPE_META_KEY];\n const result = ToolErrorEnvelopeSchema.safeParse(candidate);\n return result.success ? result.data : null;\n}\n","/**\n * Rate-limit denial: the error returned to the caller, and the audit record\n * that says what limit was hit.\n *\n * Split out of `secure-handler.ts` when #5577 pushed that file past its\n * 400-line cap. The two belong together: the audit record's numbers are only\n * honest if they come from the same limiter read that produced the denial.\n */\nimport type { ILogger } from '../../core/index.js';\nimport type { RateLimiter, RateLimiterState } from './rate-limiter.js';\nimport type { IAuditLogger } from '../../audit/audit-types.js';\nimport { actorFromContext } from '../../audit/secure-handler-audit.js';\nimport type { RequestContext } from './request-context.js';\nimport { toolStructuredError, type ToolResult } from '../tools/tool-result.js';\n\n/**\n * Creates a rate limit error response. Rate limits are transient — the\n * structured envelope marks it retryable (#2649).\n */\nfunction rateLimitError(nextTokenMs: number): ToolResult {\n return toolStructuredError({\n errorCategory: 'transient',\n message: `Rate limit exceeded. Try again in ${String(nextTokenMs)}ms.`,\n });\n}\n\n/**\n * The denial, together with the limiter state that produced it.\n *\n * The state travels with the result rather than being re-read at the audit\n * call: `getState()` refills first, so a second read can report a bucket that\n * has recovered a token since the denial, and the audit record would then\n * describe a limiter that would have allowed the call (#5577).\n */\nexport interface RateLimitDenial {\n readonly error: ToolResult;\n readonly state: RateLimiterState;\n}\n\n/** Acquires a token; on refusal returns the denial and the state behind it. */\nexport function checkRateLimit(rateLimiter: RateLimiter, logger: ILogger): RateLimitDenial | null {\n const acquired = rateLimiter.tryAcquire();\n if (!acquired) {\n const state = rateLimiter.getState();\n logger.warn('Rate limit exceeded');\n return { error: rateLimitError(state.nextTokenMs), state };\n }\n return null;\n}\n\n/** Emits an audit event for a rate limit violation. */\nexport function emitRateLimitAudit(\n auditLogger: IAuditLogger,\n toolName: string,\n ctx: RequestContext,\n state: RateLimiterState\n): void {\n const actor = actorFromContext(ctx);\n auditLogger.logRateLimitViolation({\n toolName,\n actor,\n // The bucket is empty at denial time, so the tokens spent out of it are\n // `capacity - tokens`. Both numbers are measured from the limiter that\n // denied this call; they were hard-coded to 0/0 before #5577, which made\n // every durable record read \"Rate limit exceeded: 0/0 requests\".\n currentRate: Math.round(state.capacity - state.tokens),\n limitRate: state.capacity,\n requestId: ctx.requestId,\n });\n}\n","/**\n * Security-tier refusal: the error returned to the caller, and the audit record\n * that says an injection attempt was refused.\n *\n * Split out of `secure-handler.ts` alongside `secure-handler-rate-limit.ts`,\n * and for the same reason: the refusal and the record of it belong together, so\n * the record cannot be written from a second, later read of the evidence.\n *\n * ## Why the record exists at all\n *\n * The tier branch used to return before both the rate limiter and\n * `executeAndAudit`, so a refused prompt-injection attempt against a\n * `user-facing` / `external` tool produced ZERO audit-chain events while an\n * ordinary successful call produced one. The hostile traffic was the only\n * traffic invisible to the chain — a reviewer reading the audit log saw a quiet\n * period, not an attack. `logSecurityEvent` had been declared and implemented\n * since #193 with no production caller; this is it.\n *\n * ## Why the rate limiter still runs AFTER this check\n *\n * The obvious follow-on — meter the refusal so probing is not free — is wrong\n * here. `rateLimiterFactory.getForTool(name)` hands every caller of a tool the\n * SAME token bucket, so charging refused input would let one sender empty a\n * tool's bucket with malformed arguments and deny it to everyone else. Refusal\n * is cheap (regex over already-parsed arguments, no model call), so the traffic\n * it admits is bounded anyway. The record above is the control; the limiter is\n * not.\n */\nimport type { ILogger } from '../../core/index.js';\nimport type { IAuditLogger } from '../../audit/audit-types.js';\nimport { actorFromContext } from '../../audit/secure-handler-audit.js';\nimport type { RequestContext } from './request-context.js';\nimport type { SanitizeToolInputResult } from './tool-input-sanitizer.js';\nimport { toolStructuredError, type ToolResult } from '../tools/tool-result.js';\n\n/**\n * Security tier for MCP tools. Controls input validation strictness.\n *\n * - 'standard': Default. XML injection tag stripping only (existing behavior).\n * - 'user-facing': Accepts user task descriptions. Rejects known injection patterns.\n * - 'external': Processes external URLs/content. Strictest validation.\n *\n * @see Issue #1586 — Tiered security validation\n */\nexport type SecurityTier = 'standard' | 'user-facing' | 'external';\n\n/**\n * A refusal, together with what triggered it.\n *\n * `eventType` and `patterns` travel with the error rather than being recomputed\n * at the audit call, so the durable record names the same evidence the caller\n * was refused on.\n */\nexport interface SecurityTierRefusal {\n readonly error: ToolResult;\n /** Audit `eventType`; also the `violationType` on the chained record. */\n readonly eventType: 'sanitization_incomplete' | 'injection_pattern_blocked';\n /** Detected pattern names; empty for an incomplete-sanitization refusal. */\n readonly patterns: readonly string[];\n readonly tier: SecurityTier;\n}\n\n/** Reject inputs with detected injection patterns for elevated security tiers. */\nexport function checkSecurityTier(\n tier: SecurityTier,\n sanitizeResult: SanitizeToolInputResult,\n logger: ILogger\n): SecurityTierRefusal | null {\n // A value the sanitizer could not reduce to a fixed point is refused at EVERY\n // tier, standard included, because the argument still carries whatever the\n // stripper could not remove. `detectedPatterns` cannot catch it — the pattern\n // detectors match phrases, not tags, so a deeply nested `<system>` returned\n // clean-looking metadata with an empty pattern list. Refusing here is fail-\n // closed and rare: it takes six levels of hand-nested tags to reach.\n if (sanitizeResult.sanitizationIncomplete) {\n logger.warn('Input rejected: sanitizer did not reach a fixed point', { tier });\n return {\n error: toolStructuredError({\n errorCategory: 'permission',\n message:\n 'Input validation failed: the input could not be fully sanitized within the pass budget, ' +\n 'so it still contains markup the sanitizer removes. Simplify the input and retry.',\n }),\n eventType: 'sanitization_incomplete',\n patterns: [],\n tier,\n };\n }\n if (tier === 'standard' || sanitizeResult.detectedPatterns.length === 0) {\n return null;\n }\n logger.warn('Input rejected by security tier validation', {\n tier,\n patterns: sanitizeResult.detectedPatterns,\n });\n return {\n // Security-tier rejection of suspected injection patterns — an\n // access-control denial, categorized `permission` (#2649).\n error: toolStructuredError({\n errorCategory: 'permission',\n message:\n `Input validation failed: detected patterns [${sanitizeResult.detectedPatterns.join(', ')}]. ` +\n 'Remove prompt injection patterns and retry.',\n }),\n eventType: 'injection_pattern_blocked',\n patterns: sanitizeResult.detectedPatterns,\n tier,\n };\n}\n\n/** Emits an audit event for a security-tier refusal. */\nexport function emitSecurityTierAudit(\n auditLogger: IAuditLogger,\n toolName: string,\n ctx: RequestContext,\n refusal: SecurityTierRefusal\n): void {\n auditLogger.logSecurityEvent({\n eventType: refusal.eventType,\n // `critical` is this enum's security-violation level ('info' / 'warning' /\n // 'critical'); a refused injection attempt is not routine noise, and the\n // level is what a downstream consumer filters on.\n severity: 'critical',\n actor: actorFromContext(ctx),\n description:\n refusal.eventType === 'sanitization_incomplete'\n ? `Tool '${toolName}' refused input the sanitizer could not reduce to a fixed point.`\n : `Tool '${toolName}' refused input carrying injection patterns [${refusal.patterns.join(', ')}].`,\n requestId: ctx.requestId,\n metadata: { toolName, tier: refusal.tier, patterns: [...refusal.patterns] },\n });\n}\n","/**\n * nexus-agents/mcp - Tool Input Sanitizer Middleware\n *\n * Lightweight sanitization for MCP tool arguments. Strips XML-like\n * conversation injection tags and detects prompt injection patterns\n * in all string values within tool arguments.\n *\n * Defense-in-depth layer that protects against prompt injection\n * through tool arguments containing external content.\n *\n * @module mcp/middleware/tool-input-sanitizer\n * (Source: Issue #828 — Wire security modules into production pipeline)\n */\n\nimport type { ILogger } from '../../core/index.js';\n\n/**\n * Result of sanitizing tool input.\n */\nexport interface SanitizeToolInputResult {\n /** Sanitized arguments (XML tags stripped from string values) */\n readonly sanitized: unknown;\n /** Whether any modification was made */\n readonly wasModified: boolean;\n /** Count of strings that were modified */\n readonly modifiedCount: number;\n /** Injection patterns detected (for logging) */\n readonly detectedPatterns: readonly string[];\n /**\n * True when a value could NOT be reduced to a fixed point within the pass\n * budget, so `sanitized` still carries whatever the stripper could not\n * remove.\n *\n * `wasModified` cannot answer this: it is equally true for a clean strip and\n * for a strip that gave up. A nested tag reconstructs itself as its wrapper\n * is removed, so depth N needs N passes — at depth 6 the result was\n * `<system>PAYLOAD` with `wasModified: true` and no detected pattern, and\n * `checkSecurityTier` had nothing to refuse on. Treat true as \"do not hand\n * this to a model\".\n */\n readonly sanitizationIncomplete: boolean;\n /**\n * Number of HTML comments removed (#5258).\n *\n * Reported rather than discarded so a reader can tell that content was taken\n * out. A stripped body is shorter than what the author wrote, and without\n * this count that difference is invisible — the reviewer would read a\n * truncated PR description with no indication anything was removed.\n */\n readonly commentsRemoved: number;\n /**\n * Number of XML-like conversation-structure tags removed (#5385).\n *\n * Separate from {@link commentsRemoved} and from {@link modifiedCount} because\n * neither can represent a tag strip on its own. `modifiedCount` counts FIELDS,\n * so a comment and a tag in the same field is one modified field —\n * indistinguishable from a lone comment. Every consumer that reported the\n * removal from `commentsRemoved` alone therefore called a stripped injection\n * attempt \"routine\", and an attacker only had to add an HTML comment (which\n * GitHub's default PR template already supplies) to guarantee it.\n *\n * A stripped tag is the removal a reviewer most needs told, so it gets its own\n * counter rather than being inferred.\n */\n readonly tagsRemoved: number;\n}\n\n/**\n * XML-like tags that mimic conversation structure or system prompts.\n * Stripping these prevents prompt injection through tool arguments.\n */\nconst XML_INJECTION_PATTERN =\n /<\\/?(system|human|assistant|instructions|user|prompt|context|tool_use|tool_result)\\b[^>]*>/gi;\n\n/**\n * Patterns that indicate attempted prompt injection.\n *\n * A detection here is not merely logged: at an elevated `securityTier`,\n * `checkSecurityTier` (secure-handler.ts) turns any entry in\n * `detectedPatterns` into a hard `permission` refusal with no fallback. So a\n * detector that false-positives takes the tool offline for that input. Add one\n * only when the pattern cannot appear in benign text.\n *\n * There is deliberately NO `hidden_instruction` detector. Classifying the\n * interior of an HTML comment was attempted twice (#5258, #5262, #5270) and\n * failed on both axes: the trigger list `/execute|delete|merge|apply/i`\n * refused GitHub's own default PR template (`<!-- Please delete options that\n * are not relevant -->`), and every regex form of the containment check\n * backtracked catastrophically. `stripHtmlComments` replaces it — removing the\n * comment is strictly stronger than judging its contents, and produces no\n * detection, so it cannot false-positive into a refusal.\n */\nconst INJECTION_DETECTORS: ReadonlyArray<{ name: string; pattern: RegExp }> = [\n { name: 'system_prompt_override', pattern: /ignore (?:all )?previous (?:instructions|rules)/i },\n { name: 'role_impersonation', pattern: /i(?:'m| am) the (?:repo |project )?(?:owner|admin)/i },\n];\n\n/**\n * Remove HTML comments from untrusted input (#5258, panel option \"strip\",\n * 5 of 5 approvers, audit #144).\n *\n * The vector is an ASYMMETRY, not the words: a comment is invisible in rendered\n * markdown, so a human reviewer never sees it while a model reading the raw\n * body does. Removing the comment removes the asymmetry outright — a hostile\n * instruction cannot influence a model that never receives it — which is\n * strictly stronger than classifying comment interiors and does not invite the\n * bypass arms race that narrowing a trigger list does.\n *\n * It also fixes the reason this changed: GitHub's own default PR template\n * contains `<!-- Please delete options that are not relevant -->`, whose\n * `delete` matched the trigger list, so a contributor using the template GitHub\n * offers had their review hard-refused with no override.\n *\n * **Applied at EVERY tier**, which the security reviewer and the dissenting\n * reviewer independently agreed on: there is no legitimate reason for an agent\n * to act on instructions hidden in a comment, from any source.\n *\n * **No exemption for fenced code blocks**, and that is a deliberate cost. The\n * dissent is right that a frontend or markdown PR can legitimately show\n * `<!-- … -->` as example code, and that example is lost from the model's view.\n * Exempting fences would hand an attacker a one-line bypass — wrap the payload\n * in a fence — so the collateral damage is accepted rather than traded for a\n * hole. The removal is counted and reported, so a reader can see that something\n * was taken out rather than silently reading a shortened body.\n *\n * Unterminated comments are left alone: `<!--` with no `-->` has no interior,\n * and treating the rest of the document as comment would delete the body.\n */\nfunction stripHtmlComments(value: string): { cleaned: string; removed: number } {\n const OPEN = '<!--';\n const CLOSE = '-->';\n let out = '';\n let from = 0;\n let removed = 0;\n for (;;) {\n const open = value.indexOf(OPEN, from);\n if (open === -1) break;\n const close = value.indexOf(CLOSE, open + OPEN.length);\n if (close === -1) break;\n out += value.slice(from, open);\n from = close + CLOSE.length;\n removed++;\n }\n return { cleaned: removed === 0 ? value : out + value.slice(from), removed };\n}\n\n/**\n * Sanitizes a single string value by stripping XML injection tags and HTML\n * comments. Returns the cleaned string, whether it changed, and how many\n * comments were removed.\n */\nfunction sanitizeString(value: string): {\n cleaned: string;\n modified: boolean;\n /** True when the pass budget ran out with the value still reducible. */\n incomplete: boolean;\n commentsRemoved: number;\n tagsRemoved: number;\n} {\n // Loop to a fixed point over BOTH strips together. Each one can reconstruct\n // what the other removes, in both directions, so neither is safe alone:\n //\n // tag → tag: `<sys<system>tem>x</sys</system>tem>`\n // removing the inner `<system>` closes the outer fragments\n // into a live `<system>x</system>` (#1496).\n // comment → comment: `<!-<!-- -->- payload -->`\n // removing the inner comment splices `<!-` onto `- payload\n // -->`, yielding a live `<!-- payload -->`.\n // comment → tag: `<sys<!-- -->tem>x</sys<!-- -->tem>`\n // removing the comments reconstructs `<system>x</system>`.\n //\n // The third case is why the comment strip cannot simply run once after the\n // tag loop: doing so closed the first direction and opened the other. Both\n // run in every pass, and the pass repeats until the string stops changing.\n //\n // Bounded rather than `while`: a pathological input must not spin here, and\n // five passes clears any nesting depth seen in practice (two suffice for the\n // payloads above).\n //\n // Hitting the cap is NOT the same as a clean strip, and reporting it as one\n // was the defect. Stripping a nested tag splices the surrounding fragments\n // back into a live tag, so depth N needs N passes: at depth 6 this returned\n // `<system>PAYLOAD` with `modified: true` and no detected pattern — exactly\n // what a successful strip returns — and `checkSecurityTier` passed it\n // through at every tier. `incomplete` is the signal that separates\n // \"cleaned\" from \"gave up while still dirty\". Raising MAX_PASSES only moves\n // the depth.\n const MAX_PASSES = 5;\n let cleaned = value;\n let commentsRemoved = 0;\n let tagsRemoved = 0;\n for (let pass = 0; pass < MAX_PASSES; pass++) {\n XML_INJECTION_PATTERN.lastIndex = 0;\n // #5385: counted HERE, at the only place that knows a TAG was the thing\n // removed. A field-level \"was modified\" flag cannot substitute: a comment\n // and a tag in the SAME field produce one modified field, so the tag\n // becomes unrepresentable and every consumer reports the removal as a\n // routine comment strip — which is the reassurance an attacker wants.\n const afterTags = cleaned.replace(XML_INJECTION_PATTERN, () => {\n tagsRemoved += 1;\n return '';\n });\n const { cleaned: afterComments, removed } = stripHtmlComments(afterTags);\n // Counted per pass and accumulated: a comment removed on pass 2 was really\n // removed, and under-reporting it would hide content from the reader for\n // exactly the reason this count exists.\n commentsRemoved += removed;\n if (afterComments === cleaned) break;\n cleaned = afterComments;\n }\n // One more strip: if it still changes the string, the loop ran out of passes\n // rather than reaching a fixed point, and `cleaned` still carries whatever it\n // could not remove.\n XML_INJECTION_PATTERN.lastIndex = 0;\n const probe = stripHtmlComments(cleaned.replace(XML_INJECTION_PATTERN, '')).cleaned;\n return {\n cleaned,\n modified: cleaned !== value,\n incomplete: probe !== cleaned,\n commentsRemoved,\n tagsRemoved,\n };\n}\n\n/**\n * Detects injection patterns in a string without modifying it.\n */\nfunction detectPatterns(value: string): string[] {\n const detected: string[] = [];\n for (const { name, pattern } of INJECTION_DETECTORS) {\n pattern.lastIndex = 0;\n if (pattern.test(value)) detected.push(name);\n }\n return detected;\n}\n\n/**\n * Recursively sanitizes all string values in an object/array.\n * Returns a deep copy with XML injection tags stripped from strings.\n */\nfunction sanitizeValue(\n value: unknown,\n stats: {\n count: number;\n patterns: string[];\n commentsRemoved: number;\n tagsRemoved: number;\n incomplete: boolean;\n }\n): unknown {\n if (typeof value === 'string') {\n const patterns = detectPatterns(value);\n if (patterns.length > 0) {\n stats.patterns.push(...patterns);\n }\n const { cleaned, modified, incomplete, commentsRemoved, tagsRemoved } = sanitizeString(value);\n if (modified) stats.count++;\n if (incomplete) stats.incomplete = true;\n stats.commentsRemoved += commentsRemoved;\n stats.tagsRemoved += tagsRemoved;\n return cleaned;\n }\n\n if (Array.isArray(value)) {\n return value.map((item) => sanitizeValue(item, stats));\n }\n\n if (value !== null && typeof value === 'object') {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n // Scan the KEY too. This recursed over values and copied keys verbatim,\n // so relocating a payload from a value into a key raised no signal at\n // all and `checkSecurityTier` had nothing to refuse on. The key is not\n // rewritten — renaming a caller's key silently is its own misreport —\n // but it now contributes to `detectedPatterns` like any other string.\n const keyPatterns = detectPatterns(key);\n if (keyPatterns.length > 0) stats.patterns.push(...keyPatterns);\n result[key] = sanitizeValue(val, stats);\n }\n return result;\n }\n\n return value;\n}\n\n/**\n * Sanitizes MCP tool arguments by stripping XML injection tags\n * from all string values and detecting injection patterns.\n *\n * @param args - Tool arguments to sanitize\n * @returns Sanitized result with modification tracking\n */\nexport function sanitizeToolInput(args: unknown): SanitizeToolInputResult {\n if (args === undefined || args === null) {\n return {\n sanitized: args,\n wasModified: false,\n modifiedCount: 0,\n tagsRemoved: 0,\n detectedPatterns: [],\n sanitizationIncomplete: false,\n commentsRemoved: 0,\n };\n }\n\n const stats = {\n count: 0,\n patterns: [] as string[],\n commentsRemoved: 0,\n tagsRemoved: 0,\n incomplete: false,\n };\n const sanitized = sanitizeValue(args, stats);\n const uniquePatterns = [...new Set(stats.patterns)];\n\n return {\n sanitized,\n wasModified: stats.count > 0,\n modifiedCount: stats.count,\n detectedPatterns: uniquePatterns,\n sanitizationIncomplete: stats.incomplete,\n commentsRemoved: stats.commentsRemoved,\n tagsRemoved: stats.tagsRemoved,\n };\n}\n\n/**\n * Logs sanitization results when modifications or detections occur.\n */\nexport function logSanitizationResult(\n result: SanitizeToolInputResult,\n logger: ILogger,\n toolName: string\n): void {\n if (result.wasModified) {\n logger.warn('Tool input sanitized — XML injection tags stripped', {\n tool: toolName,\n modifiedFields: result.modifiedCount,\n });\n }\n if (result.commentsRemoved > 0) {\n // Separate from the line above because it means something different to a\n // reader: content the author wrote is absent from what the model saw. It\n // is not necessarily an attack — GitHub's default PR template trips it —\n // so this records the removal without characterizing intent (#5258).\n logger.warn('Tool input sanitized — HTML comments removed', {\n tool: toolName,\n commentsRemoved: result.commentsRemoved,\n });\n }\n if (result.tagsRemoved > 0) {\n // Its own line, and NOT folded into the comment warning above (#5385). That\n // warning says the removal is routine; a stripped conversation-structure tag\n // is not, and reporting the two together let a comment mask a tag in every\n // consumer that read only `commentsRemoved`.\n logger.warn('Tool input sanitized — conversation-structure tags stripped', {\n tool: toolName,\n tagsRemoved: result.tagsRemoved,\n });\n }\n if (result.detectedPatterns.length > 0) {\n logger.warn('Injection patterns detected in tool input', {\n tool: toolName,\n patterns: result.detectedPatterns,\n });\n }\n}\n","/**\n * Process-wide registry for the MCP {@link IPolicyFirewall} (#4888).\n *\n * The firewall was constructed at startup and reached exactly one log line: no\n * tool's deps carried it, so `createSecureHandler` never received one and no\n * policy rule was ever evaluated against a real call. Threading it explicitly\n * through every tool's deps was the alternative; the panel chose a registry\n * (record #75, 5/5 approvers) because a new tool cannot forget to read it, and\n * a silent omission is exactly how the gap arose.\n *\n * @module mcp/middleware/policy-registry\n */\n\nimport type { ILogger } from '../../core/index.js';\nimport { DEFAULT_EXECUTION_MODE } from '../../config/schemas-security.js';\nimport { parseBoolValue } from '../../config/defaults-env.js';\nimport type { ExecutionMode, IPolicyFirewall } from './policy-types.js';\n\n/**\n * The mode the registry holds before registration sets one and after a reset —\n * one helper for both so the initial value and the reset cannot drift apart\n * (#6431 review).\n */\nfunction defaultMode(): ExecutionMode {\n return DEFAULT_EXECUTION_MODE;\n}\n\nlet globalPolicyFirewall: IPolicyFirewall | undefined;\nlet globalExecutionMode: ExecutionMode = defaultMode();\n\n/**\n * The firewall every secure handler consults when its own config omits one.\n *\n * `undefined` means no firewall was wired, and secure handlers skip the policy\n * check entirely — the pre-#4888 behaviour.\n */\nexport function getGlobalPolicyFirewall(): IPolicyFirewall | undefined {\n return globalPolicyFirewall;\n}\n\n/** Wires the firewall for the process. Called once during tool registration. */\nexport function setGlobalPolicyFirewall(firewall: IPolicyFirewall): void {\n globalPolicyFirewall = firewall;\n}\n\n/**\n * The execution mode every secure handler evaluates policy under when its own\n * config carries none (#6431, #6294).\n *\n * The operator's `security.policy.defaultMode` used to travel from config to a\n * startup log line and stop: `createSecureHandler`, the middleware chain and\n * the tool wrapper each resolved a literal `'read-only'`, so an enforcing\n * firewall would have denied every mutation tool regardless of the setting.\n * The registration now sets the mode here — the same seam #4888 chose for the\n * firewall, for the same reason: a handler cannot forget to read it.\n */\nexport function getGlobalExecutionMode(): ExecutionMode {\n return globalExecutionMode;\n}\n\n/** Sets the process-wide execution mode. Called once during tool registration. */\nexport function setGlobalExecutionMode(mode: ExecutionMode): void {\n globalExecutionMode = mode;\n}\n\n/**\n * Clears the wired firewall and restores the default execution mode. Tests\n * only — the server wires once at startup.\n */\nexport function resetGlobalPolicyFirewall(): void {\n globalPolicyFirewall = undefined;\n globalExecutionMode = defaultMode();\n}\n\n/** The mode the firewall will run in, and what decided it. */\ninterface PolicyRolloutMode {\n readonly mode: 'enforce' | 'warn';\n readonly reason: 'NEXUS_MCP_POLICY_ENFORCE' | 'rollout default';\n}\n\n/**\n * Resolves the EFFECTIVE firewall mode from the environment (#6431). One\n * resolver for the startup security line and for the staging call, so the\n * two cannot disagree about what is in effect.\n *\n * `security.policy.policyMode` is deliberately not an input: it defaults to\n * `enforce` and had never been applied to a real call, so honouring it would\n * turn the default on for every operator at once. Whether it should be is\n * #4988's decision; until then the env var is the only switch, and a config\n * `warn` does not override an explicit opt-in.\n */\nexport function resolvePolicyRolloutMode(env: NodeJS.ProcessEnv = process.env): PolicyRolloutMode {\n return parseBoolValue(env['NEXUS_MCP_POLICY_ENFORCE'], false)\n ? { mode: 'enforce', reason: 'NEXUS_MCP_POLICY_ENFORCE' }\n : { mode: 'warn', reason: 'rollout default' };\n}\n\n/**\n * `enforce (NEXUS_MCP_POLICY_ENFORCE)` / `warn (rollout default)` — the mode\n * in effect and why, as one field a reader cannot mistake for the config value.\n */\nexport function formatPolicyRolloutMode(rollout: PolicyRolloutMode): string {\n return `${rollout.mode} (${rollout.reason})`;\n}\n\n/**\n * Stages a wired firewall into the mode the rollout allows, returning it.\n *\n * `getPolicyValues` defaults `policyMode` to `'enforce'`, and that default has\n * been harmless only because nothing consumed the firewall. Honouring it the\n * moment the wiring lands would turn rules that have never evaluated a single\n * real call into denials, for every operator, in one release. So the default\n * is `warn`: every rule is evaluated and every would-be denial is logged, none\n * is applied — the evidence #4988 needs.\n *\n * `NEXUS_MCP_POLICY_ENFORCE=1` (#6431) is the per-operator opt-in #4987\n * described. It runs the firewall in `enforce` regardless of the configured\n * `policyMode`, so an operator can enforce today and the soak has an enforce\n * cohort. Two things made that safe to wire: every registered tool is\n * classified from its manifest `readOnlyHint` (#5114), and the default\n * execution mode is `read-write` with the registry carrying it to every\n * handler (#6431, #6294) — so enforcing denies path-rule violations and\n * unclassified tools, not every mutation tool on the manifest.\n *\n * The log line names the EFFECTIVE mode and the reason. It no longer reports\n * the configured value: `configuredMode: 'enforce'` beside a firewall just set\n * to warn claimed an enforcement that did not happen.\n */\nexport function stagePolicyFirewallForRollout(\n firewall: IPolicyFirewall,\n logger: ILogger,\n env: NodeJS.ProcessEnv = process.env\n): IPolicyFirewall {\n const rollout = resolvePolicyRolloutMode(env);\n if (firewall.getMode() !== rollout.mode) {\n firewall.setMode(rollout.mode);\n }\n const denialsApplied = rollout.mode === 'enforce';\n logger.info(\n denialsApplied\n ? 'MCP policy firewall wired in enforce mode — denials are applied'\n : 'MCP policy firewall wired in warn mode — denials are logged, not applied',\n {\n policyMode: formatPolicyRolloutMode(rollout),\n denialsApplied,\n ruleCount: firewall.getRules().length,\n }\n );\n return firewall;\n}\n","/**\n * Occurrence sampling for warn-mode policy near-misses (#5228 review).\n *\n * #4991 made a warn-mode near-miss durable, which is what #4988's enforce\n * decision needs to read. The review panel's dissent named the cost: in enforce\n * mode a denial halts the call, which self-limits a looping agent, but in warn\n * mode the call proceeds — so an agent repeatedly tripping the same rule emits\n * one chain record per iteration, without bound.\n *\n * The naive remedies both fail. Suppressing silently reproduces the original\n * defect: the chain would again under-report what happened. Time-windowing\n * loses the trailing count — a loop that fires ten thousand times and then\n * stops leaves its final window unreported, because the emit that would have\n * carried the count never comes.\n *\n * So this samples on OCCURRENCE, not on time: emit the 1st, 2nd, 4th, 8th …\n * of each `{tool, rule}` pair. Three properties follow, and each is the reason\n * a simpler scheme was rejected:\n *\n * 1. **The first is always recorded.** A near-miss is never invisible, which is\n * the property #4991 exists to provide.\n * 2. **Growth is logarithmic.** Ten thousand occurrences produce fourteen\n * records rather than ten thousand.\n * 3. **No trailing loss.** Every emitted record carries its own ordinal, so the\n * last one written establishes \"this fired at least N times\" without needing\n * a later flush. Stopping mid-window costs nothing.\n *\n * Pure and synchronous apart from the counter it owns: no clock, no I/O. Time\n * plays no part, which is what removes the trailing-count problem.\n *\n * @module mcp/middleware/would-deny-sampler\n */\n\nimport { getTimeProvider } from '../../core/index.js';\n\n/**\n * Distinct `{tool, rule}` pairs tracked before the counter map is cleared.\n *\n * A dedup that grows without bound would move the problem rather than solve it.\n * The reset is deliberately crude — clear and start over — because the ordinals\n * it produces are a floor, not an exact tally, so restarting understates rather\n * than fabricating.\n *\n * Read the floor as the MAXIMUM ordinal recorded for a pair WITHIN A BURST, not\n * the last one and not a lifetime sum: after either reset — this cap, or\n * {@link IDLE_RESET_MS} — a pair that had reached 8192 emits `1` again, and the\n * earlier record is what still establishes how far that burst got. Ordinals are\n * therefore never summed across records. A cap this size is far above any real\n * rule set; reaching it means something is generating synthetic tool names,\n * which is itself worth seeing in the log.\n */\nexport const MAX_TRACKED_PAIRS = 500;\n\n/**\n * Idle time after which a pair's sequence starts over.\n *\n * Without this the counter is process-lifetime state, so a long-lived server\n * treats an occurrence on day 1 and another on day 30 as one continuous \"loop\"\n * — by then the sequence is so sparse that the total must double to earn\n * another record. #4988 reads a soak window measured in DAYS, so chronologically\n * distinct incidents were collapsing into a single exponential sequence and the\n * later ones were sampled out.\n *\n * This resets on IDLENESS, which is not the fixed-window scheme rejected\n * earlier. A fixed window suppresses occurrences intending to report the\n * suppressed count when the window rolls, and loses that count if the pair goes\n * quiet first. Nothing is pending here: every record is emitted when it happens\n * and is self-contained, so an idle reset costs no information. The unit of\n * sampling becomes the BURST, which is what \"a loop\" actually means.\n */\nexport const IDLE_RESET_MS = 10 * 60 * 1000;\n\n/** Per-pair occurrence count and when it was last seen. */\ninterface PairState {\n count: number;\n lastSeenMs: number;\n}\n\n/** Module state, reset by {@link resetWouldDenySampler}. */\nconst occurrences = new Map<string, PairState>();\n\n/** The key a near-miss is deduped on. */\nfunction pairKey(toolName: string, ruleName: string | undefined): string {\n return `${toolName}::${ruleName ?? '(unnamed rule)'}`;\n}\n\n/** True for 1, 2, 4, 8, 16 … — one bit set. */\nfunction isPowerOfTwo(n: number): boolean {\n return n > 0 && (n & (n - 1)) === 0;\n}\n\n/** What the caller should do with this occurrence. */\nexport interface WouldDenySample {\n /** Whether to write a durable audit record for it. */\n readonly emit: boolean;\n /** Which occurrence of this `{tool, rule}` pair it is, 1-based. */\n readonly occurrence: number;\n}\n\n/**\n * Record one warn-mode near-miss and decide whether it is written.\n *\n * Call ONLY for `would_deny`. A real `deny` must never be sampled: it halts the\n * call, so it is already self-limiting, and dropping one would lose a record of\n * an action that was actually blocked.\n */\nexport function sampleWouldDeny(toolName: string, ruleName: string | undefined): WouldDenySample {\n if (occurrences.size >= MAX_TRACKED_PAIRS) occurrences.clear();\n\n const now = getTimeProvider().now();\n const key = pairKey(toolName, ruleName);\n const prior = occurrences.get(key);\n // A pair that has been quiet longer than the idle threshold starts a new\n // burst, so its next occurrence is ordinal 1 and is emitted.\n const stale = prior !== undefined && now - prior.lastSeenMs > IDLE_RESET_MS;\n const occurrence = prior === undefined || stale ? 1 : prior.count + 1;\n occurrences.set(key, { count: occurrence, lastSeenMs: now });\n\n return { emit: isPowerOfTwo(occurrence), occurrence };\n}\n\n/**\n * Phrase the ordinal for the audit record's `reason`.\n *\n * This is the HUMAN-readable form. The machine-readable one is the typed\n * `policyOccurrence` field on the audit record — an earlier revision carried\n * the ordinal in prose ALONE, and review rejected that correctly: a consumer\n * counting records would read 14 records as 14 near-misses when 10,000\n * occurred, so the record did not structurally represent its own partial\n * coverage. Both now travel together; neither replaces the other.\n *\n * The first occurrence says nothing extra — there is no suppression to disclose\n * yet, and annotating it would make the common case noisier for no information.\n */\nexport function describeOccurrence(occurrence: number): string {\n if (occurrence <= 1) return '';\n return ` (occurrence ${String(occurrence)}; intermediate occurrences of this rule were sampled out)`;\n}\n\n/** Clears the counters. Test-only seam; production never resets mid-process. */\nexport function resetWouldDenySampler(): void {\n occurrences.clear();\n}\n","/**\n * Policy-verdict audit emission (#4991, sampling added under #5228 review).\n *\n * Split out of `secure-handler.ts` to keep that file under its line cap, and\n * because these two functions are one concern: turning a policy verdict into a\n * durable chain record, and deciding which near-misses are written.\n *\n * @module mcp/middleware/policy-audit-emit\n */\n\nimport type { IAuditLogger, PolicyAuditDecision } from '../../audit/audit-types.js';\nimport type { RequestContext } from './request-context.js';\nimport { actorFromContext } from '../../audit/secure-handler-audit.js';\nimport { sampleWouldDeny, describeOccurrence } from './would-deny-sampler.js';\n\n/** The subset of the handler config this needs. */\nexport interface PolicyAuditTarget {\n readonly toolName: string;\n readonly auditLogger?: IAuditLogger | undefined;\n}\n\n/**\n * Write the audit record for a policy verdict, sampling warn-mode near-misses.\n *\n * The asymmetry is the point (#5228 review). A real `deny` is ALWAYS recorded:\n * it halts the call, so it is already self-limiting, and dropping one would\n * lose the record of an action that was actually blocked. A `would_deny` allows\n * the call to proceed, so an agent looping against the same rule emits one\n * record per iteration — the unbounded-growth case the review dissent named.\n *\n * Near-misses are therefore sampled at occurrences 1, 2, 4, 8 … per\n * `{tool, rule}` pair, and every record written names its own ordinal. The\n * first is always recorded, so nothing becomes invisible; growth is\n * logarithmic; and because the ordinal rides on the record rather than on a\n * later flush, a loop that stops mid-sequence still leaves \"fired at least N\n * times\" readable in the chain.\n */\nexport function recordPolicyVerdict(\n config: PolicyAuditTarget,\n requestContext: RequestContext,\n verdict: PolicyAuditDecision,\n ruleName: string | undefined\n): void {\n const auditLogger = config.auditLogger;\n if (!auditLogger) return;\n\n if (verdict !== 'would_deny') {\n emitPolicyAudit({\n auditLogger,\n toolName: config.toolName,\n ctx: requestContext,\n decision: verdict,\n reason: 'policy denied',\n });\n return;\n }\n\n const sample = sampleWouldDeny(config.toolName, ruleName);\n if (!sample.emit) return;\n\n emitPolicyAudit({\n auditLogger,\n toolName: config.toolName,\n ctx: requestContext,\n decision: verdict,\n reason: `policy would have denied (warn mode)${describeOccurrence(sample.occurrence)}`,\n occurrence: sample.occurrence,\n });\n}\n\ninterface PolicyAuditEmission {\n readonly auditLogger: IAuditLogger;\n readonly toolName: string;\n readonly ctx: RequestContext;\n readonly decision: PolicyAuditDecision;\n readonly reason: string;\n /** Set only for a sampled `would_deny`; absent means nothing was sampled. */\n readonly occurrence?: number;\n}\n\nfunction emitPolicyAudit({\n auditLogger,\n toolName,\n ctx,\n decision,\n reason,\n occurrence,\n}: PolicyAuditEmission): void {\n const actor = actorFromContext(ctx);\n auditLogger.logPolicyDecision({\n policyName: 'default',\n decision,\n reason,\n toolName,\n actor,\n requestId: ctx.requestId,\n // Typed and queryable, not only prose in `reason` (#5228 review): a\n // consumer counting records must be able to see that 14 records stand for\n // 10,000 occurrences without parsing a sentence.\n ...(occurrence === undefined ? {} : { occurrence }),\n });\n}\n","/**\n * The policy check every secure handler runs, as ONE function (#6431 review).\n *\n * Extracted from `secure-handler.ts` so that a handler which dispatches another\n * tool's engine on the caller's behalf — `run { execute: true }` runs the\n * selected strategy's engine directly, never through that tool's secure\n * handler — evaluates the same firewall, under the same mode, and returns the\n * same denial envelope and audit record the target tool's own handler would.\n * Before this, `run_dev_pipeline` was denied under the read-only lock while\n * `run { execute: true, forceStrategy: 'dev-pipeline' }` ran the same engine.\n *\n * @module mcp/middleware/policy-check\n */\n\nimport type { ILogger } from '../../core/index.js';\nimport type { IAuditLogger, PolicyAuditDecision } from '../../audit/audit-types.js';\nimport type { RequestContext } from './request-context.js';\nimport { type IPolicyFirewall, type ExecutionMode, createPolicyContext } from './policy.js';\nimport { getGlobalPolicyFirewall, getGlobalExecutionMode } from './policy-registry.js';\nimport { recordPolicyVerdict } from './policy-audit-emit.js';\nimport { toolStructuredError, type ToolResult } from '../tools/tool-result.js';\n\nconst registrationAuditLoggers = new WeakMap<ILogger, IAuditLogger>();\n\n/** Sets or clears the audit logger used while secure handlers are registered. */\nexport function setSecureHandlerAuditLogger(logger: ILogger, auditLogger?: IAuditLogger): void {\n if (auditLogger === undefined) registrationAuditLoggers.delete(logger);\n else registrationAuditLoggers.set(logger, auditLogger);\n}\n\n/** The audit logger registered for this registration logger, if any. */\nexport function getRegisteredAuditLogger(logger: ILogger): IAuditLogger | undefined {\n return registrationAuditLoggers.get(logger);\n}\n\n/**\n * Creates a policy denial error response — an access-control denial,\n * categorized `permission` (#2649).\n */\nfunction policyDeniedError(reason: string, requestId: string): ToolResult {\n return toolStructuredError({\n errorCategory: 'permission',\n message: `Policy denied: ${reason} (request: ${requestId})`,\n });\n}\n\n/** Options for policy check */\ninterface PolicyCheckOptions {\n firewall: IPolicyFirewall;\n toolName: string;\n args: unknown;\n mode: ExecutionMode;\n allowedPaths?: readonly string[] | undefined;\n logger: ILogger;\n requestId: string;\n}\n\n/**\n * What the policy evaluation produced: the denial result to return (if any),\n * and the verdict to record on the chain.\n *\n * The verdict is returned separately because it is NOT derivable from the\n * result (#4991). In warn mode a rule fires and the firewall allows anyway, so\n * `result` is null exactly as it is for an ordinary allow — the two are\n * indistinguishable downstream unless the decision travels with it.\n */\ninterface PolicyCheckOutcome {\n readonly result: ToolResult | null;\n /** `null` when no rule fired — an ordinary allow, which is not recorded. */\n readonly verdict: PolicyAuditDecision | null;\n /**\n * The rule that fired, when one did. Carried out so the near-miss sampler can\n * key on `{tool, rule}` — sampling on the tool alone would let one noisy rule\n * suppress a different rule's first occurrence on the same tool.\n */\n readonly ruleName?: string | undefined;\n}\n\n/**\n * Evaluates policy firewall and returns error if denied.\n */\nfunction checkPolicy(opts: PolicyCheckOptions): PolicyCheckOutcome {\n const ctxOpts = {\n mode: opts.mode,\n ...(opts.allowedPaths && { allowedPaths: opts.allowedPaths }),\n };\n const decision = opts.firewall.evaluate(createPolicyContext(opts.toolName, opts.args, ctxOpts));\n\n if (!decision.allowed) {\n opts.logger.warn('Policy denied tool execution', {\n reason: decision.reason,\n ruleName: decision.ruleName,\n });\n return {\n result: policyDeniedError(decision.reason, opts.requestId),\n verdict: 'deny',\n ruleName: decision.ruleName,\n };\n }\n\n // Warn mode: the evaluator sets `overriddenByWarnMode` when a rule denied and\n // the mode allowed anyway. Read that flag and nothing else — not the '[WARN\n // MODE]' reason prefix (display copy, breaks on a reword), and not the\n // presence of `ruleName` on an allowed decision. The latter was the first\n // implementation and a panel rejected it: naming the rule that PERMITTED an\n // action is ordinary practice, so that inference would start reporting\n // authorized calls as near-misses the day an allow rule sets `ruleName`.\n if (decision.overriddenByWarnMode === true) {\n opts.logger.debug('Policy would have denied (warn mode)', {\n reason: decision.reason,\n ruleName: decision.ruleName,\n });\n return { result: null, verdict: 'would_deny', ruleName: decision.ruleName };\n }\n\n opts.logger.debug('Policy check passed', { reason: decision.reason });\n return { result: null, verdict: null };\n}\n\n/** The subset of a secure-handler config the policy check reads. */\nexport interface PolicyCheckTarget {\n readonly toolName: string;\n readonly policyFirewall?: IPolicyFirewall | undefined;\n readonly allowedPaths?: readonly string[] | undefined;\n readonly auditLogger?: IAuditLogger | undefined;\n}\n\n/**\n * Evaluates the policy firewall for this call, or returns `null` when none is\n * configured.\n *\n * #4888: the firewall falls back to the process-wide registry. Nothing ever\n * supplied `config.policyFirewall`, so before that fallback this check was\n * unreachable for every registered tool.\n */\nexport function runPolicyCheck(\n config: PolicyCheckTarget,\n sanitizedArgs: unknown,\n mode: ExecutionMode,\n logger: ILogger,\n requestContext: RequestContext\n): { error: ToolResult | null; nearMiss: boolean } {\n const firewall = config.policyFirewall ?? getGlobalPolicyFirewall();\n if (!firewall) return { error: null, nearMiss: false };\n\n const { result, verdict, ruleName } = checkPolicy({\n firewall,\n toolName: config.toolName,\n args: sanitizedArgs,\n mode,\n allowedPaths: config.allowedPaths,\n logger,\n requestId: requestContext.requestId,\n });\n\n // Emitted for a real denial AND for a warn-mode near-miss (#4991). An\n // ordinary allow (verdict null) is not recorded: emitting every permitted\n // call would bury the soak signal it exists to surface.\n if (verdict !== null && config.auditLogger) {\n recordPolicyVerdict(config, requestContext, verdict, ruleName);\n }\n // #5228 review: the near-miss travels on regardless of whether the policy\n // record above was sampled out. A `would_deny` lets the call EXECUTE, so its\n // invocation record must not be indistinguishable from one where no rule\n // fired — otherwise sampling, which exists to bound growth, would restore the\n // silent-allow inference this change is meant to break.\n return { error: result, nearMiss: verdict === 'would_deny' };\n}\n\n/** A tool a handler dispatches on the caller's behalf. */\nexport interface DispatchedToolPolicyOptions {\n /** The tool whose engine is about to run (e.g. `run_dev_pipeline`). */\n readonly toolName: string;\n readonly args: unknown;\n /** The dispatching tool's REGISTRATION logger — the audit logger is keyed on it. */\n readonly logger: ILogger;\n readonly requestContext: RequestContext;\n}\n\n/**\n * Evaluates the wired firewall for a tool a handler dispatches directly\n * (#6431 review). Same firewall, same registry mode, same denial envelope and\n * the same audit record the target tool's own secure handler produces; `null`\n * means allowed (or no firewall wired, exactly as for the target tool).\n */\nexport function checkPolicyForDispatchedTool(opts: DispatchedToolPolicyOptions): ToolResult | null {\n const auditLogger = getRegisteredAuditLogger(opts.logger);\n const target: PolicyCheckTarget = {\n toolName: opts.toolName,\n ...(auditLogger !== undefined && { auditLogger }),\n };\n return runPolicyCheck(\n target,\n opts.args,\n getGlobalExecutionMode(),\n opts.logger,\n opts.requestContext\n ).error;\n}\n","/** Detect replacement of the install backing a long-lived MCP server (#6959). */\nimport { readFileSync, statSync } from 'node:fs';\nimport { basename, dirname, isAbsolute, relative, resolve, sep } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { ILogger } from '../../core/index.js';\nimport { VERSION } from '../../version.js';\nimport { toolStructuredError, type ToolResult } from '../tools/tool-result.js';\n\n// Captured at module load, before registration or any lazy tool imports. tsup\n// emits flat dist chunks; source runs cannot identify an installed artifact.\nconst moduleDirectory = dirname(fileURLToPath(import.meta.url));\nconst packagePath =\n basename(moduleDirectory) === 'dist' ? resolve(moduleDirectory, '../package.json') : undefined;\nlet cachedMtime: number | undefined;\nlet installedVersion = VERSION;\nlet refusal: ToolResult | undefined;\nlet warned = false;\n\nfunction warnOnce(logger: ILogger, message: string): void {\n if (warned) return;\n warned = true;\n logger.warn(message);\n}\n\nfunction refuseInstall(version: string, logger: ILogger): ToolResult {\n const message = `nexus-agents was upgraded from ${VERSION} to ${version} while this MCP server was running; restart the MCP server (or your client) to load the new version.`;\n warnOnce(logger, message);\n refusal = toolStructuredError({ errorCategory: 'business', message });\n return refusal;\n}\n\n/** Stat on every dispatch, reading package metadata only when mtime changes. */\nexport function checkRunningInstall(logger: ILogger): ToolResult | undefined {\n if (packagePath === undefined) {\n warnOnce(\n logger,\n 'Running install check unmeasured: starting package.json cannot be determined; skipping check.'\n );\n return undefined;\n }\n if (refusal !== undefined) return refusal;\n try {\n const mtime = statSync(packagePath).mtimeMs;\n if (mtime !== cachedMtime) {\n installedVersion = readInstalledVersion(packagePath);\n cachedMtime = mtime;\n }\n return installedVersion === VERSION ? undefined : refuseInstall(installedVersion, logger);\n } catch (error) {\n if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {\n return refuseInstall('removed', logger);\n }\n const message =\n 'Running install check could not read package.json; restart the MCP server (or your client).';\n warnOnce(logger, message);\n return toolStructuredError({ errorCategory: 'internal', message });\n }\n}\n\n/** Map only the missing target, never its importer or an unrelated dependency. */\nexport function mapInstallModuleError(error: unknown, logger: ILogger): ToolResult | undefined {\n if (packagePath === undefined) return undefined;\n const target = missingModuleTarget(error);\n if (target === undefined) return undefined;\n const path = moduleTargetPath(target);\n if (path === undefined) return undefined;\n if (!isAbsolute(path)) return undefined;\n const within = relative(moduleDirectory, path);\n if (within === '' || within === '..' || within.startsWith(`..${sep}`) || isAbsolute(within))\n return undefined;\n return checkRunningInstall(logger) ?? refuseInstall('unknown (installation changed)', logger);\n}\n\n/** Invalid file URLs are unrelated module errors, not evidence of replacement. */\nfunction moduleTargetPath(target: string): string | undefined {\n try {\n return target.startsWith('file:') ? fileURLToPath(target) : target;\n } catch {\n return undefined;\n }\n}\n\n/** Validate only the installed version field consumed by the guard. */\nfunction readInstalledVersion(path: string): string {\n const pkg: unknown = JSON.parse(readFileSync(path, 'utf8'));\n if (\n typeof pkg !== 'object' ||\n pkg === null ||\n !('version' in pkg) ||\n typeof pkg.version !== 'string'\n ) {\n throw new Error('Installed package.json has no version');\n }\n return pkg.version;\n}\n\n/** Node's ESM and CommonJS module-miss messages identify the missing target. */\nfunction missingModuleTarget(error: unknown): string | undefined {\n if (!(error instanceof Error) || !('code' in error)) return undefined;\n if (error.code !== 'ERR_MODULE_NOT_FOUND' && error.code !== 'MODULE_NOT_FOUND') return undefined;\n return /Cannot find module ['\"]([^'\"]+)['\"]/.exec(error.message)?.[1];\n}\n","/**\n * nexus-agents/mcp - Secure Handler Middleware\n *\n * Higher-order function that wraps MCP tool handlers with security middleware:\n * - RequestContext creation and tracking\n * - PolicyFirewall evaluation\n * - Logging with request context\n *\n * (Source: Issue #185 Phase 1 - PolicyFirewall integration)\n *\n * @module mcp/middleware/secure-handler\n */\n\nimport type { ILogger } from '../../core/index.js';\nimport { createLogger, getTimeProvider } from '../../core/index.js';\nimport {\n createRequestContext,\n contextForLogging,\n type RequestContext,\n type CallerInfo,\n getCurrentRequestContext,\n serverCallerInfo,\n} from './request-context.js';\nimport type { IPolicyFirewall, ExecutionMode } from './policy.js';\nimport type { RateLimiter } from './rate-limiter.js';\nimport { checkRateLimit, emitRateLimitAudit } from './secure-handler-rate-limit.js';\nimport {\n checkSecurityTier,\n emitSecurityTierAudit,\n type SecurityTier,\n} from './secure-handler-tier.js';\nimport type { IAuditLogger, AuditOutcome } from '../../audit/audit-types.js';\nimport { actorFromContext, resultToOutcome } from '../../audit/secure-handler-audit.js';\nimport { sanitizeToolInput, logSanitizationResult } from './tool-input-sanitizer.js';\nimport { sanitizeErrorDetails, sanitizeStringLeaves } from '../../security/output-sanitizer.js';\nimport { toolStructuredError, type ToolResult } from '../tools/tool-result.js';\nimport { getGlobalExecutionMode } from './policy-registry.js';\nimport { runPolicyCheck, getRegisteredAuditLogger } from './policy-check.js';\nimport { checkRunningInstall, mapInstallModuleError } from './install-state.js';\n\nexport type { ToolResult };\n\n/**\n * Tool handler function signature.\n */\nexport type ToolHandler = (args: unknown) => Promise<ToolResult>;\n\nexport type { SecurityTier };\n\n/**\n * Configuration for the secure handler wrapper.\n */\nexport interface SecureHandlerConfig {\n /** Tool name for logging and policy evaluation */\n toolName: string;\n /** Security tier controlling input validation strictness (default: 'standard') */\n securityTier?: SecurityTier;\n /**\n * Fields whose PRE-sanitization value must be hashed for a persisted record\n * (#5385), keyed by arg name, valued by the canonical hasher for that field.\n *\n * The handler receives only the resulting HASHES, never the raw text. That is\n * the point: `reviewedDiffHash` has to bind bytes the governor gate can\n * recompute from git, but handing a handler the raw args would partly defeat\n * sanitizing before dispatch — a careless handler could put unsanitized\n * untrusted content into a prompt. A 64-hex digest cannot be injected into\n * anything, so the seam is safe by construction rather than by discipline.\n */\n rawHashFields?: Readonly<Record<string, (raw: string) => string>>;\n /** Policy firewall instance (optional - if not provided, policy checks are skipped) */\n policyFirewall?: IPolicyFirewall;\n /** Execution mode for policy evaluation */\n executionMode?: ExecutionMode;\n /** Allowed paths for file operations */\n allowedPaths?: readonly string[];\n /** Rate limiter instance (optional) */\n rateLimiter?: RateLimiter;\n /** Logger instance (optional - creates default if not provided) */\n logger?: ILogger;\n /** Caller information extractor (optional) */\n callerInfo?: CallerInfo;\n /** Audit logger for structured audit trail (Issue #740 Phase 2) */\n auditLogger?: IAuditLogger;\n}\n\n// The registration audit-logger map and the policy check moved to\n// policy-check.ts (#6431 review) so `run { execute: true }` can evaluate the\n// firewall for its selected strategy tool with the SAME check. Re-exported so\n// the registration seam's import path is unchanged.\nexport { setSecureHandlerAuditLogger } from './policy-check.js';\n\n/**\n * Extended handler context passed to the wrapped handler.\n */\n/**\n * What the middleware removed from this call's input, disclosed to the handler\n * (#5385).\n *\n * The middleware sanitizes BEFORE dispatch, so a handler receives cleaned args\n * and cannot otherwise know either what its raw input was or what was taken\n * out. That was low-impact while sanitization only stripped conversation-\n * structure XML tags; #5258 added HTML-comment stripping, which fires on any\n * markdown change — including this repo's own governance-regeneration PRs.\n *\n * Counts only, never the removed bytes: handing raw or stripped content back to\n * the handler would partially defeat sanitize-before-dispatch, which is the\n * property this middleware exists to guarantee.\n */\n/*\n * Not exported: every consumer reaches it through `HandlerContext.sanitization`\n * or builds an object literal, so exporting the name adds a symbol with no\n * cross-file consumer — which the #3024 gate correctly rejects.\n */\ninterface SanitizationContext {\n /** Whether sanitization changed the args at all. */\n readonly wasModified: boolean;\n /** HTML comments removed from untrusted fields (#5258). */\n readonly commentsRemoved: number;\n /** How many FIELDS the sanitizer changed at all (#5385). */\n readonly fieldsModified: number;\n /**\n * XML-like conversation-structure tags removed (#5385).\n *\n * Its own counter, not inferred from the two above. `fieldsModified` counts\n * FIELDS, so a comment and a tag in the same field is ONE modified field —\n * arithmetic over the other counters cannot recover the tag, and every\n * consumer then reports a stripped injection attempt as a routine comment\n * strip. An attacker only has to include an HTML comment to get that\n * reassurance, and GitHub's default PR template already supplies one.\n */\n readonly tagsRemoved: number;\n /**\n * Pre-sanitization hashes of the fields named in\n * {@link SecureHandlerConfig.rawHashFields} (#5385).\n *\n * Empty when the tool declared none — which is distinguishable from \"declared\n * and the field was absent\", because a declared-but-absent field simply has\n * no key here and the handler can tell the two apart by what it asked for.\n */\n readonly rawFieldHashes: Readonly<Record<string, string>>;\n /**\n * UTF-8 byte length (`Buffer.byteLength`) of each field in\n * {@link rawFieldHashes}, measured on the SAME raw value the hash was\n * computed over, before sanitization (#6177). Same key set as the hashes: a\n * declared-but-absent field has no entry in either.\n *\n * Carried because a hash alone cannot say how much it covers. `pr_review`'s\n * hasher truncates at a byte cap, and the handler — holding only the\n * sanitized text — measured coverage over that text, so a raw diff over the\n * cap whose sanitized form was under it was recorded as fully bound. A byte\n * count, like a digest, cannot be injected into anything, so the seam stays\n * sanitize-before-dispatch.\n */\n readonly rawFieldBytes: Readonly<Record<string, number>>;\n}\n\nexport interface HandlerContext {\n /** Request context for this invocation */\n requestContext: RequestContext;\n /** Logger with request context attached */\n logger: ILogger;\n /**\n * What sanitization removed (#5385).\n *\n * REQUIRED, not optional, deliberately. An optional field invites\n * `ctx.sanitization?.commentsRemoved ?? 0`, which renders \"the middleware did\n * not tell me\" as \"nothing was removed\" — a default reported as a\n * measurement, which is the shape this repo treats as a p1 on the governor\n * path. Always present means always truthful.\n */\n readonly sanitization: SanitizationContext;\n}\n\n/**\n * Tool handler with context signature.\n */\nexport type ContextAwareHandler = (args: unknown, ctx: HandlerContext) => Promise<ToolResult>;\n\n/**\n * Creates an internal error response (#2649).\n */\nfunction internalError(message: string, requestId: string): ToolResult {\n return toolStructuredError({\n errorCategory: 'internal',\n message: `Internal error: ${message} (request: ${requestId})`,\n });\n}\n\n/**\n * Maximum input size for tool arguments (10MB).\n * Prevents memory exhaustion from oversized payloads.\n * (Source: Issue #740 - MCP security hardening)\n */\nconst MAX_INPUT_SIZE_BYTES = 10 * 1024 * 1024;\n\n/**\n * Redact detected secrets from tool output text (#6484).\n * Unifies on canonical sanitizeErrorDetails to cover Anthropic sk-ant-*,\n * OpenAI sk-proj-*, Gemini AIzaSy*, GitHub PATs, and URL/Bearer credentials.\n */\nfunction sanitizeOutput(text: string, logger?: ILogger): string {\n const sanitized = sanitizeErrorDetails(text, undefined, '[REDACTED]');\n if (sanitized !== text && logger !== undefined) {\n logger.warn('Potential secret detected in tool output, redacting');\n }\n return sanitized;\n}\n\n/** Recursively sanitize record entries (#6484). */\nfunction sanitizeDeepRecord(\n obj: Record<string, unknown>,\n logger?: ILogger\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(obj)) {\n result[key] = sanitizeStringLeaves(val, (text) => sanitizeOutput(text, logger));\n }\n return result;\n}\n\n/** Sanitize all text content, structuredContent, and _meta in a tool result (#740, #6484). */\nfunction sanitizeToolResult(result: ToolResult, logger: ILogger): void {\n for (const item of result.content) {\n item.text = sanitizeOutput(item.text, logger);\n }\n if (result.structuredContent !== undefined) {\n result.structuredContent = sanitizeDeepRecord(result.structuredContent, logger);\n }\n if (result._meta !== undefined) {\n result._meta = sanitizeDeepRecord(result._meta, logger);\n }\n}\n\n/** Validates input size and returns error if too large. */\nfunction checkInputSize(args: unknown, logger: ILogger, requestId: string): ToolResult | null {\n if (args === undefined) return null;\n const inputSize = JSON.stringify(args).length;\n if (inputSize > MAX_INPUT_SIZE_BYTES) {\n logger.warn('Input size exceeds limit', { inputSize, limit: MAX_INPUT_SIZE_BYTES });\n return internalError('Input too large', requestId);\n }\n return null;\n}\n\n/**\n * Executes handler and logs result.\n */\nasync function executeHandler(\n handler: ToolHandler | ContextAwareHandler,\n args: unknown,\n ctx: HandlerContext,\n logger: ILogger,\n logLifecycle: boolean\n): Promise<ToolResult> {\n const startTime = getTimeProvider().now();\n const result =\n handler.length >= 2 ? await handler(args, ctx) : await (handler as ToolHandler)(args);\n\n // Suppressed when the middleware chain already brackets this call: its own\n // audit middleware logs a completion spanning the whole stack, where this\n // one times only the handler body (#4981).\n if (!logLifecycle) return result;\n\n const durationMs = getTimeProvider().now() - startTime;\n if (result.isError === true) {\n logger.warn('Tool execution completed with error', { durationMs });\n } else {\n logger.info('Tool execution completed', { durationMs });\n }\n return result;\n}\n\ninterface ToolAuditEmission {\n readonly auditLogger: IAuditLogger;\n readonly toolName: string;\n readonly ctx: RequestContext;\n readonly outcome: AuditOutcome;\n readonly durationMs: number;\n /** A warn-mode rule fired for this call (#5228 review). */\n readonly nearMiss: boolean;\n}\n\n/**\n * Emits an audit event for a tool invocation, however it ended.\n *\n * ONE emitter for both exits, deliberately. The success path and the throw path\n * previously had separate functions differing only in `outcome`, and the\n * near-miss annotation was added to the success one alone — so a warn-mode\n * near-miss whose handler THREW produced an `outcome: 'error'` record with no\n * policy annotation, indistinguishable from a clean call that errored. That is\n * the inference this change exists to break, on the path where an action ran\n * and did not complete cleanly, which is the more review-worthy case.\n *\n * Two exits with one shared obligation is exactly the seam a duplicated emitter\n * lets you wire half of. Merging them makes the annotation structural rather\n * than something each caller has to remember.\n */\nfunction emitToolAudit({\n auditLogger,\n toolName,\n ctx,\n outcome,\n durationMs,\n nearMiss,\n}: ToolAuditEmission): void {\n const actor = actorFromContext(ctx);\n auditLogger.logToolInvocation({\n toolName,\n outcome,\n actor,\n requestId: ctx.requestId,\n durationMs,\n // The fact the sampler must never suppress: this call EXECUTED and a rule\n // would have denied it. The policy record carries the detail and may be\n // sampled; this says the action itself was not clean, on every occurrence.\n ...(nearMiss ? { policyDecision: 'would_deny' as const } : {}),\n });\n}\n\n/** The raw-field measurements {@link measureRawFields} takes before sanitization. */\ntype RawFieldMeasurements = Pick<SanitizationContext, 'rawFieldHashes' | 'rawFieldBytes'>;\n\n/**\n * Hash AND measure the declared raw fields before sanitization (#5385, #6177).\n *\n * Only string values are measured; a missing or non-string field contributes no\n * key rather than an empty-string hash, so \"absent\" cannot be mistaken for\n * \"present and empty\" — the two have different digests and only one is a\n * measurement. The byte length is taken in the same loop, on the same value,\n * so the two maps describe the same bytes by construction.\n */\nfunction measureRawFields(\n fields: Readonly<Record<string, (raw: string) => string>> | undefined,\n args: unknown\n): RawFieldMeasurements {\n if (fields === undefined) return { rawFieldHashes: {}, rawFieldBytes: {} };\n if (typeof args !== 'object' || args === null) return { rawFieldHashes: {}, rawFieldBytes: {} };\n const source = args as Record<string, unknown>;\n const rawFieldHashes: Record<string, string> = {};\n const rawFieldBytes: Record<string, number> = {};\n for (const [field, hash] of Object.entries(fields)) {\n const value = source[field];\n if (typeof value !== 'string') continue;\n rawFieldHashes[field] = hash(value);\n rawFieldBytes[field] = Buffer.byteLength(value, 'utf-8');\n }\n return { rawFieldHashes, rawFieldBytes };\n}\n\n/**\n * The context reported on an early-exit path, where sanitization never ran.\n *\n * Reported anyway, so a caller never has to distinguish \"not sanitized\" from\n * \"sanitized, nothing removed\". #5385: the raw hashes are computed here too. An\n * early exit means the tool never ran, but a caller that asked for a hash still\n * learns whether the field was present — absence of the key means absent, not\n * \"hash of nothing\".\n */\nfunction unsanitizedContext(config: SecureHandlerConfig, args: unknown): SanitizationContext {\n return {\n wasModified: false,\n commentsRemoved: 0,\n fieldsModified: 0,\n tagsRemoved: 0,\n ...measureRawFields(config.rawHashFields, args),\n };\n}\n\n/** Emits an audit event for a policy denial. */\n/** Pre-execution checks: input size, input sanitization, rate limit, policy. */\nfunction runPreChecks(\n config: SecureHandlerConfig,\n args: unknown,\n mode: ExecutionMode,\n requestContext: RequestContext,\n logger: ILogger\n): {\n error: ToolResult | null;\n sanitizedArgs: unknown;\n nearMiss: boolean;\n sanitization: SanitizationContext;\n} {\n const sizeResult = checkInputSize(args, logger, requestContext.requestId);\n if (sizeResult) {\n return {\n error: sizeResult,\n sanitizedArgs: args,\n nearMiss: false,\n sanitization: unsanitizedContext(config, args),\n };\n }\n\n // Sanitize tool input: strip XML injection tags, detect injection patterns (Issue #828)\n const sanitizeResult = sanitizeToolInput(args);\n logSanitizationResult(sanitizeResult, logger, config.toolName);\n const sanitizedArgs = sanitizeResult.wasModified ? sanitizeResult.sanitized : args;\n const sanitization: SanitizationContext = {\n wasModified: sanitizeResult.wasModified,\n commentsRemoved: sanitizeResult.commentsRemoved,\n fieldsModified: sanitizeResult.modifiedCount,\n tagsRemoved: sanitizeResult.tagsRemoved,\n // #5385/#6177: hashed and measured from `args`, the RAW input, before\n // sanitization touched it.\n ...measureRawFields(config.rawHashFields, args),\n };\n\n // Tiered validation: reject (not strip) for user-facing/external tools (Issue #1586)\n const refusal = checkSecurityTier(config.securityTier ?? 'standard', sanitizeResult, logger);\n if (refusal !== null) {\n // Without this the refused call is the ONE kind of traffic that leaves no\n // trace in the audit chain: it returns above both the rate limiter and\n // `executeAndAudit`, so an attack read as a quiet period. See\n // `secure-handler-tier.ts` for why the limiter still runs after this check.\n if (config.auditLogger)\n emitSecurityTierAudit(config.auditLogger, config.toolName, requestContext, refusal);\n return { error: refusal.error, sanitizedArgs, nearMiss: false, sanitization };\n }\n\n if (config.rateLimiter) {\n const denial = checkRateLimit(config.rateLimiter, logger);\n if (denial) {\n if (config.auditLogger)\n emitRateLimitAudit(config.auditLogger, config.toolName, requestContext, denial.state);\n return { error: denial.error, sanitizedArgs, nearMiss: false, sanitization };\n }\n }\n\n const policy = runPolicyCheck(config, sanitizedArgs, mode, logger, requestContext);\n if (policy.error)\n return { error: policy.error, sanitizedArgs, nearMiss: policy.nearMiss, sanitization };\n\n return { error: null, sanitizedArgs, nearMiss: policy.nearMiss, sanitization };\n}\n\n/**\n * Wraps a tool handler with security middleware.\n *\n * @param handler - The original tool handler or context-aware handler\n * @param config - Security configuration\n * @returns Wrapped handler with security middleware\n */\nexport function createSecureHandler(\n handler: ToolHandler | ContextAwareHandler,\n config: SecureHandlerConfig\n): ToolHandler {\n const registeredAuditLogger = config.logger && getRegisteredAuditLogger(config.logger);\n if (config.auditLogger === undefined && registeredAuditLogger !== undefined)\n config = { ...config, auditLogger: registeredAuditLogger };\n const logger = config.logger ?? createLogger({ tool: config.toolName });\n\n return async (args: unknown): Promise<ToolResult> => {\n const installError = checkRunningInstall(logger);\n if (installError !== undefined) return installError;\n // Resolved per call, like the firewall (#6431 review): handlers are created\n // at registration, before the operator's mode reaches the registry, so a\n // mode captured here would be the pre-registration default for the life of\n // the process.\n const mode = config.executionMode ?? getGlobalExecutionMode();\n // A configured `callerInfo` wins; otherwise the caller is what the server\n // measured — the transport it connected (#6795), the same source the\n // middleware chain uses, so both contexts of one call derive one tier.\n const ctxOpts = {\n toolName: config.toolName,\n caller: config.callerInfo ?? serverCallerInfo(),\n };\n // Adopt the middleware chain's context when this handler is nested inside\n // it, so one call carries one id and one start/complete pair (#4981).\n //\n // Gated on the tool NAME matching: an in-process nested tool call would\n // otherwise inherit its parent's identity, and two different tools would\n // share one request id. Gated on presence at all because every direct\n // caller of createSecureHandler — the tests — has no outer context, and\n // must keep minting and logging its own.\n const ambient = getCurrentRequestContext();\n const inherited = ambient?.toolName === config.toolName ? ambient : undefined;\n // Join the outer request's IDENTITY, but keep deriving this handler's own\n // caller and trust tier. The chain mints its context from the server's\n // measured caller only, so adopting that object wholesale would discard a configured\n // `callerInfo` — downgrading trustTier and the audit actor to \"unknown\"\n // on the very path this change is meant to make auditable.\n const requestContext =\n inherited === undefined\n ? createRequestContext(ctxOpts)\n : createRequestContext({ ...ctxOpts, inheritRequestId: inherited.requestId });\n const requestLogger = logger.child(contextForLogging(requestContext));\n if (inherited === undefined) {\n requestLogger.info('Tool invocation started');\n }\n\n const {\n error: preCheckError,\n sanitizedArgs,\n nearMiss,\n sanitization,\n } = runPreChecks(config, args, mode, requestContext, requestLogger);\n if (preCheckError) return preCheckError;\n\n return executeAndAudit(handler, sanitizedArgs, config, {\n requestContext,\n requestLogger,\n nearMiss,\n sanitization,\n logLifecycle: inherited === undefined,\n });\n };\n}\n\n/**\n * Executes the wrapped handler with audit emission on both the success and\n * exception paths. Extracted from `createSecureHandler` to keep that\n * function within the 50-line budget.\n */\n/** The per-call state `executeAndAudit` needs, bundled to stay under the param cap. */\ninterface Invocation {\n readonly requestContext: RequestContext;\n readonly requestLogger: ILogger;\n /** False when the middleware chain already brackets this call (#4981). */\n readonly logLifecycle: boolean;\n /**\n * A warn-mode policy rule fired for this call (#5228 review).\n *\n * Carried onto the invocation record whether or not the policy record itself\n * was sampled, so an executed near-miss is never indistinguishable from a\n * call no rule touched.\n */\n readonly nearMiss: boolean;\n /** What sanitization removed, forwarded to the handler (#5385). */\n readonly sanitization: SanitizationContext;\n}\n\nasync function executeAndAudit(\n handler: ToolHandler | ContextAwareHandler,\n sanitizedArgs: unknown,\n config: SecureHandlerConfig,\n invocation: Invocation\n): Promise<ToolResult> {\n const { requestContext, requestLogger } = invocation;\n const execStartTime = getTimeProvider().now();\n try {\n const result = await executeHandler(\n handler,\n sanitizedArgs,\n { requestContext, logger: requestLogger, sanitization: invocation.sanitization },\n requestLogger,\n invocation.logLifecycle\n );\n sanitizeToolResult(result, requestLogger);\n if (config.auditLogger) {\n emitToolAudit({\n auditLogger: config.auditLogger,\n toolName: config.toolName,\n ctx: requestContext,\n outcome: resultToOutcome(result.isError, false),\n durationMs: getTimeProvider().now() - execStartTime,\n nearMiss: invocation.nearMiss,\n });\n }\n return result;\n } catch (error) {\n const rawMessage = error instanceof Error ? error.message : 'Unknown error';\n requestLogger.error('Tool execution failed', error instanceof Error ? error : undefined);\n if (config.auditLogger) {\n emitToolAudit({\n auditLogger: config.auditLogger,\n toolName: config.toolName,\n ctx: requestContext,\n outcome: 'error',\n durationMs: getTimeProvider().now() - execStartTime,\n nearMiss: invocation.nearMiss,\n });\n }\n // Closes a secret-leak path: adapter SDKs commonly echo offending\n // credentials in their error messages (e.g. Anthropic's\n // AuthenticationError carries `sk-ant-api03-…` substrings; fetch\n // wrappers can echo Authorization headers). The success branch above\n // runs sanitizeToolResult; the exception path must too.\n const installError = mapInstallModuleError(error, requestLogger);\n if (installError !== undefined) return installError;\n const sanitized = sanitizeOutput(rawMessage, requestLogger);\n return internalError(sanitized, requestContext.requestId);\n }\n}\n\n/**\n * Creates a secure handler factory with shared configuration.\n * Useful for registering multiple tools with the same security settings.\n *\n * @param sharedConfig - Shared security configuration\n * @returns Factory function for creating secure handlers\n */\nexport function createSecureHandlerFactory(\n sharedConfig: Omit<SecureHandlerConfig, 'toolName'>\n): (toolName: string, handler: ToolHandler | ContextAwareHandler) => ToolHandler {\n return (toolName: string, handler: ToolHandler | ContextAwareHandler) =>\n createSecureHandler(handler, { ...sharedConfig, toolName });\n}\n","/**\n * nexus-agents/mcp - MCP Notification Helper\n *\n * Routes operator-facing orchestration events through the existing logger\n * to stderr in server mode, leaving stdout for JSON-RPC frames.\n *\n * Also provides progress notification support via AsyncLocalStorage\n * for resetting client-side request timeouts (MCP SDK resetTimeoutOnProgress).\n *\n * @module mcp/mcp-notifier\n * (Source: Issue #973, #974 — Claude Code Observability)\n * (Source: Issue #1108 — Progress heartbeat timeout reset)\n * (Source: Issue #5167 — Migrate operator output off MCP Logging)\n */\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { createLogger, getErrorMessage } from '../core/index.js';\n\n/**\n * Legacy MCP logging level names retained for API compatibility.\n */\nexport type McpLogLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error';\n\n/**\n * Operator event logger used by MCP tools.\n */\nexport interface IMcpNotifier {\n /** Log info-level operator event (key orchestration events) */\n info(logger: string, data: Record<string, unknown>): void;\n /** Log debug-level operator event (detailed execution steps) */\n debug(logger: string, data: Record<string, unknown>): void;\n /** Log warning-level operator event */\n warn(logger: string, data: Record<string, unknown>): void;\n}\n\nconst internalLogger = createLogger({ component: 'mcp-notifier' });\n\n/**\n * Creates an operator notifier backed by the existing logger.\n * Server startup configures the logger destination as stderr. The server\n * argument is retained for compatibility; no MCP Logging messages are sent.\n * Logging failures never propagate to tool execution.\n */\nexport function createMcpNotifier(_server: McpServer): IMcpNotifier {\n function log(\n level: 'info' | 'debug' | 'warn',\n logger: string,\n data: Record<string, unknown>\n ): void {\n try {\n internalLogger[level](logger, data);\n } catch (error: unknown) {\n try {\n internalLogger.debug('Failed to log operator event', {\n level,\n logger,\n error: getErrorMessage(error),\n });\n } catch {\n // An unavailable stderr sink cannot report its own failure. Keep\n // operator logging from breaking tools or falling back to stdout.\n }\n }\n }\n\n return {\n info: (logger, data) => {\n log('info', logger, data);\n },\n debug: (logger, data) => {\n log('debug', logger, data);\n },\n warn: (logger, data) => {\n log('warn', logger, data);\n },\n };\n}\n\n/**\n * No-op notifier for when MCP server is not available.\n */\nexport const NOOP_NOTIFIER: IMcpNotifier = {\n info: () => undefined,\n debug: () => undefined,\n warn: () => undefined,\n};\n\n// ============================================================================\n// Progress Notification Support (MCP SDK resetTimeoutOnProgress)\n// ============================================================================\n\n/**\n * Callback to send a progress notification to the MCP client.\n * When the client sets resetTimeoutOnProgress=true, each notification\n * resets the client's 60s request timeout.\n */\nexport type ProgressSender = (progress: number, total?: number) => void;\n\n/**\n * Progress context stored via AsyncLocalStorage.\n * Set by toSdkCallbackWithProgress when a progressToken is available.\n */\nexport interface ProgressContext {\n readonly progressToken: string | number;\n readonly sendNotification: ProgressSender;\n}\n\n/**\n * AsyncLocalStorage for MCP progress context.\n * Allows withProgressHeartbeat to access the progress sender without\n * threading it through the entire middleware chain.\n */\nexport const progressContextStorage = new AsyncLocalStorage<ProgressContext>();\n\n// ============================================================================\n// Abort Signal Support (MCP SDK cancellation)\n// ============================================================================\n\n/**\n * AsyncLocalStorage for MCP abort signal.\n * Set by toSdkCallback when the SDK provides an AbortSignal.\n * Allows middleware (e.g., TimeoutGuard) to race client cancellation\n * alongside server-side timeouts.\n */\nexport const abortSignalStorage = new AsyncLocalStorage<AbortSignal>();\n\n/**\n * Wraps an async operation with periodic heartbeat notifications.\n *\n * When a progressToken is available (via AsyncLocalStorage from the MCP\n * request handler), sends real `notifications/progress` that reset the\n * client's request timeout (MCP SDK resetTimeoutOnProgress feature).\n *\n * Also logs operator heartbeats at debug level.\n *\n * @param toolName - Name of the tool for notification context\n * @param notifier - MCP notifier instance\n * @param operation - The async operation to wrap\n * @param intervalMs - Heartbeat interval (default: 15000ms)\n * @returns The operation result\n */\nexport async function withProgressHeartbeat<T>(\n toolName: string,\n notifier: IMcpNotifier,\n operation: () => Promise<T>,\n intervalMs = 15_000\n): Promise<T> {\n const startTime = Date.now();\n let beatCount = 0;\n const progressCtx = progressContextStorage.getStore();\n\n const timer = setInterval(() => {\n beatCount++;\n const elapsed = Math.round((Date.now() - startTime) / 1000);\n\n // Send real progress notification if client provided progressToken\n if (progressCtx !== undefined) {\n progressCtx.sendNotification(beatCount);\n }\n\n // Log the operator heartbeat independently of client progress\n notifier.debug(toolName, {\n event: 'heartbeat',\n elapsedSeconds: elapsed,\n beatCount,\n hasProgressToken: progressCtx !== undefined,\n });\n }, intervalMs);\n\n try {\n return await operation();\n } finally {\n clearInterval(timer);\n }\n}\n","/**\n * nexus-agents/mcp - Tool Wrapper Helper\n *\n * Provides a convenient wrapper for MCP tools that automatically applies\n * the middleware chain with timeout protection (CVE-2026-0621 mitigation).\n *\n * @module mcp/middleware/tool-wrapper\n * (Source: Issue #271, CVE-2026-0621 mitigation)\n */\n\nimport { randomUUID } from 'node:crypto';\nimport { appendFileSync, existsSync, mkdirSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\n\nimport type { ILogger } from '../../core/index.js';\nimport type { TimeoutConfig, SecurityConfig } from '../../config/schemas.js';\nimport type { IPolicyFirewall, ExecutionMode } from './policy.js';\nimport type { RateLimiterConfig } from './rate-limiter.js';\nimport { RateLimiter } from './rate-limiter.js';\nimport { VERSION } from '../../version.js';\nimport {\n withMiddleware,\n createMiddlewareFactory,\n type ToolHandler,\n type ContextAwareToolHandler,\n type MiddlewareChainConfig,\n} from './middleware-chain.js';\nimport { MCP_TIMEOUTS, resolveToolClassGuardMs } from '../../config/timeouts.js';\nimport {\n progressContextStorage,\n abortSignalStorage,\n type ProgressContext,\n} from '../mcp-notifier.js';\nimport { createLogger as createInternalLogger, getErrorMessage } from '../../core/index.js';\nimport { getNexusDataDir } from '../../config/nexus-data-dir.js';\n\n/**\n * Default timeout configuration.\n * Values sourced from config/timeouts.ts (Issue #984).\n */\nexport const DEFAULT_TIMEOUT_CONFIG: TimeoutConfig = {\n defaultTimeoutMs: MCP_TIMEOUTS.defaultMs,\n maxTimeoutMs: MCP_TIMEOUTS.maxMs,\n enableLogging: true,\n uriValidation: true,\n};\n\n/**\n * Default per-tool timeout overrides.\n * Sourced from config/timeouts.ts (Issue #984).\n */\nexport const DEFAULT_TOOL_TIMEOUTS: Record<string, number> = {\n ...MCP_TIMEOUTS.perTool,\n};\n\n/**\n * Resolves the timeout for a specific tool — the central resolution chokepoint\n * (#3734). Lookup chain:\n * 1. explicit override (caller-supplied),\n * 2. security config `perToolTimeout`,\n * 3. the operation-class runaway-guard for the tool's {@link TOOL_CLASS}\n * classification (or {@link DEFAULT_OPERATION_CLASS} = 300s if unclassified),\n * honoring `NEXUS_TIMEOUT_MULTIPLIER` + per-class env overrides.\n *\n * The class-guard step replaces the old `DEFAULT_TOOL_TIMEOUTS` literal lookup\n * AND the punitive 60s global default — every tool now gets a generous\n * non-punitive runaway-guard.\n * (Issue #657 — per-tool timeout; #3734 — central class authority)\n */\nexport function getToolTimeout(\n toolName: string,\n security?: SecurityConfig,\n explicitMs?: number\n): number {\n // Explicit override takes highest priority.\n if (explicitMs !== undefined) {\n return explicitMs;\n }\n // Security config per-tool overrides still win over the class default.\n const perToolConfig = security?.timeout?.perToolTimeout;\n const perToolMs = perToolConfig?.[toolName];\n if (perToolMs !== undefined) {\n return perToolMs;\n }\n // Class-derived runaway-guard (multiplier + per-class env overrides applied).\n return resolveToolClassGuardMs(toolName);\n}\n\n/**\n * Configuration for creating a tool factory.\n */\nexport interface ToolFactoryConfig {\n /** Logger instance */\n logger?: ILogger | undefined;\n /** Security configuration (includes timeout config) */\n security?: SecurityConfig | undefined;\n /** Policy firewall instance */\n policyFirewall?: IPolicyFirewall | undefined;\n /** Rate limiter configuration */\n rateLimiter?: RateLimiterConfig | RateLimiter | undefined;\n /** Allowed paths for file operations */\n allowedPaths?: readonly string[] | undefined;\n}\n\n/**\n * Per-tool configuration options.\n */\nexport interface ToolWrapperOptions {\n /** Execution mode for policy evaluation (default: the registry's process-wide mode, #6431) */\n executionMode?: ExecutionMode | undefined;\n /** Custom timeout in ms (overrides default) */\n timeoutMs?: number | undefined;\n /** Skip timeout protection (use sparingly) */\n skipTimeout?: boolean | undefined;\n /** Skip rate limiting */\n skipRateLimit?: boolean | undefined;\n}\n\n/**\n * Gets timeout configuration from security config or uses defaults.\n */\nfunction getTimeoutConfig(\n security?: SecurityConfig,\n overrideMs?: number\n): MiddlewareChainConfig['timeout'] {\n const timeoutConfig = security?.timeout ?? DEFAULT_TIMEOUT_CONFIG;\n\n return {\n defaultTimeoutMs: overrideMs ?? timeoutConfig.defaultTimeoutMs,\n maxTimeoutMs: timeoutConfig.maxTimeoutMs,\n enableLogging: timeoutConfig.enableLogging,\n };\n}\n\n/**\n * Creates a tool factory with shared configuration.\n *\n * This factory produces wrapped handlers that include timeout protection,\n * rate limiting, and other middleware as configured.\n *\n * @example\n * ```typescript\n * const wrapTool = createToolFactory({\n * security: appConfig.security,\n * rateLimiter: { capacity: 100, refillRate: 10 },\n * });\n *\n * const handler = wrapTool('my_tool', async (args) => {\n * // Your tool logic here\n * return { content: [{ type: 'text', text: 'Done' }] };\n * });\n * ```\n */\nexport function createToolFactory(\n config: ToolFactoryConfig\n): (\n toolName: string,\n handler: ContextAwareToolHandler | ToolHandler,\n options?: ToolWrapperOptions\n) => ToolHandler {\n const { security, policyFirewall, rateLimiter, allowedPaths, logger } = config;\n\n return (toolName, handler, options) => {\n const skip = {\n timeout: options?.skipTimeout,\n rateLimit: options?.skipRateLimit,\n };\n\n const chainConfig: Omit<MiddlewareChainConfig, 'toolName'> = {\n logger,\n policyFirewall,\n // Unset falls through to the registry's process-wide mode (#6431).\n executionMode: options?.executionMode,\n allowedPaths,\n rateLimiter,\n timeout: skip.timeout === true ? undefined : getTimeoutConfig(security, options?.timeoutMs),\n skip,\n };\n\n return withMiddleware(toolName, handler, chainConfig);\n };\n}\n\n/**\n * Wraps a single tool handler with timeout protection.\n *\n * This is a convenience function for simple cases where you don't need\n * the full factory setup.\n *\n * @example\n * ```typescript\n * const handler = wrapToolWithTimeout('my_tool', async (args) => {\n * return { content: [{ type: 'text', text: 'Done' }] };\n * });\n * ```\n */\nexport function wrapToolWithTimeout(\n toolName: string,\n handler: ContextAwareToolHandler | ToolHandler,\n options?: {\n timeoutMs?: number;\n logger?: ILogger;\n }\n): ToolHandler {\n return withMiddleware(toolName, handler, {\n timeout: getTimeoutConfig(undefined, options?.timeoutMs),\n logger: options?.logger,\n });\n}\n\n/** Shape of the MCP SDK's extra._meta for progress tokens. */\ninterface SdkMeta {\n readonly progressToken?: string | number;\n}\n\n/** Shape of the MCP SDK's extra object passed to tool handlers. */\ninterface SdkExtra {\n readonly _meta?: SdkMeta;\n readonly signal?: AbortSignal;\n readonly sendNotification?: (notification: {\n method: string;\n params?: Record<string, unknown>;\n }) => Promise<void>;\n}\n\nconst wrapperLogger = createInternalLogger({ component: 'tool-wrapper' });\n\n/** Extract progress context from MCP SDK extra if progressToken present. */\nfunction extractProgressContext(extra: unknown): ProgressContext | undefined {\n const sdk = extra as SdkExtra | undefined;\n const token = sdk?._meta?.progressToken;\n const sendFn = sdk?.sendNotification;\n if (token === undefined || sendFn === undefined) return undefined;\n\n return {\n progressToken: token,\n sendNotification: (progress: number, total?: number) => {\n const params: Record<string, unknown> = {\n progressToken: token,\n progress,\n };\n if (total !== undefined) params['total'] = total;\n sendFn({ method: 'notifications/progress', params }).catch((err: unknown) => {\n wrapperLogger.debug('Failed to send progress notification', {\n error: getErrorMessage(err),\n });\n });\n },\n };\n}\n\n/**\n * Runs handler within nested AsyncLocalStorage contexts for progress + abort.\n */\n/** SDK-compatible tool result with optional structuredContent (Issue #1117). */\ntype SdkToolResult = {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n structuredContent?: Record<string, unknown>;\n // Post-#2649: the structured error envelope lives in `_meta` under\n // `nexus-agents/error` (not in `structuredContent`, which is validated\n // against `outputSchema` even on error results).\n _meta?: Record<string, unknown>;\n};\n\nasync function runWithContexts(\n handler: ToolHandler,\n args: unknown,\n progressCtx: ProgressContext | undefined,\n signal: AbortSignal | undefined\n): Promise<SdkToolResult> {\n const run = (): Promise<SdkToolResult> => handler(args);\n return stampBuild(await runInContexts(run, progressCtx, signal));\n}\n\nfunction runInContexts(\n run: () => Promise<SdkToolResult>,\n progressCtx: ProgressContext | undefined,\n signal: AbortSignal | undefined\n): Promise<SdkToolResult> {\n // Nest contexts: abort signal outer, progress inner\n if (signal !== undefined && progressCtx !== undefined) {\n return abortSignalStorage.run(signal, () => progressContextStorage.run(progressCtx, run));\n }\n if (signal !== undefined) {\n return abortSignalStorage.run(signal, run);\n }\n if (progressCtx !== undefined) {\n return progressContextStorage.run(progressCtx, run);\n }\n return run();\n}\n\n/**\n * Adapts a ToolHandler to the MCP SDK's expected callback signature.\n *\n * Extracts progressToken and AbortSignal from extra, runs the handler\n * within AsyncLocalStorage contexts so middleware can access them.\n *\n * @param handler - Our internal ToolHandler\n * @returns SDK-compatible callback function\n */\nexport function toSdkCallback(\n handler: ToolHandler\n): (args: unknown, extra: unknown) => Promise<SdkToolResult> {\n return (args: unknown, extra: unknown) => {\n const progressCtx = extractProgressContext(extra);\n const signal = (extra as SdkExtra | undefined)?.signal;\n return runWithContexts(handler, args, progressCtx, signal);\n };\n}\n\n/**\n * `_meta` key naming the build that produced a tool result (#5008).\n *\n * The MCP server a client is talking to is routinely a pinned global install\n * rather than the working tree, so a result read as evidence about \"the\n * current code\" could be answering from a months-old build with nothing in the\n * response to say so.\n *\n * It rides in `_meta` for the same reason the error envelope does (#2649):\n * `structuredContent` is validated against the tool's `outputSchema` with\n * `additionalProperties: false`, so an undeclared field there fails every call\n * with -32602 (#5044/#5045). `_meta` is the spec's out-of-band channel and is\n * never schema-validated.\n *\n * Kept module-private: the tests assert the literal wire name, which pins what\n * a client actually sees rather than agreeing with the constant.\n */\nconst BUILD_META_KEY = 'nexus-agents/build';\n\n/**\n * Attaches the build stamp, preserving any `_meta` the handler already set.\n *\n * Applied inside `runWithContexts` rather than in the result factories or in\n * either SDK adapter. There are TWO adapters — `toSdkCallback` and\n * `toSdkCallbackWithTimeoutCheck`, the latter used by `consensus_vote`,\n * `orchestrate` and `run_workflow` — and stamping in one of them left the other\n * three tools unstamped. `runWithContexts` is what both call, on the ordinary\n * and the budget-mismatch path alike, so it is the actual chokepoint. A tool\n * that assembles its result by hand is stamped here too.\n */\nfunction stampBuild(result: SdkToolResult): SdkToolResult {\n return {\n ...result,\n _meta: { ...result._meta, [BUILD_META_KEY]: { version: VERSION } },\n };\n}\n\n/**\n * MCP SDK client default request timeout. Matches `DEFAULT_REQUEST_TIMEOUT_MSEC`\n * in `@modelcontextprotocol/sdk` (`shared/protocol.js`). If a tool's\n * configured server-side budget exceeds this and the client did not send a\n * `progressToken` (i.e. did not pass `onprogress` to its `request()` call),\n * the client kills the request at this threshold regardless of what the\n * server is doing — heartbeats fire but go nowhere.\n *\n * (Source: audit on #2619 / #2631 — root cause of \"MCP error -32001 at 60010ms\")\n */\nexport const MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS = 60_000;\n\n/**\n * Relative path under `$NEXUS_DATA_DIR` for the timeout-mismatch event log.\n * The #2632 WARN was log-only — useful in tail but not queryable. #2703\n * records each mismatch event as a JSONL row keyed by a correlation\n * `eventId` shared with the log entry, so \"did mismatch cause this\n * timeout?\" can be answered by joining the warning's eventId against the\n * recorded outcome — not just counted in aggregate.\n *\n * Schema lives at `docs/architecture/MCP_PROTOCOL.md` (Timeout-mismatch\n * telemetry section).\n */\nexport const TIMEOUT_MISMATCH_TELEMETRY_REL_PATH = 'mcp-telemetry/timeout-mismatch-events.jsonl';\n\n/** A single timeout-mismatch event recorded to the telemetry JSONL. */\nexport interface TimeoutMismatchEvent {\n readonly eventId: string;\n readonly toolName: string;\n readonly configuredTimeoutMs: number;\n readonly mcpSdkDefaultMs: number;\n readonly startedAt: string;\n readonly endedAt: string;\n readonly durationMs: number;\n readonly outcome: 'success' | 'error';\n /** From the post-#2649 structured error envelope when present. */\n readonly errorCategory?: string;\n readonly errorMessage?: string;\n}\n\n/**\n * Cache the \"dir ensured\" flag so we don't re-`existsSync` on every call\n * (closes #2955 site 3, partial). Cached per `dirname(path)` so an\n * operator changing `NEXUS_DATA_DIR` between calls (test/dev only)\n * still works after a process restart. The full async-write part of\n * the perf fix is deferred — `tool-wrapper-budget-check.test.ts` reads\n * the JSONL synchronously after `await callback(...)` and expects the\n * write to be visible, so switching to `fs.promises.appendFile` broke\n * those tests. The dir-cache is the cheaper part of the perf win\n * (skipping 1 existsSync per call); the appendFileSync vs appendFile\n * win can ship later behind a test-helper that awaits pending writes.\n */\nconst ensuredDirs = new Set<string>();\n\n/** Best-effort append — telemetry recording must never fail the user's tool call. */\nfunction appendTimeoutMismatchEvent(event: TimeoutMismatchEvent): void {\n try {\n const path = join(getNexusDataDir(), TIMEOUT_MISMATCH_TELEMETRY_REL_PATH);\n const dir = dirname(path);\n if (!ensuredDirs.has(dir)) {\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true });\n ensuredDirs.add(dir);\n }\n appendFileSync(path, JSON.stringify(event) + '\\n', 'utf-8');\n } catch (err) {\n wrapperLogger.debug('Best-effort timeout-mismatch event recording failed', {\n error: getErrorMessage(err),\n });\n }\n}\n\n/** Pull the post-#2649 errorCategory off an error result's `_meta` envelope. */\nfunction extractErrorCategoryFromResult(result: SdkToolResult): string | undefined {\n const envelope = result._meta?.['nexus-agents/error'];\n if (envelope !== null && typeof envelope === 'object' && 'errorCategory' in envelope) {\n const cat = (envelope as { errorCategory?: unknown }).errorCategory;\n if (typeof cat === 'string') return cat;\n }\n return undefined;\n}\n\n/** First text-content line of an error result, truncated for logging. */\nfunction extractErrorMessageFromResult(result: SdkToolResult): string | undefined {\n const first = result.content[0];\n if (first?.type === 'text' && typeof first.text === 'string') {\n return first.text.slice(0, 500);\n }\n return undefined;\n}\n\ninterface MismatchCallContext {\n readonly log: ILogger;\n readonly handler: ToolHandler;\n readonly args: unknown;\n readonly progressCtx: ProgressContext | undefined;\n readonly signal: AbortSignal | undefined;\n readonly toolName: string;\n readonly configuredTimeoutMs: number;\n}\n\ninterface MismatchOutcome {\n readonly outcome: 'success' | 'error';\n readonly errorCategory?: string;\n readonly errorMessage?: string;\n}\n\n/** Build a complete event record from a finished mismatched call. */\nfunction buildMismatchEvent(\n ctx: MismatchCallContext,\n eventId: string,\n t0: number,\n outcome: MismatchOutcome\n): TimeoutMismatchEvent {\n const t1 = Date.now();\n return {\n eventId,\n toolName: ctx.toolName,\n configuredTimeoutMs: ctx.configuredTimeoutMs,\n mcpSdkDefaultMs: MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS,\n startedAt: new Date(t0).toISOString(),\n endedAt: new Date(t1).toISOString(),\n durationMs: t1 - t0,\n outcome: outcome.outcome,\n ...(outcome.errorCategory !== undefined ? { errorCategory: outcome.errorCategory } : {}),\n ...(outcome.errorMessage !== undefined ? { errorMessage: outcome.errorMessage } : {}),\n };\n}\n\n/** Classify a tool result into a MismatchOutcome (success or structured error). */\nfunction classifyResult(result: SdkToolResult): MismatchOutcome {\n if (result.isError !== true) return { outcome: 'success' };\n const errorCategory = extractErrorCategoryFromResult(result);\n const errorMessage = extractErrorMessageFromResult(result);\n return {\n outcome: 'error',\n ...(errorCategory !== undefined ? { errorCategory } : {}),\n ...(errorMessage !== undefined ? { errorMessage } : {}),\n };\n}\n\n/** Run a mismatched call, recording its outcome to the JSONL. */\nasync function runMismatchedCall(ctx: MismatchCallContext): Promise<SdkToolResult> {\n const eventId = randomUUID();\n const t0 = Date.now();\n ctx.log.warn(\n 'MCP tool budget exceeds client default and no progressToken received — request likely to be killed by client before server-side deadline',\n {\n tool: ctx.toolName,\n eventId,\n configuredTimeoutMs: ctx.configuredTimeoutMs,\n mcpSdkDefaultMs: MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS,\n remediation:\n 'Client should pass `onprogress` and `resetTimeoutOnProgress: true` when calling, or extend `options.timeout`. See docs/architecture/MCP_PROTOCOL.md.',\n }\n );\n try {\n const result = await runWithContexts(ctx.handler, ctx.args, ctx.progressCtx, ctx.signal);\n appendTimeoutMismatchEvent(buildMismatchEvent(ctx, eventId, t0, classifyResult(result)));\n return result;\n } catch (err) {\n appendTimeoutMismatchEvent(\n buildMismatchEvent(ctx, eventId, t0, {\n outcome: 'error',\n errorMessage: getErrorMessage(err).slice(0, 500),\n })\n );\n throw err;\n }\n}\n\n/**\n * Like `toSdkCallback`, but emits a one-shot WARN at invocation start when\n * the configured per-tool TIMEOUT exceeds the MCP SDK client default AND the\n * client did not send a `progressToken`. The call is almost certainly going\n * to die at the client default (~60s) regardless of server-side timeout\n * config or progress heartbeats — surface that at the moment of invocation\n * so operators can spot the mismatch in logs without waiting for the\n * timeout to fire.\n *\n * Wrap a long-running tool (`orchestrate`, `consensus_vote`,\n * `execute_expert`, `run_workflow`) with this instead of plain\n * `toSdkCallback`. Tools whose timeout already fits within\n * `MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS` should keep using `toSdkCallback`.\n *\n * This checks TIME only; it is not a token/spend budget. It was named\n * `toSdkCallbackWithBudgetCheck`, which read as spend enforcement on\n * `run_workflow` when no such enforcement existed (#4754).\n *\n * Each mismatch is **also recorded** to\n * `$NEXUS_DATA_DIR/mcp-telemetry/timeout-mismatch-events.jsonl` with a\n * correlation `eventId` (also surfaced in the WARN log entry) and the\n * call's eventual outcome (`success` / `error` + post-#2649 errorCategory\n * if present). This lets the Epic #2631 gate be answered with data —\n * \"of N mismatches, what fraction ended in a timeout?\" — not just counted\n * in aggregate (#2703).\n *\n * (Source: audit on #2619 / #2631 — observability for client-timeout mismatch)\n */\nexport function toSdkCallbackWithTimeoutCheck(\n handler: ToolHandler,\n toolName: string,\n configuredTimeoutMs: number,\n logger?: ILogger\n): (args: unknown, extra: unknown) => Promise<SdkToolResult> {\n const log = logger ?? wrapperLogger;\n return (args: unknown, extra: unknown) => {\n const progressCtx = extractProgressContext(extra);\n const signal = (extra as SdkExtra | undefined)?.signal;\n const isMismatch =\n configuredTimeoutMs > MCP_SDK_DEFAULT_REQUEST_TIMEOUT_MS && progressCtx === undefined;\n if (!isMismatch) return runWithContexts(handler, args, progressCtx, signal);\n return runMismatchedCall({\n log,\n handler,\n args,\n progressCtx,\n signal,\n toolName,\n configuredTimeoutMs,\n });\n };\n}\n\n/**\n * Re-export middleware factory for advanced use cases.\n */\nexport { createMiddlewareFactory, withMiddleware };\n","/**\n * nexus-agents/mcp - Timeout Guard Middleware\n *\n * Provides timeout protection for MCP operations to mitigate ReDoS and\n * other denial-of-service vectors. Implements configurable timeouts with\n * proper cleanup and error handling.\n *\n * (Source: CVE-2026-0621, GHSA-8r9q-7v3j-jr4g)\n * (Source: Issue #107)\n *\n * @module mcp/middleware/timeout-guard\n */\n\nimport {\n getErrorMessage,\n createLogger,\n type ILogger,\n type Result,\n ok,\n err,\n getTimeProvider,\n} from '../../core/index.js';\n\n/**\n * Error codes for timeout-related failures.\n */\nexport type TimeoutErrorCode =\n | 'OPERATION_TIMEOUT'\n | 'OPERATION_CANCELLED'\n | 'INVALID_TIMEOUT'\n | 'GUARD_ERROR';\n\n/**\n * Timeout guard error.\n */\nexport interface TimeoutError {\n readonly code: TimeoutErrorCode;\n readonly message: string;\n readonly operation?: string;\n readonly timeoutMs?: number;\n readonly cause?: Error;\n}\n\n/**\n * Configuration for timeout guard.\n */\nexport interface TimeoutGuardConfig {\n /** Default timeout in milliseconds (default: 30000) */\n readonly defaultTimeoutMs?: number;\n /** Maximum allowed timeout in milliseconds (default: 300000) */\n readonly maxTimeoutMs?: number;\n /** Whether to log timeout events (default: true) */\n readonly enableLogging?: boolean;\n /** Logger instance */\n readonly logger?: ILogger;\n}\n\n/**\n * Result of a guarded operation.\n */\nexport interface GuardedResult<T> {\n /** The operation result */\n readonly value: T;\n /** Execution duration in milliseconds */\n readonly durationMs: number;\n /** Whether the operation was near timeout */\n readonly nearTimeout: boolean;\n}\n\n/** Execution options for guarded operations. */\nexport interface ExecuteOptions {\n /** Custom timeout for this operation */\n readonly timeoutMs?: number;\n /** Name for logging/debugging */\n readonly operationName?: string;\n /** Cleanup function to call on timeout */\n readonly onTimeout?: () => void;\n /** AbortSignal for client-initiated cancellation */\n readonly signal?: AbortSignal;\n}\n\n// Canonical source: config/timeouts.ts (Issue #1046)\nimport { TIMEOUT_GUARD } from '../../config/timeouts.js';\n\nconst DEFAULT_TIMEOUT_MS = TIMEOUT_GUARD.defaultMs;\nconst MAX_TIMEOUT_MS = TIMEOUT_GUARD.maxMs;\nconst NEAR_TIMEOUT_THRESHOLD = TIMEOUT_GUARD.nearTimeoutThreshold;\n\n/** Internal state for tracking timeout. */\ninterface TimeoutState {\n timeoutId: ReturnType<typeof setTimeout> | undefined;\n timedOut: boolean;\n}\n\n/**\n * Creates a timeout error for operation timeout.\n */\nfunction createTimeoutError(operationName: string, timeoutMs: number): TimeoutError {\n return {\n code: 'OPERATION_TIMEOUT',\n message: `Operation '${operationName}' timed out after ${String(timeoutMs)}ms`,\n operation: operationName,\n timeoutMs,\n };\n}\n\n/**\n * Creates a guard error from a caught exception.\n */\nfunction createGuardError(error: unknown, operationName: string): TimeoutError {\n const guardError: TimeoutError = {\n code: 'GUARD_ERROR',\n message: getErrorMessage(error),\n operation: operationName,\n };\n if (error instanceof Error) {\n return { ...guardError, cause: error };\n }\n return guardError;\n}\n\n/**\n * Timeout guard for protecting async operations from hanging.\n *\n * Provides protection against:\n * - ReDoS attacks (CVE-2026-0621)\n * - Slow/hanging external services\n * - Resource exhaustion\n *\n * @example\n * ```typescript\n * const guard = new TimeoutGuard({ defaultTimeoutMs: 5000 });\n *\n * const result = await guard.execute(\n * () => someAsyncOperation(),\n * { operationName: 'process-uri' }\n * );\n *\n * if (result.ok) {\n * console.log('Completed in', result.value.durationMs, 'ms');\n * }\n * ```\n */\nexport class TimeoutGuard {\n private readonly defaultTimeoutMs: number;\n private readonly maxTimeoutMs: number;\n private readonly enableLogging: boolean;\n private readonly logger: ILogger;\n\n constructor(config?: TimeoutGuardConfig) {\n this.defaultTimeoutMs = config?.defaultTimeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.maxTimeoutMs = config?.maxTimeoutMs ?? MAX_TIMEOUT_MS;\n this.enableLogging = config?.enableLogging ?? true;\n this.logger = config?.logger ?? createLogger({ component: 'timeout-guard' });\n }\n\n /** Resolves effective timeout and operation name from options. */\n private resolveOptions(options?: ExecuteOptions): {\n timeoutMs: number;\n operationName: string;\n } {\n return {\n timeoutMs: Math.min(options?.timeoutMs ?? this.defaultTimeoutMs, this.maxTimeoutMs),\n operationName: options?.operationName ?? 'unknown',\n };\n }\n\n /**\n * Executes an async operation with timeout protection.\n */\n async execute<T>(\n operation: () => Promise<T>,\n options?: ExecuteOptions\n ): Promise<Result<GuardedResult<T>, TimeoutError>> {\n const { timeoutMs, operationName } = this.resolveOptions(options);\n\n const validationError = this.validateTimeout(timeoutMs, operationName);\n if (validationError !== null) {\n return err(validationError);\n }\n\n return this.runGuarded(operation, timeoutMs, operationName, options);\n }\n\n /** Runs the operation with timeout and optional abort signal. */\n private async runGuarded<T>(\n operation: () => Promise<T>,\n timeoutMs: number,\n operationName: string,\n options?: ExecuteOptions\n ): Promise<Result<GuardedResult<T>, TimeoutError>> {\n this.logStart(operationName, timeoutMs);\n const startTime = getTimeProvider().now();\n const state: TimeoutState = { timeoutId: undefined, timedOut: false };\n\n try {\n const result = await this.runWithTimeout(\n operation,\n timeoutMs,\n state,\n options?.onTimeout,\n options?.signal\n );\n return this.handleSuccess(result, startTime, timeoutMs, operationName);\n } catch {\n const cancelled = options?.signal?.aborted === true && !state.timedOut;\n return err(\n this.handleFailure(state.timedOut, operationName, timeoutMs, startTime, cancelled)\n );\n } finally {\n if (state.timeoutId !== undefined) {\n clearTimeout(state.timeoutId);\n }\n }\n }\n\n private validateTimeout(timeoutMs: number, operationName: string): TimeoutError | null {\n if (timeoutMs <= 0) {\n return {\n code: 'INVALID_TIMEOUT',\n message: `Invalid timeout: ${String(timeoutMs)}ms`,\n operation: operationName,\n };\n }\n return null;\n }\n\n private logStart(operationName: string, timeoutMs: number): void {\n if (this.enableLogging) {\n this.logger.debug('Starting guarded operation', { operation: operationName, timeoutMs });\n }\n }\n\n private async runWithTimeout<T>(\n operation: () => Promise<T>,\n timeoutMs: number,\n state: TimeoutState,\n onTimeout?: () => void,\n signal?: AbortSignal\n ): Promise<T> {\n const timeoutPromise = new Promise<never>((_resolve, reject) => {\n state.timeoutId = setTimeout(() => {\n state.timedOut = true;\n onTimeout?.();\n reject(new Error(`Operation timed out after ${String(timeoutMs)}ms`));\n }, timeoutMs);\n });\n\n const promises: Array<Promise<T>> = [operation(), timeoutPromise];\n\n // Race against client AbortSignal if provided\n if (signal !== undefined && !signal.aborted) {\n const abortPromise = new Promise<never>((_resolve, reject) => {\n signal.addEventListener(\n 'abort',\n () => {\n reject(new Error('Operation cancelled by client'));\n },\n { once: true }\n );\n });\n promises.push(abortPromise);\n }\n\n return Promise.race(promises);\n }\n\n private handleSuccess<T>(\n result: T,\n startTime: number,\n timeoutMs: number,\n operationName: string\n ): Result<GuardedResult<T>, TimeoutError> {\n const durationMs = getTimeProvider().now() - startTime;\n const nearTimeout = durationMs > timeoutMs * NEAR_TIMEOUT_THRESHOLD;\n\n if (nearTimeout && this.enableLogging) {\n this.logger.warn('Operation completed near timeout threshold', {\n operation: operationName,\n durationMs,\n timeoutMs,\n thresholdPercent: Math.round((durationMs / timeoutMs) * 100),\n });\n }\n\n if (this.enableLogging) {\n this.logger.debug('Guarded operation completed', { operation: operationName, durationMs });\n }\n\n return ok({ value: result, durationMs, nearTimeout });\n }\n\n private handleFailure(\n timedOut: boolean,\n operationName: string,\n timeoutMs: number,\n startTime: number,\n cancelled = false\n ): TimeoutError {\n const durationMs = getTimeProvider().now() - startTime;\n\n if (cancelled) {\n this.logger.info('Operation cancelled by client', {\n operation: operationName,\n durationMs,\n });\n return {\n code: 'OPERATION_CANCELLED',\n message: `Operation '${operationName}' cancelled by client`,\n operation: operationName,\n };\n }\n\n if (timedOut) {\n this.logger.error('Operation timed out', undefined, {\n operation: operationName,\n timeoutMs,\n durationMs,\n });\n return createTimeoutError(operationName, timeoutMs);\n }\n\n return createGuardError(new Error('Unknown error'), operationName);\n }\n\n /**\n * Creates a guarded version of an async function.\n */\n guard<TArgs extends unknown[], TResult>(\n fn: (...args: TArgs) => Promise<TResult>,\n options?: { readonly timeoutMs?: number; readonly operationName?: string }\n ): (...args: TArgs) => Promise<Result<GuardedResult<TResult>, TimeoutError>> {\n return async (...args: TArgs): Promise<Result<GuardedResult<TResult>, TimeoutError>> => {\n return this.execute(() => fn(...args), options);\n };\n }\n}\n\n/**\n * URI validation utilities to complement timeout protection.\n */\nexport const UriValidation = {\n MAX_URI_LENGTH: 8192,\n MAX_TEMPLATE_DEPTH: 3,\n SUSPICIOUS_PATTERN: /\\{[+#./;?&]?[^}]*\\*\\}.*\\{[+#./;?&]?[^}]*\\*\\}|\\{(?:[^{}]*\\{){3,}/,\n\n validate(uri: string): Result<string, TimeoutError> {\n if (uri.length > this.MAX_URI_LENGTH) {\n return err({\n code: 'GUARD_ERROR',\n message: `URI exceeds maximum length: ${String(uri.length)} > ${String(this.MAX_URI_LENGTH)}`,\n operation: 'uri-validation',\n });\n }\n\n if (this.SUSPICIOUS_PATTERN.test(uri)) {\n return err({\n code: 'GUARD_ERROR',\n message: 'URI contains suspicious patterns that may cause performance issues',\n operation: 'uri-validation',\n });\n }\n\n return ok(uri);\n },\n\n sanitize(uri: string): string {\n const sanitized = uri.slice(0, this.MAX_URI_LENGTH);\n let depth = 0;\n let result = '';\n\n for (const char of sanitized) {\n if (char === '{') {\n depth++;\n if (depth > this.MAX_TEMPLATE_DEPTH) continue;\n } else if (char === '}') {\n if (depth > this.MAX_TEMPLATE_DEPTH) {\n depth--;\n continue;\n }\n depth--;\n }\n result += char;\n }\n\n return result;\n },\n};\n\n/**\n * Creates a timeout guard with default MCP-appropriate settings.\n */\nexport function createDefaultTimeoutGuard(logger?: ILogger): TimeoutGuard {\n const config: TimeoutGuardConfig = {\n defaultTimeoutMs: DEFAULT_TIMEOUT_MS,\n maxTimeoutMs: MAX_TIMEOUT_MS,\n enableLogging: true,\n };\n if (logger !== undefined) {\n return new TimeoutGuard({ ...config, logger });\n }\n return new TimeoutGuard(config);\n}\n","/**\n * nexus-agents/mcp - Centralized Middleware Chain\n *\n * Provides a composable middleware chain for MCP tools with guaranteed\n * execution order: metrics → audit → rate-limit → validation → policy →\n * timeout → handler. There is NO auth stage in this chain: authentication\n * lives in `auth-handler.ts` and is wired separately.\n *\n * `policy` (PolicyFirewall) is the only authorization stage this chain can\n * mount, and the production wrapper (`wrapToolWithTimeout`) does not mount\n * even that — authorization for a registered tool runs inside\n * `createSecureHandler` against the process PolicyFirewall. The ClawGuard\n * access-policy stage that used to sit after `policy` was deleted in #5107\n * (#5022 decision, epic #5105): it read its policy from an AsyncLocalStorage\n * store no inbound request ever populated, and the deriver that filled that\n * store went in #5108. The chain reports the stages it built at debug level so\n * that composition is observable rather than inferred;\n * `single-authorization-mechanism.test.ts` pins it.\n *\n * @module mcp/middleware/middleware-chain\n * (Source: Issue #189 - Centralized MCP middleware chain)\n */\n\nimport type { z } from 'zod';\nimport type { ILogger } from '../../core/index.js';\nimport { createLogger, getTimeProvider } from '../../core/index.js';\nimport { validateToolInput } from './validation.js';\nimport { RateLimiter, type RateLimiterConfig } from './rate-limiter.js';\nimport { type IPolicyFirewall, type ExecutionMode, createPolicyContext } from './policy.js';\nimport { TimeoutGuard, type TimeoutGuardConfig } from './timeout-guard.js';\nimport { getGlobalExecutionMode } from './policy-registry.js';\nimport { MCP_TIMEOUTS } from '../../config/timeouts.js';\nimport {\n createRequestContext,\n contextForLogging,\n runWithRequestContext,\n serverCallerInfo,\n type RequestContext,\n} from './request-context.js';\nimport { createMetricsMiddleware } from './tool-metrics.js';\nimport { abortSignalStorage } from '../mcp-notifier.js';\nimport { toolStructuredError } from '../tools/tool-result.js';\nimport type { ErrorCategory } from '../error-envelope.js';\n\n/**\n * MCP tool result type.\n *\n * This interface is structurally compatible with the MCP SDK's CallToolResult.\n * The content array accepts text content for simplicity, while remaining\n * compatible with the SDK's broader ContentBlock type at runtime.\n */\nexport interface ToolResult {\n /** Content blocks returned by the tool (text content) */\n content: Array<{ type: 'text'; text: string }>;\n /** Whether this represents an error result */\n isError?: boolean;\n /** Structured output for SDK outputSchema validation (Issue #1117) */\n structuredContent?: Record<string, unknown>;\n /** Out-of-band metadata — carries the #2649 error envelope on errors. */\n _meta?: Record<string, unknown>;\n}\n\n/**\n * Middleware context passed through the chain.\n */\nexport interface MiddlewareContext {\n /** Unique request ID for tracing */\n readonly requestContext: RequestContext;\n /** Logger with request context */\n readonly logger: ILogger;\n /** Validated arguments (set after validation middleware) */\n validatedArgs?: unknown;\n}\n\n/**\n * Middleware function signature.\n * Each middleware receives the context and a next function to call.\n */\nexport type Middleware = (\n args: unknown,\n ctx: MiddlewareContext,\n next: (args: unknown, ctx: MiddlewareContext) => Promise<ToolResult>\n) => Promise<ToolResult>;\n\n/**\n * Configuration for the middleware chain.\n */\nexport interface MiddlewareChainConfig {\n /** Tool name for logging and policy evaluation */\n toolName: string;\n /** Zod schema for input validation (optional) */\n schema?: z.ZodType;\n /** Policy firewall instance (optional) */\n policyFirewall?: IPolicyFirewall | undefined;\n /** Execution mode for policy evaluation (default: the registry's process-wide mode, #6431) */\n executionMode?: ExecutionMode | undefined;\n /** Allowed paths for file operations */\n allowedPaths?: readonly string[] | undefined;\n /** Rate limiter configuration (optional) */\n rateLimiter?: RateLimiterConfig | RateLimiter | undefined;\n /** Timeout configuration (optional) */\n timeout?: TimeoutGuardConfig | undefined;\n /** Logger instance (optional) */\n logger?: ILogger | undefined;\n /** Skip specific middleware steps */\n skip?: MiddlewareSkipConfig | undefined;\n}\n\n/**\n * Configuration for skipping middleware steps.\n */\nexport interface MiddlewareSkipConfig {\n validation?: boolean | undefined;\n policy?: boolean | undefined;\n rateLimit?: boolean | undefined;\n timeout?: boolean | undefined;\n audit?: boolean | undefined;\n}\n\n/**\n * Tool handler function signature.\n */\nexport type ToolHandler = (args: unknown) => Promise<ToolResult>;\n\n/**\n * Context-aware handler that receives middleware context.\n */\nexport type ContextAwareToolHandler = (\n args: unknown,\n ctx: MiddlewareContext\n) => Promise<ToolResult>;\n\n/**\n * Creates an error result with the structured error envelope (#2649).\n * Each middleware passes the category that matches its failure mode.\n */\nfunction errorResult(category: ErrorCategory, message: string, requestId: string): ToolResult {\n return toolStructuredError({\n errorCategory: category,\n message: `${message} (request: ${requestId})`,\n });\n}\n\n/**\n * Creates validation middleware.\n */\nfunction createValidationMiddleware(schema: z.ZodType): Middleware {\n return async (args, ctx, next) => {\n const result = validateToolInput(schema, args);\n if (!result.ok) {\n ctx.logger.warn('Validation failed', {\n error: result.error.message,\n });\n return errorResult(\n 'validation',\n `Validation error: ${result.error.message}`,\n ctx.requestContext.requestId\n );\n }\n ctx.validatedArgs = result.value;\n return next(result.value, ctx);\n };\n}\n\n/**\n * Creates policy middleware.\n */\nfunction createPolicyMiddleware(\n firewall: IPolicyFirewall,\n toolName: string,\n mode: ExecutionMode,\n allowedPaths?: readonly string[]\n): Middleware {\n return async (args, ctx, next) => {\n const policyCtx = createPolicyContext(toolName, args, {\n mode,\n ...(allowedPaths !== undefined && { allowedPaths }),\n });\n const decision = firewall.evaluate(policyCtx);\n\n if (!decision.allowed) {\n ctx.logger.warn('Policy denied', {\n reason: decision.reason,\n ruleName: decision.ruleName,\n });\n return errorResult(\n 'permission',\n `Policy denied: ${decision.reason}`,\n ctx.requestContext.requestId\n );\n }\n ctx.logger.debug('Policy check passed', { reason: decision.reason });\n return next(args, ctx);\n };\n}\n\n/**\n * Creates rate limit middleware.\n */\nfunction createRateLimitMiddleware(limiter: RateLimiter): Middleware {\n return async (args, ctx, next) => {\n const acquired = limiter.tryAcquire();\n if (!acquired) {\n const state = limiter.getState();\n ctx.logger.warn('Rate limit exceeded', {\n nextTokenMs: state.nextTokenMs,\n });\n return errorResult(\n 'transient',\n `Rate limit exceeded. Try again in ${String(state.nextTokenMs)}ms`,\n ctx.requestContext.requestId\n );\n }\n return next(args, ctx);\n };\n}\n\n/**\n * Creates timeout middleware.\n * Reads AbortSignal from AsyncLocalStorage for client cancellation support.\n */\nfunction createTimeoutMiddleware(guard: TimeoutGuard, toolName: string): Middleware {\n return async (args, ctx, next) => {\n const signal = abortSignalStorage.getStore();\n const result = await guard.execute(() => next(args, ctx), {\n operationName: toolName,\n ...(signal !== undefined ? { signal } : {}),\n });\n\n if (!result.ok) {\n ctx.logger.error('Operation timed out', undefined, {\n code: result.error.code,\n timeoutMs: result.error.timeoutMs,\n });\n // #3726 discoverability: append the per-tool hint (e.g. \"retry in async\n // job-mode\") so a sync long-running tool that hits its ceiling tells the\n // caller how to escape it. Absent hint → message unchanged.\n const hint = MCP_TIMEOUTS.perToolTimeoutHint[toolName];\n const message = hint !== undefined ? `${result.error.message} ${hint}` : result.error.message;\n // Timeout — transient, a retry with more headroom may succeed.\n return errorResult('transient', message, ctx.requestContext.requestId);\n }\n\n if (result.value.nearTimeout) {\n ctx.logger.warn('Operation completed near timeout threshold', {\n durationMs: result.value.durationMs,\n });\n }\n\n return result.value.value;\n };\n}\n\n/**\n * Creates audit middleware that logs start/end of request.\n */\nfunction createAuditMiddleware(): Middleware {\n return async (args, ctx, next) => {\n const startTime = getTimeProvider().now();\n ctx.logger.info('Tool invocation started');\n\n try {\n const result = await next(args, ctx);\n const durationMs = getTimeProvider().now() - startTime;\n\n if (result.isError === true) {\n ctx.logger.warn('Tool execution completed with error', { durationMs });\n } else {\n ctx.logger.info('Tool execution completed', { durationMs });\n }\n return result;\n } catch (error) {\n const durationMs = getTimeProvider().now() - startTime;\n const message = error instanceof Error ? error.message : 'Unknown error';\n ctx.logger.error('Tool execution failed', error instanceof Error ? error : undefined, {\n durationMs,\n });\n return errorResult('internal', `Internal error: ${message}`, ctx.requestContext.requestId);\n }\n };\n}\n\n/**\n * Composes multiple middleware functions into a single chain.\n */\nfunction composeMiddleware(middlewares: Middleware[]): Middleware {\n return (args, ctx, finalHandler) => {\n const dispatch = (index: number, currentArgs: unknown): Promise<ToolResult> => {\n if (index >= middlewares.length) {\n return finalHandler(currentArgs, ctx);\n }\n const middleware = middlewares[index];\n if (middleware === undefined) {\n return finalHandler(currentArgs, ctx);\n }\n return middleware(currentArgs, ctx, (nextArgs) => dispatch(index + 1, nextArgs));\n };\n return dispatch(0, args);\n };\n}\n\n/**\n * A stage the chain mounts, named so the built stack can be reported.\n *\n * `policy` is the one authorization stage. Adding a name here that gates a\n * call is a #5022-class decision (which boundary authorizes, and with what),\n * not a refactor — `single-authorization-mechanism.test.ts` pins the list.\n */\ntype MiddlewareStageName = 'metrics' | 'audit' | 'rateLimit' | 'validation' | 'policy' | 'timeout';\n\ninterface MiddlewareStage {\n readonly name: MiddlewareStageName;\n readonly middleware: Middleware;\n}\n\n/** Helper: adds audit middleware if not skipped */\nfunction addAuditMiddleware(stages: MiddlewareStage[], skip: MiddlewareSkipConfig): void {\n if (skip.audit !== true) {\n stages.push({ name: 'audit', middleware: createAuditMiddleware() });\n }\n}\n\n/** Helper: adds rate limit middleware if configured */\nfunction addRateLimitMiddleware(\n stages: MiddlewareStage[],\n config: MiddlewareChainConfig,\n skip: MiddlewareSkipConfig\n): void {\n if (skip.rateLimit !== true && config.rateLimiter !== undefined) {\n const limiter =\n config.rateLimiter instanceof RateLimiter\n ? config.rateLimiter\n : new RateLimiter(config.rateLimiter);\n stages.push({ name: 'rateLimit', middleware: createRateLimitMiddleware(limiter) });\n }\n}\n\n/** Helper: adds validation middleware if schema provided */\nfunction addValidationMiddleware(\n stages: MiddlewareStage[],\n config: MiddlewareChainConfig,\n skip: MiddlewareSkipConfig\n): void {\n if (skip.validation !== true && config.schema !== undefined) {\n stages.push({ name: 'validation', middleware: createValidationMiddleware(config.schema) });\n }\n}\n\n/** Helper: adds policy middleware if configured */\nfunction addPolicyMiddleware(\n stages: MiddlewareStage[],\n config: MiddlewareChainConfig,\n skip: MiddlewareSkipConfig\n): void {\n if (skip.policy !== true && config.policyFirewall !== undefined) {\n const mode = config.executionMode ?? getGlobalExecutionMode();\n stages.push({\n name: 'policy',\n middleware: createPolicyMiddleware(\n config.policyFirewall,\n config.toolName,\n mode,\n config.allowedPaths\n ),\n });\n }\n}\n\n/** Helper: adds timeout middleware if configured */\nfunction addTimeoutMiddleware(\n stages: MiddlewareStage[],\n config: MiddlewareChainConfig,\n skip: MiddlewareSkipConfig\n): void {\n if (skip.timeout !== true && config.timeout !== undefined) {\n const guard = new TimeoutGuard(config.timeout);\n stages.push({ name: 'timeout', middleware: createTimeoutMiddleware(guard, config.toolName) });\n }\n}\n\n/** Helper: builds the middleware stack, in execution order */\nfunction buildMiddlewareStack(config: MiddlewareChainConfig): MiddlewareStage[] {\n const skip = config.skip ?? {};\n const stages: MiddlewareStage[] = [];\n\n stages.push({ name: 'metrics', middleware: createMetricsMiddleware() }); // Tool usage analytics (#1022)\n addAuditMiddleware(stages, skip);\n addRateLimitMiddleware(stages, config, skip);\n addValidationMiddleware(stages, config, skip);\n addPolicyMiddleware(stages, config, skip);\n addTimeoutMiddleware(stages, config, skip);\n\n return stages;\n}\n\n/**\n * Creates a middleware chain with the standard execution order.\n *\n * Order: metrics → audit → rate-limit → validation → policy → timeout → handler\n *\n * Audit wraps everything to capture timing. Rate limit is checked early\n * to reject requests before expensive validation. Timeout wraps the\n * actual handler execution.\n *\n * @param config - Chain configuration\n * @returns A function that wraps handlers with the middleware chain\n */\nexport function createMiddlewareChain(\n config: MiddlewareChainConfig\n): (handler: ContextAwareToolHandler) => ToolHandler {\n const logger = config.logger ?? createLogger({ tool: config.toolName });\n const stages = buildMiddlewareStack(config);\n // The built composition, from the array that is actually composed. This is\n // the record a test (or an operator at debug level) reads to know what sits\n // on the dispatch boundary for a tool — a stage cannot be mounted without\n // appearing here (#5107).\n logger.debug('Middleware chain built', {\n toolName: config.toolName,\n stages: stages.map((stage) => stage.name),\n });\n const composed = composeMiddleware(stages.map((stage) => stage.middleware));\n\n return (handler: ContextAwareToolHandler): ToolHandler => {\n return async (args: unknown): Promise<ToolResult> => {\n // The transport the server connected is what the server measured about\n // the caller (#6795): stdio derives tier 1, and no recorded transport\n // stays unmeasured rather than defaulting to a tier.\n const requestContext = createRequestContext({\n toolName: config.toolName,\n caller: serverCallerInfo(),\n });\n const requestLogger = logger.child(contextForLogging(requestContext));\n const ctx: MiddlewareContext = { requestContext, logger: requestLogger };\n // Publish the context ambiently so inner layers adopt it instead of\n // minting a second one (#4981). Argument threading cannot reach them:\n // createSecureHandler returns a 1-arity function, so the dispatch in\n // `withMiddleware` below drops ctx, and some tools put a 1-arity\n // prerequisite wrapper between the two layers as well.\n return runWithRequestContext(requestContext, () =>\n composed(args, ctx, (finalArgs, finalCtx) => handler(finalArgs, finalCtx))\n );\n };\n };\n}\n\n/**\n * Convenience function to wrap a handler with default middleware.\n *\n * @param toolName - Name of the tool\n * @param handler - The tool handler\n * @param options - Optional middleware configuration\n * @returns Wrapped handler with middleware\n */\nexport function withMiddleware(\n toolName: string,\n handler: ContextAwareToolHandler | ToolHandler,\n options?: Partial<Omit<MiddlewareChainConfig, 'toolName'>>\n): ToolHandler {\n // Note: Policy firewall is NOT added by default - must be explicitly configured\n // This follows the principle of minimal defaults with explicit opt-in for security\n const config: MiddlewareChainConfig = {\n toolName,\n ...options,\n };\n\n const chain = createMiddlewareChain(config);\n\n // Wrap handler to support both signatures\n const wrappedHandler: ContextAwareToolHandler = (args, ctx) => {\n // Check if handler expects context (2 params)\n if (handler.length >= 2) {\n return handler(args, ctx);\n }\n return (handler as ToolHandler)(args);\n };\n\n return chain(wrappedHandler);\n}\n\n/**\n * Creates a middleware chain factory with shared configuration.\n *\n * @param sharedConfig - Configuration shared across all tools\n * @returns Factory function for creating wrapped handlers\n */\nexport function createMiddlewareFactory(\n sharedConfig: Omit<MiddlewareChainConfig, 'toolName' | 'schema'>\n): (\n toolName: string,\n handler: ContextAwareToolHandler | ToolHandler,\n schema?: z.ZodType\n) => ToolHandler {\n return (toolName, handler, schema) => {\n const options = schema !== undefined ? { ...sharedConfig, schema } : sharedConfig;\n return withMiddleware(toolName, handler, options);\n };\n}\n","/**\n * MCP Tool Annotations Registry — DERIVED VIEW (#993; folded into the manifest #3597).\n *\n * The per-tool annotation + side-effect DATA now lives in the canonical\n * {@link TOOL_MANIFEST} (one `{ name, annotations, sideEffects }` entry per tool,\n * #3597 increment 2 of #3563). This module is the keyed-record + accessor view\n * over that data: `TOOL_ANNOTATIONS` is derived from the manifest (each entry's\n * `.annotations`/`.sideEffects` are the SAME objects the manifest holds — no\n * clone, so reference identity is preserved for the live accessor in\n * `mcp/tool-annotations.ts`). The annotation TYPES are defined in the manifest\n * (the import-free leaf) and re-exported here for the existing consumers.\n *\n * @module mcp/tools/tool-annotations\n * (Source: Issue #993 — Document MCP tool side effects in schema metadata)\n */\n\nimport {\n TOOL_MANIFEST,\n type SideEffectCategory,\n type SideEffect,\n type ToolAnnotations,\n type ToolSideEffectsEntry,\n} from './tool-manifest.js';\n\n// Re-export the annotation types from their canonical home (the manifest leaf)\n// so existing consumers can keep importing them from this module.\nexport type { SideEffectCategory, SideEffect, ToolAnnotations, ToolSideEffectsEntry };\n\n/**\n * Canonical registry of tool annotations and side effects, keyed by tool name.\n * Derived from {@link TOOL_MANIFEST}; each value's `annotations`/`sideEffects`\n * reference the manifest entry's own objects (preserving identity).\n */\nexport const TOOL_ANNOTATIONS: Readonly<Record<string, ToolSideEffectsEntry>> = Object.freeze(\n Object.fromEntries(\n TOOL_MANIFEST.map((entry) => [\n entry.name,\n { annotations: entry.annotations, sideEffects: entry.sideEffects },\n ])\n )\n);\n\n/**\n * Returns the annotations for a given tool, or undefined if not found.\n */\nexport function getToolAnnotations(toolName: string): ToolSideEffectsEntry | undefined {\n return TOOL_ANNOTATIONS[toolName];\n}\n\n/**\n * Returns only the MCP protocol annotations for a tool (for registerTool config).\n */\nexport function getMcpAnnotations(toolName: string): ToolAnnotations | undefined {\n return TOOL_ANNOTATIONS[toolName]?.annotations;\n}\n\n/**\n * Returns side effects for a tool filtered by category.\n */\nexport function getSideEffectsByCategory(\n toolName: string,\n category: SideEffectCategory\n): readonly SideEffect[] {\n const entry = TOOL_ANNOTATIONS[toolName];\n if (entry === undefined) return [];\n return entry.sideEffects.filter((se) => se.category === category);\n}\n","/**\n * Central per-tool MCP annotations accessors (#2648, Epic A; consolidated #3358).\n *\n * The MCP 2025-11-25 spec defines four boolean hints clients use to reason\n * about each tool's safety, retry semantics, and permission UX:\n *\n * - `readOnlyHint` — tool does not modify persistent state\n * - `destructiveHint` — tool can perform destructive operations\n * - `idempotentHint` — calling with the same input is safe to repeat\n * - `openWorldHint` — tool interacts with systems outside the server's control\n *\n * Per the MCP spec these are **hints**, not enforcement primitives — clients\n * should never trust them from an untrusted server. But for nexus-agents (a\n * governance substrate) the hints are load-bearing for:\n *\n * - Programmatic prerequisite gates (Epic B / #2652) — uses\n * `destructiveHint` and `openWorldHint` to decide what to gate.\n * - Retry policy decisions in pipeline runners — only retry tools where\n * `idempotentHint === true`.\n * - Permission-prompt UX consistency across Claude / Codex / Gemini /\n * OpenCode harnesses.\n *\n * The audit (#2648 / docs/research/nexus-agents-multi-harness-alignment-audit.md\n * §6 T14) requires **every** registered tool declare all four hints\n * explicitly — no defaults.\n *\n * **Single source of truth (#3358):** the per-tool annotation DATA lives in\n * the side-effects superset registry at `./tools/tool-annotations.ts`, where\n * each entry pairs the MCP hints (`.annotations`) with curated side-effects\n * metadata (#993). This file is the LIVE ACCESSOR path: each tool's\n * `registerTool()` call site reads its annotations via `getToolAnnotations(name)`.\n * Data flows data-file → accessor-file only (no circular import).\n *\n * @module mcp/tool-annotations\n */\n\nimport type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';\nimport { TOOL_ANNOTATIONS as TOOL_SIDE_EFFECTS } from './tools/tool-annotations.js';\n\n/**\n * Look up the MCP annotations for a registered MCP tool. Throws if the tool\n * name isn't in the central registry — this enforces \"every tool declares its\n * hints explicitly\" rather than silently falling back to MCP's defaults\n * (which assume destructive + non-idempotent + open-world).\n *\n * The data is derived from the side-effects superset registry\n * (`./tools/tool-annotations.ts`, #993); only the four MCP hint booleans are\n * returned here — side-effects metadata is reached via that module's\n * `getSideEffectsByCategory`.\n */\nexport function getToolAnnotations(name: string): ToolAnnotations {\n const entry = TOOL_SIDE_EFFECTS[name];\n if (entry === undefined) {\n throw new Error(\n `getToolAnnotations: no entry for tool \"${name}\". Add it to TOOL_ANNOTATIONS in src/mcp/tools/tool-annotations.ts (#2648/#3358).`\n );\n }\n return entry.annotations;\n}\n","/**\n * nexus-agents/consensus/decision - Voting thresholds\n *\n * The numeric bars a tally is measured against. Extracted from\n * `consensus/types-core.ts` and `mcp/tools/consensus-vote-types.ts` (#6000\n * step 1) so the constants that decide what \"approved\" means live in a module\n * that can be governed on its own path, apart from the routine types and\n * schemas they used to share a file with. Every previous home re-exports\n * these, so the public API is unchanged.\n *\n * @module consensus/decision/thresholds\n */\n\nimport type { ConsensusAlgorithm } from '../types-core.js';\n\n/**\n * The exact 2/3 supermajority agreement threshold — the governance constant for\n * \"supermajority\" / Byzantine (2-of-3) quorum. SINGLE SOURCE (#3571): every\n * consensus site that needs a supermajority references this constant. It must\n * not be rounded: `0.67` rejected an exact 2-of-3 quorum (#5543).\n * (Per-algorithm values like 0.5/1.0 are intentionally NOT centralized — 0.5 is\n * semantically overloaded across several algorithms.)\n */\nexport const SUPERMAJORITY_THRESHOLD = 2 / 3;\n\n/**\n * Voting thresholds for each algorithm.\n */\nexport const VOTING_THRESHOLDS: Record<ConsensusAlgorithm, number> = {\n simple_majority: 0.5,\n supermajority: SUPERMAJORITY_THRESHOLD,\n unanimous: 1.0,\n proof_of_learning: 0.5, // Uses weighted voting\n opinion_wise: 0.5, // Uses correlation-aware Bayesian aggregation (Issue #333)\n higher_order: 0.5, // Alias for opinion_wise (Issue #514)\n};\n\n/**\n * Fraction of total voters that, if errored, forces the vote to fail\n * regardless of `errorPolicy`. (#2630 — safety floor.)\n */\nexport const ERROR_FLOOR_FRACTION = 0.5;\n","/**\n * nexus-agents/consensus - Core Type Definitions\n *\n * Core type definitions and Zod schemas for the consensus engine.\n * Supports multiple voting strategies for multi-agent decisions.\n */\n\nimport { z } from 'zod';\nimport { DEFAULT_MIN_VOTERS_FOR_QUORUM } from './decision/quorum.js';\n\n/**\n * Consensus algorithm types.\n * - simple_majority: >50% of votes required\n * - supermajority: >=67% of votes required\n * - unanimous: 100% approval required\n * - proof_of_learning: weighted voting based on agent performance\n * - opinion_wise: higher-order voting with correlation awareness (Issue #333)\n * - higher_order: alias for opinion_wise (Issue #514)\n */\nexport const ConsensusAlgorithmSchema = z.enum([\n 'simple_majority',\n 'supermajority',\n 'unanimous',\n 'proof_of_learning',\n 'opinion_wise',\n 'higher_order',\n]);\nexport type ConsensusAlgorithm = z.infer<typeof ConsensusAlgorithmSchema>;\n\n/**\n * Vote decision options.\n */\nexport const VoteDecisionSchema = z.enum(['approve', 'reject', 'abstain']);\nexport type VoteDecision = z.infer<typeof VoteDecisionSchema>;\n\n/**\n * Proposal status in the lifecycle.\n */\nexport const ProposalStatusSchema = z.enum([\n 'pending',\n 'voting',\n 'approved',\n 'rejected',\n 'timeout',\n 'closed',\n]);\nexport type ProposalStatus = z.infer<typeof ProposalStatusSchema>;\n\n/**\n * Structured rejection feedback categories (Issue #1213).\n * Enables reject→refine→re-vote feedback loops by classifying rejection reasons.\n */\nexport const RejectionCategorySchema = z.enum([\n 'YAGNI',\n 'DRY_VIOLATION',\n 'OVER_ENGINEERING',\n 'SCOPE_CREEP',\n 'SECURITY_RISK',\n 'MISALIGNED',\n 'INSUFFICIENT_EVIDENCE',\n]);\nexport type RejectionCategory = z.infer<typeof RejectionCategorySchema>;\n\n/**\n * All valid rejection category values, for runtime reference.\n */\nexport const REJECTION_CATEGORIES = RejectionCategorySchema.options;\n\n/**\n * Pre-verified finding emitted by a voter (#2245 v4 follow-up).\n * Mirrors `cli/voter-response.ts:RawFindingSchema` — kept inline here to\n * avoid a circular cli→consensus import. Downstream code in mcp/tools\n * adds the derived `verified` flag based on the gate fields.\n */\nconst FindingShapeSchema = z.object({\n summary: z.string().min(1).max(500),\n location: z.string().min(1).max(200),\n severity: z.enum(['critical', 'high', 'medium', 'low', 'info']).default('medium'),\n gate: z.object({\n reread_cited_line: z.enum(['passed', 'failed', 'skipped']).default('skipped'),\n traced_call_path: z.enum(['passed', 'failed', 'skipped']).default('skipped'),\n named_assertion: z.string().default(''),\n ruled_out_language_non_issue: z.enum(['passed', 'failed', 'skipped']).default('skipped'),\n }),\n claim: z.string().min(1).max(2000),\n});\n\n/**\n * A vote cast by an agent.\n */\nexport const VoteSchema = z.object({\n decision: VoteDecisionSchema,\n reasoning: z.string().min(1).describe('Explanation for the vote'),\n confidence: z.number().min(0).max(1).describe('Confidence level 0-1'),\n conditions: z.array(z.string()).optional().describe('Conditions for approval'),\n /** Structured rejection categories for reject→refine→re-vote loops (Issue #1213). */\n rejectionCategories: z\n .array(RejectionCategorySchema)\n .optional()\n .describe('Rejection reason categories when decision is reject'),\n /** Pre-verified PR-review findings (#2245 v4 follow-up). Optional;\n * populated only when the voter emits the structured top-level array. */\n findings: z.array(FindingShapeSchema).optional().describe('PR-review findings (pre-verified)'),\n /** Which declared option this voter chose, when the proposal declared\n * `options` (#4472). Absent when the proposal declared none, or when the\n * voter's selection matched no declared option — an unmatched selection is\n * recorded as absent, never coerced onto a default. */\n selectedOption: z\n .string()\n .optional()\n .describe('Chosen option when the proposal declares options'),\n timestamp: z.iso.datetime().optional(),\n});\nexport type Vote = z.infer<typeof VoteSchema>;\n\n/**\n * A proposal submitted for consensus.\n */\nexport const ProposalSchema = z.object({\n id: z.string().optional().describe('Auto-generated if not provided'),\n title: z.string().min(1).max(200).describe('Short proposal title'),\n description: z.string().min(1).describe('Detailed proposal description'),\n algorithm: ConsensusAlgorithmSchema,\n timeout: z.number().int().positive().optional().describe('Timeout in milliseconds'),\n requiredVoters: z.array(z.string()).optional().describe('Agent IDs that must vote'),\n metadata: z.record(z.string(), z.unknown()).optional().describe('Additional context'),\n createdAt: z.iso.datetime().optional(),\n});\nexport type Proposal = z.infer<typeof ProposalSchema>;\n\n/**\n * Unique identifier for a proposal.\n */\nexport type ProposalId = string;\n\n/**\n * Vote counts summary.\n */\nexport interface VoteCounts {\n approve: number;\n reject: number;\n abstain: number;\n total: number;\n}\n\n/**\n * Weighted vote counts for proof-of-learning.\n */\n/**\n * What a weighted tally's weights were actually derived from (#5117).\n *\n * `proof_of_learning` reported `\"X% weighted approval\"` and a populated\n * `weightedCounts` for tallies in which every weight was structurally `1.0` —\n * because the performance map feeding them has never had a writer\n * (`updateAgentPerformance` has no non-test caller). A reader was invited to\n * believe voter track record influenced the outcome. It did not, and could not.\n *\n * Deliberately NOT derived by checking whether any weight differs from `1.0`.\n * A voter with a perfect record legitimately weighs `1.0`, so numeric equality\n * cannot tell \"measured, and they were reliable\" from \"never measured\" — that\n * test would be its own can't-distinguish defect. The basis is derived from\n * PROVENANCE: whether a performance record existed for each voter.\n *\n * `partial` is a real state, not a rounding of the other two. Some voters\n * having history while others do not must not be reported as fully\n * performance-weighted.\n */\nexport type WeightBasis = 'performance' | 'partial' | 'unweighted';\n\nexport interface WeightedVoteCounts {\n approve: number;\n reject: number;\n abstain: number;\n totalWeight: number;\n}\n\n/**\n * Result of a consensus decision.\n */\nexport interface ConsensusResult {\n proposalId: ProposalId;\n proposal: Proposal;\n outcome: ProposalStatus;\n votes: Map<string, Vote>;\n voteCounts: VoteCounts;\n weightedCounts?: WeightedVoteCounts | undefined;\n /**\n * What the weights in `weightedCounts` were derived from (#5117).\n *\n * Carried on the RESULT, not just computed inside the strategy, because the\n * result is what a reviewer reads. A tally reported as weighted when every\n * weight was structurally 1.0 invites the reader to believe voter track\n * record moved the number. Absent for strategies that do not weight at all.\n */\n weightBasis?: WeightBasis | undefined;\n approvalPercentage: number;\n quorumReached: boolean;\n startedAt: string;\n closedAt: string;\n durationMs: number;\n}\n\n/**\n * Consensus result schema for validation.\n */\nexport const ConsensusResultSchema = z.object({\n proposalId: z.string(),\n proposal: ProposalSchema,\n outcome: ProposalStatusSchema,\n votes: z.map(z.string(), VoteSchema),\n voteCounts: z.object({\n approve: z.number().int().nonnegative(),\n reject: z.number().int().nonnegative(),\n abstain: z.number().int().nonnegative(),\n total: z.number().int().nonnegative(),\n }),\n weightedCounts: z\n .object({\n approve: z.number().nonnegative(),\n reject: z.number().nonnegative(),\n abstain: z.number().nonnegative(),\n totalWeight: z.number().nonnegative(),\n })\n .optional(),\n approvalPercentage: z.number().min(0).max(100),\n quorumReached: z.boolean(),\n startedAt: z.iso.datetime(),\n closedAt: z.iso.datetime(),\n durationMs: z.number().int().nonnegative(),\n});\n\n/**\n * Agent performance record for proof-of-learning.\n */\nexport interface AgentPerformance {\n agentId: string;\n totalVotes: number;\n correctVotes: number;\n successRate: number;\n lastUpdated: string;\n}\n\n/**\n * Agent performance schema.\n */\nexport const AgentPerformanceSchema = z.object({\n agentId: z.string(),\n totalVotes: z.number().int().nonnegative(),\n correctVotes: z.number().int().nonnegative(),\n successRate: z.number().min(0).max(1),\n lastUpdated: z.iso.datetime(),\n});\n\n/**\n * Proposal content caching configuration for determinism. (Issue #589)\n */\nexport interface ProposalCacheConfig {\n /** Enable content-based caching for repeated proposals */\n enabled: boolean;\n /** TTL in milliseconds (default: 1 hour) */\n ttlMs: number;\n /** Maximum cached entries (default: 500) */\n maxEntries: number;\n}\n\n/**\n * Incremental quorum configuration (Issue #1408).\n * When enabled, ambiguous votes trigger voter pool expansion.\n */\nexport interface IncrementalQuorumConfig {\n /** Enable incremental quorum expansion. Default: false */\n enabled: boolean;\n /** Maximum expansion rounds (5→7→9). Default: 2 */\n maxExpansionRounds: number;\n /** Voters to add per expansion round. Default: 2 */\n votersPerExpansion: number;\n /** Minimum average confidence to avoid expansion. Default: 0.6 */\n confidenceThreshold: number;\n /** Ambiguity band: if approval rate is within this of threshold, expand. Default: 0.15 */\n ambiguityBand: number;\n}\n\n/**\n * Default incremental quorum configuration.\n */\nexport const DEFAULT_INCREMENTAL_QUORUM_CONFIG: IncrementalQuorumConfig = {\n enabled: false,\n maxExpansionRounds: 2,\n votersPerExpansion: 2,\n confidenceThreshold: 0.6,\n ambiguityBand: 0.15,\n};\n\n/**\n * Callback to request additional voters for incremental quorum.\n * Returns the IDs of newly added voters.\n */\nexport type VoterExpansionCallback = (\n proposalId: ProposalId,\n currentVoterCount: number,\n requestedCount: number\n) => Promise<readonly string[]>;\n\n/**\n * Consensus engine configuration.\n */\nexport interface ConsensusEngineConfig {\n defaultTimeout: number;\n minVotersForQuorum: number;\n maxActiveProposals: number;\n enablePerformanceTracking: boolean;\n /** Maximum number of closed proposals to retain. Oldest are evicted when exceeded. (Issue #549) */\n maxClosedProposals: number;\n /** Content-based proposal caching for determinism (Issue #589) */\n proposalCache?: ProposalCacheConfig;\n /** Incremental quorum configuration (Issue #1408) */\n incrementalQuorum?: IncrementalQuorumConfig;\n}\n\n/**\n * Proposal cache configuration schema. (Issue #589)\n */\nexport const ProposalCacheConfigSchema = z.object({\n enabled: z.boolean().default(false),\n ttlMs: z.number().int().positive().default(3600000), // 1 hour\n maxEntries: z.number().int().positive().default(500),\n});\n\n/**\n * Consensus engine configuration schema.\n */\nexport const ConsensusEngineConfigSchema = z.object({\n defaultTimeout: z.number().int().positive().default(300000), // 5 minutes\n minVotersForQuorum: z.number().int().positive().default(DEFAULT_MIN_VOTERS_FOR_QUORUM),\n maxActiveProposals: z.number().int().positive().default(100),\n enablePerformanceTracking: z.boolean().default(true),\n maxClosedProposals: z.number().int().positive().default(1000), // Issue #549\n proposalCache: ProposalCacheConfigSchema.optional(), // Issue #589\n});\n\n/**\n * Default configuration values.\n */\nexport const DEFAULT_CONSENSUS_CONFIG: ConsensusEngineConfig = {\n defaultTimeout: 300000, // 5 minutes\n minVotersForQuorum: DEFAULT_MIN_VOTERS_FOR_QUORUM, // governed: decision/quorum.ts (#6180)\n maxActiveProposals: 100,\n enablePerformanceTracking: true,\n maxClosedProposals: 1000, // Issue #549: Prevent unbounded memory growth\n};\n\n/**\n * `SUPERMAJORITY_THRESHOLD` and `VOTING_THRESHOLDS` moved to\n * `decision/thresholds.ts` (#6000 step 1) so the bars a tally is measured\n * against can be governed on their own path. Re-exported here so every\n * existing import keeps resolving.\n */\nexport { SUPERMAJORITY_THRESHOLD, VOTING_THRESHOLDS } from './decision/thresholds.js';\n\n/**\n * Internal proposal state managed by the engine.\n */\nexport interface ProposalState {\n proposal: Proposal;\n status: ProposalStatus;\n votes: Map<string, Vote>;\n voteWeights: Map<string, number>;\n startedAt: Date;\n timeoutId?: ReturnType<typeof setTimeout>;\n /** Number of incremental quorum expansions applied (Issue #1408). */\n expansionRounds?: number;\n /**\n * True while a quorum expansion is awaiting its callback for this\n * proposal. Concurrent `vote()` calls check this to avoid double-\n * expanding across the `await` gap (Issue #2861). Per-proposal so\n * independent proposals never block each other.\n */\n expansionInFlight?: boolean;\n}\n\n/**\n * Consensus metrics for monitoring.\n */\nexport interface ConsensusMetrics {\n totalProposals: number;\n approvedProposals: number;\n rejectedProposals: number;\n timedOutProposals: number;\n averageDurationMs: number;\n averageVotesPerProposal: number;\n algorithmUsage: Record<ConsensusAlgorithm, number>;\n}\n\n/**\n * Consensus metrics schema.\n */\nexport const ConsensusMetricsSchema = z.object({\n totalProposals: z.number().int().nonnegative(),\n approvedProposals: z.number().int().nonnegative(),\n rejectedProposals: z.number().int().nonnegative(),\n timedOutProposals: z.number().int().nonnegative(),\n averageDurationMs: z.number().nonnegative(),\n averageVotesPerProposal: z.number().nonnegative(),\n algorithmUsage: z.record(ConsensusAlgorithmSchema, z.number().int().nonnegative()),\n});\n","/**\n * nexus-agents/consensus/decision - Quorum computation\n *\n * The engine's quorum predicate and its default bar, extracted verbatim\n * (#6180, mirroring #6000 step 1) from the two files that used to hold them:\n *\n * - `isQuorumReached` — the `votes.size >= config.minVotersForQuorum`\n * comparison that `consensus/result-builder.ts` inlined in each of its three\n * builders. It is the `quorumReached` input to `determineFinalStatus`, so a\n * caller that answered it by hand could pass every verdict fixture; the\n * #6175 seam now derives the expected quorum through this function.\n * - `DEFAULT_MIN_VOTERS_FOR_QUORUM` — the literal `2` that\n * `consensus/types-core.ts` held twice, as the Zod default of\n * `ConsensusEngineConfigSchema.minVotersForQuorum` and as\n * `DEFAULT_CONSENSUS_CONFIG.minVotersForQuorum`. Both now read it from here.\n *\n * What stays outside: the engine's own `minVotersForQuorum` config field (a\n * caller may still raise the bar per engine), the vote-list shaping that\n * decides which seats reach the engine (`consensus-vote-error-policy.ts`), and\n * `consensus/quorum-validator.ts`, which is a different predicate for the\n * `VotingProtocol` path and has no in-tree caller (#4666).\n *\n * This module imports nothing at runtime: `types-core.ts` imports the default\n * from here, so a runtime edge back would be a cycle.\n *\n * @module consensus/decision/quorum\n */\n\n/**\n * The smallest number of votes — approve, reject or abstain — a proposal must\n * hold when it closes for the engine to report `quorumReached`. Below it the\n * engine's outcome is `rejected` whatever the tally says.\n */\nexport const DEFAULT_MIN_VOTERS_FOR_QUORUM = 2;\n\n/**\n * Whether `voteCount` votes reach a quorum of `minVotersForQuorum`.\n *\n * Counts votes, not respondents: an abstention holds a seat. Zero votes never\n * reach a positive bar, which is how the engine's empty close reads as no\n * quorum (#6172, the named empty case).\n */\nexport function isQuorumReached(voteCount: number, minVotersForQuorum: number): boolean {\n return voteCount >= minVotersForQuorum;\n}\n"],"mappings":"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","names":["z","z","z","refusal","resolve","sep","resolve","sep","refusal","dirname","err","dirname","z","z"]}
|