import { buildMcpToolDefinitions } from './mcp/definitions.js';
import { runHandler, envelopeToMcpResponse } from './mcp/runHandler.js';
import { scanSourceFiles as scanSourceFilesUtil } from './mcp/util/walk.js';
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from '@modelcontextprotocol/sdk/types.js';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { detectCallerFromEnv, detectCallerAsync, setMcpClientName } from './mcp/util/detect-caller.js';
import { createHash, randomUUID } from 'crypto';
import { SessionDNAManager } from '../../engines/session-dna.js';
import { ASCII_ART } from '../../utils/ascii-art.js';
import { drawCard } from '../../utils/ascii-card.js';
import { nexusEventBus } from '../../engines/event-bus.js';
import { getTokenEngine } from './mcp/util/token-engine.js';
import { DarwinLoop } from '../../engines/darwin-loop.js';
import { getSharedNgramIndex } from '../../engines/ngram-index.js';
import { getSharedTelemetry } from '../../engines/telemetry-remote.js';
import { getGstackBridge } from '../../engines/gstack-bridge.js';
import { getSharedContextDiscovery } from '../../engines/context-discovery.js';
import { getSharedLicenseManager, LicenseEnforcementMiddleware, isToolAllowed } from '../../licensing/index.js';
import { MiddlewarePipeline } from '../../engines/middleware-pipeline.js';
import { resolveWorkspaceContext, resolveWorkspaceStateRoot } from '../../engines/workspace-resolver.js';
import { hasValidBootstrapReceipt } from '../../engines/bootstrap/index.js';
import { handleSynapseToolCall } from '../../synapse/index.js';
import { handleArchitectsToolCall } from '../../architects/index.js';
import { ArchitectsSentinel } from '../../architects/sentinel/patrol.js';
import { SessionTelemetry, } from './mcp/types.js';
import { getFallbackRuntime, getFallbackOrchestrator, getGuardrailEngine, normalizeDetailLevel, normalizeResponseIntent, normalizeResponseTopology, buildWorkspaceEnvelope, buildResultPreview, isReadOnlyMcpTool, buildAutoMemorySummary, } from './mcp/helpers.js';
import { dispatchMcpToolCall } from './mcp/dispatch.js';
import { LifecyclePolicy } from '../../engines/lifecycle-policy.js';
import { startWatchdog } from './mcp/watchdog.js';
import { getMcpStdioInput } from './mcp/stdio-buffer.js';
// Derive project root from this file's location (dist/agents/adapters/mcp.js → project root)
const __filename = fileURLToPath(import.meta.url);
const PROJECT_ROOT = path.resolve(path.dirname(__filename), '..', '..', '..');
const AUTONOMOUS_TOOL_ORDER = [
    'nexus_session_bootstrap',
    'nexus_orchestrate',
    'nexus_plan_execution',
    'nexus_recall_memory',
    'nexus_memory_stats',
    'nexus_temporal_query',
    'nexus_store_memory',
    'nexus_search',
    'nexus_describe_tool',
    'nexus_optimize_tokens',
    'nexus_mindkit_check',
    'nexus_ghost_pass',
    'nexus_spawn_workers',
    'nexus_token_report',
    'nexus_session_dna',
    'nexus_list_skills',
    'nexus_list_workflows',
    'nexus_list_hooks',
    'nexus_list_automations',
    'nexus_list_specialists',
    'nexus_list_crews',
    'nexus_federation_status',
    'nexus_run_status',
    'nexus_runtime_health',
    'nexus_doctor',
    'nexus_autofix',
    'nexus_memory_hygiene',
    'nexus_operative_journal',
    'nexus_selection_ledger',
    'nexus_license_usage',
];
const AUTONOMOUS_TOOL_SET = new Set(AUTONOMOUS_TOOL_ORDER);
const MANUAL_OR_DIAGNOSTIC_TOOLS = new Set([
    'nexus_spawn_workers',
    'nexus_optimize_tokens',
    'nexus_ghost_pass',
    'nexus_hypertune_max',
    'nexus_graph_query',
    'nexus_skill_generate',
    'nexus_skill_deploy',
    'nexus_skill_revoke',
    'nexus_workflow_generate',
    'nexus_workflow_deploy',
    'nexus_workflow_run',
    'nexus_hook_generate',
    'nexus_hook_deploy',
    'nexus_hook_revoke',
    'nexus_automation_generate',
    'nexus_automation_deploy',
    'nexus_automation_run',
    'nexus_automation_revoke',
    'nexus_memory_audit',
    'nexus_memory_export',
    'nexus_memory_import',
    'nexus_memory_backup',
    'nexus_memory_trace',
    'nexus_darwin_propose',
    'nexus_darwin_review',
    'nexus_darwin_status',
    'nexus_net_publish',
    'nexus_net_sync',
    'nexus_assemble_context',
    'nexus_publish_trace',
    'nexus_rag_list_collections',
    'nexus_rag_create_collection',
    'nexus_rag_ingest_collection',
    'nexus_rag_attach_collection',
    'nexus_rag_detach_collection',
    'nexus_rag_delete_collection',
    'nexus_pattern_search',
    'nexus_pattern_list',
    'nexus_knowledge_provenance',
]);
const PRE_BOOTSTRAP_ALLOWED_TOOLS = new Set([
    'nexus_session_bootstrap',
    'nexus_memory_stats',
    'nexus_runtime_health',
    'nexus_doctor',
    'nexus_autofix',
    'nexus_describe_tool',
    'nexus_federation_status',
    'nexus_status',
    // Specific list tools allowed pre-bootstrap for catalog discovery only:
    'nexus_list_skills',
    'nexus_list_workflows',
    'nexus_list_hooks',
    'nexus_list_automations',
    'nexus_list_specialists',
    'nexus_list_crews',
]);
const PRE_ORCHESTRATE_ALLOWED_TOOLS = new Set([
    ...PRE_BOOTSTRAP_ALLOWED_TOOLS,
    'nexus_orchestrate',
    'nexus_plan_execution',
    'nexus_recall_memory',
    'nexus_temporal_query',
    'nexus_store_memory',
    'nexus_search',
    'nexus_optimize_tokens',
    'nexus_mindkit_check',
    'nexus_ghost_pass',
    'nexus_spawn_workers',
    'nexus_token_report',
    'nexus_session_dna',
    'nexus_run_status',
    'nexus_selection_ledger',
    'nexus_license_usage',
]);
export class MCPAdapter {
    name;
    type = 'mcp';
    connected = false;
    agents = [];
    server;
    nexusRef;
    telemetry = new SessionTelemetry();
    sessionDNA;
    runtime;
    fallbackRuntimeStateKey;
    darwinLoops = new Map();
    lifecyclePolicy = new LifecyclePolicy();
    memoryInjectionCache = new Map();
    lastMemoryInjectionAt = 0;
    currentTask = '';
    middlewarePipeline;
    scanCache = new Map();
    sessionLifecycleFinalized = false;
    sciFiMatrixLog(title, metrics, intent) {
        const width = 76;
        console.error(`\n\x1b[36m╔═══ [ \x1b[37m\x1b[1mNEXUS PRIME · ORCHESTRATION MATRIX\x1b[0m\x1b[36m ] ${'═'.repeat(Math.max(0, width - 48))}╗\x1b[0m`);
        console.error(`\x1b[36m║ \x1b[35m► \x1b[37m${title.padEnd(width - 6, ' ')} \x1b[36m║\x1b[0m`);
        console.error(`\x1b[36m╠${'═'.repeat(width - 2)}╣\x1b[0m`);
        if (intent) {
            console.error(`\x1b[36m║ \x1b[32m[DETECTED INTENT]\x1b[0m \x1b[37m${intent.padEnd(width - 22, ' ')} \x1b[36m║\x1b[0m`);
            console.error(`\x1b[36m╠${'═'.repeat(width - 2)}╣\x1b[0m`);
        }
        const keys = Object.keys(metrics);
        for (let i = 0; i < keys.length; i += 2) {
            const k1 = keys[i];
            const v1 = String(metrics[k1]).substring(0, 20);
            if (i + 1 < keys.length) {
                const k2 = keys[i + 1];
                const v2 = String(metrics[k2]).substring(0, 20);
                // We have to hardcode padding because ansi escape codes mess with .padEnd length
                console.error(`\x1b[36m║ \x1b[33m${k1}:\x1b[37m ${v1.padEnd(20, ' ')} │ \x1b[33m${k2}:\x1b[37m ${v2.padEnd(19, ' ')} \x1b[36m║\x1b[0m`);
            }
            else {
                console.error(`\x1b[36m║ \x1b[33m${k1}:\x1b[37m ${v1.padEnd(20, ' ')} │                     \x1b[36m║\x1b[0m`);
            }
        }
        console.error(`\x1b[36m╚${'═'.repeat(width - 2)}╝\x1b[0m\n`);
    }
    classifyToolCategory(toolName) {
        if ([
            'nexus_store_memory',
            'nexus_recall_memory',
            'nexus_memory_stats',
            'nexus_memory_trace',
            'nexus_memory_export',
            'nexus_memory_backup',
            'nexus_memory_import',
            'nexus_memory_update',
            'nexus_memory_delete',
            'nexus_memory_history',
            'nexus_memory_feedback',
        ].includes(toolName))
            return 'memory';
        if (['nexus_orchestrate', 'nexus_plan_execution', 'nexus_spawn_workers'].includes(toolName))
            return 'orchestration';
        if (['nexus_ghost_pass', 'nexus_mindkit_check', 'nexus_optimize_tokens'].includes(toolName))
            return 'execution';
        if (['nexus_session_bootstrap', 'nexus_session_dna', 'nexus_session_search', 'nexus_token_report'].includes(toolName))
            return 'intelligence';
        if (toolName === 'nexus_run_status' || toolName === 'nexus_federation_status' || toolName.startsWith('nexus_list_'))
            return 'system';
        return 'system';
    }
    sciFiToolHeader(category, toolName, goal) {
        if (!this.lifecyclePolicy.isEnabled('mcpCategoryVisuals')) {
            console.error(`\x1b[90m[NEXUS] ◈ ${toolName} ◈\x1b[0m`);
            return;
        }
        const rawPreview = goal || 'No goal payload provided.';
        const preview = rawPreview.length > 54 ? `${rawPreview.slice(0, 53)}…` : rawPreview;
        if (category === 'orchestration') {
            this.sciFiMatrixLog('ORCHESTRATION MATRIX', {
                'Category': 'orchestration',
                'Label': 'ORCHESTRATION MATRIX',
                'Tool': toolName,
                'Execution': 'autonomous',
                'Policy': 'enabled',
                'Status': 'dispatching',
            }, preview);
            return;
        }
        const themed = {
            memory: {
                color: '32',
                title: '🧠 MEMORY LATTICE QUERY',
                lines: [
                    `Glyph: ⬢ MEMORY NODE`,
                    `Tool: ${toolName}`,
                    `Intent: ${preview}`,
                    'Border: single-line lattice',
                ],
            },
            execution: {
                color: '33',
                title: '⚙ EXECUTION PIPELINE',
                lines: [
                    `Glyph: ▶ PIPE STAGE`,
                    `Tool: ${toolName}`,
                    `Intent: ${preview}`,
                    'Border: high-visibility single line',
                ],
            },
            intelligence: {
                color: '34',
                title: '📡 INTELLIGENCE RELAY',
                lines: [
                    `Glyph: ⟡ RELAY NODE`,
                    `Tool: ${toolName}`,
                    `Intent: ${preview}`,
                    'Border: relay channel single line',
                ],
            },
            system: {
                color: '90',
                title: '⋯ SYSTEM BUS ⋯',
                lines: [
                    `Glyph: ◌ BUS TRACE`,
                    `Tool: ${toolName}`,
                    `Intent: ${preview}`,
                    'Border: dotted diagnostics',
                ],
            },
        }[category];
        this.box(themed.title, themed.lines, themed.color);
    }
    box(title, lines, color = '34') {
        drawCard({ title, lines, color });
    }
    constructor() {
        this.name = detectCallerFromEnv();
        this.server = new Server({ name: 'nexus-prime-mcp', version: '0.4.0' }, { capabilities: { tools: {} } });
        this.sessionDNA = new SessionDNAManager(randomUUID());
        this.middlewarePipeline = new MiddlewarePipeline();
        this.registerBuiltInMiddleware();
        this.setupToolHandlers();
        // Refine caller name async (ps fallback) without blocking construction.
        setImmediate(() => {
            void detectCallerAsync().then((n) => { this.name = n; });
        });
    }
    setNexusRef(nexus) {
        this.nexusRef = nexus;
    }
    isPackageLikeWorkspace(target) {
        const relative = path.relative(path.resolve(PROJECT_ROOT), path.resolve(target));
        return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
    }
    resolveWorkspaceHintFromArgs(args = {}) {
        const rawFiles = [
            ...(Array.isArray(args.files) ? args.files : []),
            ...(Array.isArray(args.filesToModify) ? args.filesToModify : []),
        ].map((value) => String(value ?? '').trim()).filter(Boolean);
        const preferredCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
        for (const rawFile of rawFiles) {
            const absolute = path.isAbsolute(rawFile) ? rawFile : path.resolve(preferredCwd, rawFile);
            // Avoid blocking statSync: files have extensions, directory paths don't.
            const ext = path.extname(path.basename(absolute));
            return ext ? path.dirname(absolute) : absolute;
        }
        return undefined;
    }
    getWorkspaceContextForArgs(args = {}) {
        const current = this.nexusRef?.getWorkspaceContext?.();
        const preferredCwd = process.env.INIT_CWD || process.env.PWD || process.cwd();
        const hintedRoot = this.resolveWorkspaceHintFromArgs(args);
        const hinted = hintedRoot
            ? resolveWorkspaceContext({ workspaceRoot: hintedRoot, cwd: preferredCwd, env: process.env })
            : null;
        if (hinted && (!current || current.stateKey !== hinted.stateKey)) {
            try {
                this.nexusRef?.setWorkspaceContext?.(hinted);
            }
            catch { /* best-effort */ }
            return hinted;
        }
        const envResolved = resolveWorkspaceContext({ cwd: preferredCwd, env: process.env });
        if (!current)
            return envResolved;
        if (this.isPackageLikeWorkspace(current.repoRoot) && !this.isPackageLikeWorkspace(envResolved.repoRoot)) {
            try {
                this.nexusRef?.setWorkspaceContext?.(envResolved);
            }
            catch { /* best-effort */ }
            return envResolved;
        }
        return current;
    }
    resolveToolPath(candidatePath, args = {}) {
        return path.isAbsolute(candidatePath)
            ? candidatePath
            : path.join(this.getWorkspaceContextForArgs(args).repoRoot, candidatePath);
    }
    getToolProjectHash(args = {}) {
        return createHash('sha1').update(path.resolve(this.getWorkspaceContextForArgs(args).repoRoot)).digest('hex').slice(0, 12);
    }
    getRepoNgramIndex(args = {}) {
        const workspace = this.getWorkspaceContextForArgs(args);
        return getSharedNgramIndex(path.join(resolveWorkspaceStateRoot(workspace.repoRoot), 'repo-search.ngram.db'));
    }
    getDarwinLoop(args = {}) {
        const workspace = this.getWorkspaceContextForArgs(args);
        let loop = this.darwinLoops.get(workspace.stateKey);
        if (!loop) {
            loop = new DarwinLoop(this.getOrchestrator(args).getMemoryEngine());
            this.darwinLoops.set(workspace.stateKey, loop);
        }
        return loop;
    }
    getRuntime(args = {}) {
        if (this.nexusRef && typeof this.nexusRef.getRuntime === 'function') {
            return this.nexusRef.getRuntime();
        }
        const workspace = this.getWorkspaceContextForArgs(args);
        if (!this.runtime || this.fallbackRuntimeStateKey !== workspace.stateKey) {
            this.runtime = getFallbackRuntime(workspace);
            this.fallbackRuntimeStateKey = workspace.stateKey;
        }
        return this.runtime;
    }
    getOrchestrator(args = {}) {
        if (this.nexusRef && typeof this.nexusRef.getOrchestrator === 'function') {
            return this.nexusRef.getOrchestrator();
        }
        return getFallbackOrchestrator(this.getWorkspaceContextForArgs(args));
    }
    getToolProfile() {
        return String(process.env.NEXUS_MCP_TOOL_PROFILE ?? 'autonomous').toLowerCase() === 'full'
            ? 'full'
            : 'autonomous';
    }
    registerBuiltInMiddleware() {
        // License enforcement runs first (priority 5)
        this.middlewarePipeline.register(LicenseEnforcementMiddleware);
        this.middlewarePipeline.register({
            name: 'token-budget',
            priority: 10,
            before: (ctx) => {
                ctx.meta.tokenRelevant = ctx.toolName === 'nexus_optimize_tokens'
                    || Array.isArray(ctx.args.files)
                    || typeof ctx.args.goal === 'string'
                    || typeof ctx.args.prompt === 'string';
            },
        });
        this.middlewarePipeline.register({
            name: 'telemetry',
            priority: 20,
            after: async (ctx, result) => {
                const remote = getSharedTelemetry();
                remote.trackFeatureUsage(ctx.toolName, {
                    success: !ctx.meta.shortCircuitedBy,
                    resultBytes: Buffer.byteLength(JSON.stringify(result), 'utf8'),
                });
                if (typeof ctx.meta.tokenSavings === 'number' && Number(ctx.meta.tokenSavings) > 0) {
                    remote.trackTokenSavings(Number(ctx.meta.tokenSavings), { projectHash: ctx.projectHash });
                }
                if (ctx.toolName === 'nexus_store_memory') {
                    remote.trackMemoryCreated(String(ctx.meta.memoryScope ?? 'project'), {
                        projectHash: ctx.projectHash,
                        priority: Number(ctx.args.priority ?? 0),
                    });
                }
                if (ctx.toolName === 'nexus_recall_memory') {
                    remote.trackMemoryRecalled(Number(ctx.meta.memoryRecallCount ?? 0), {
                        projectHash: ctx.projectHash,
                    });
                }
            },
            onError: async (ctx) => {
                getSharedTelemetry().trackFeatureUsage(ctx.toolName, { success: false });
            },
        });
        this.middlewarePipeline.register({
            name: 'memory',
            priority: 30,
            before: (ctx) => {
                if (ctx.toolName !== 'nexus_store_memory')
                    return;
                const tags = Array.isArray(ctx.args.tags) ? ctx.args.tags.map(String) : [];
                if (!tags.includes('#project'))
                    tags.push('#project');
                if (ctx.projectHash && !tags.some((tag) => tag.startsWith('#project:'))) {
                    tags.push(`#project:${ctx.projectHash}`);
                }
                ctx.args.tags = [...new Set(tags)];
                ctx.meta.memoryScope = 'project';
            },
        });
        this.middlewarePipeline.register({
            name: 'guardrails',
            priority: 40,
            before: (ctx) => {
                if (!['nexus_store_memory', 'nexus_net_publish'].includes(ctx.toolName))
                    return;
                const action = ctx.toolName === 'nexus_store_memory'
                    ? `nexus_store_memory: ${String(ctx.args.content ?? '')}`
                    : `nexusnet_transmit: ${String(ctx.args.content ?? '')}`;
                const guardCheck = getGuardrailEngine().check({ action });
                if (guardCheck.passed)
                    return;
                ctx.meta.shortCircuitedBy = 'guardrails';
                ctx.meta.shortCircuitResult = {
                    content: [{
                            type: 'text',
                            text: `❌ GUARDRAIL BLOCKED: ${getGuardrailEngine().format(guardCheck)}`,
                        }],
                };
            },
        });
        this.middlewarePipeline.register({
            name: 'cost',
            priority: 50,
            after: (ctx, result) => {
                const durationMs = Date.now() - ctx.startTimeMs;
                const responseBytes = Buffer.byteLength(JSON.stringify(result), 'utf8');
                ctx.meta.durationMs = durationMs;
                ctx.meta.responseBytes = responseBytes;
                ctx.meta.estimatedUsd = Number(((responseBytes / 4_000) * 0.000003).toFixed(6));
            },
        });
    }
    formatProtocolChecklist() {
        return [
            'PROTOCOL CHECKLIST (follow every step):',
            '[x] nexus_session_bootstrap — DONE',
            '[ ] nexus_orchestrate — Call NOW with the user\'s request',
            '[ ] nexus_optimize_tokens — Call BEFORE reading 3+ files',
            '[ ] nexus_mindkit_check — Call BEFORE any file modifications',
            '[ ] nexus_store_memory — Call AFTER significant findings AND at session end',
            '[ ] nexus_session_dna — Call BEFORE ending this session',
        ].join('\n');
    }
    formatRemainingProtocolSteps() {
        return [
            'REMAINING PROTOCOL STEPS (MANDATORY):',
            '- Call nexus_optimize_tokens(goal, files) BEFORE reading 3+ files',
            '- Call nexus_mindkit_check(action, filesToModify) BEFORE modifying any files',
            '- Call nexus_store_memory(content, priority, tags) with key findings from this work',
            '- Call nexus_session_dna(action="generate") BEFORE ending this session',
            'Do NOT skip these steps. The orchestrate call handled planning and execution, but these lifecycle steps are your responsibility.',
        ].join('\n');
    }
    prependTextToResponse(result, text) {
        if (!text.trim())
            return result;
        const content = result.content.map((item, index) => {
            if (index === 0 && item.type === 'text') {
                return {
                    ...item,
                    text: `${text}\n\n${item.text}`,
                };
            }
            return item;
        });
        return { ...result, content };
    }
    decorateLifecycleResponse(toolName, result) {
        const telemetry = this.telemetry.snapshot();
        const warnings = [];
        if (this.telemetry.needsOptimizeTokens(toolName)) {
            warnings.push(`Nexus only sees best-effort file intent from tool arguments. Current intent references ${telemetry.fileReadIntentCount} file(s), and nexus_optimize_tokens has not been called in this phase. Call nexus_optimize_tokens(goal, files) before broad reading.`);
        }
        if (this.telemetry.needsStoreMemory(toolName)) {
            warnings.push(`${telemetry.callsSinceOrchestrate} post-orchestrate tool calls have happened without nexus_store_memory. Store the key findings before continuing.`);
        }
        if (warnings.length === 0)
            return result;
        // Make warnings highly visible: prepend as plain text AND inject as structured first content block
        const warningBlock = {
            type: 'text',
            text: JSON.stringify({
                WARNING: 'NEXUS LIFECYCLE VIOLATION',
                issues: warnings,
                requiredActions: warnings.map((w) => w.includes('nexus_optimize_tokens') ? 'Call nexus_optimize_tokens(goal, files) NOW before reading more files'
                    : w.includes('nexus_store_memory') ? 'Call nexus_store_memory(content, priority, tags) to persist findings'
                        : w),
            }, null, 2),
        };
        return {
            content: [
                warningBlock,
                ...result.content,
            ],
        };
    }
    buildPreOrchestrationBlock(toolName) {
        if (!this.telemetry.needsOrchestration(toolName))
            return null;
        if (PRE_ORCHESTRATE_ALLOWED_TOOLS.has(toolName) || isReadOnlyMcpTool(toolName))
            return null;
        return {
            content: [{
                    type: 'text',
                    text: JSON.stringify({
                        status: 'blocked',
                        code: 'orchestration-required',
                        reason: 'nexus_orchestrate has not been called for this session',
                        action: 'Call nexus_orchestrate(prompt="<your task>") before invoking mutation, worker, kernel, hook, automation, or low-level execution tools.',
                        nextTool: 'nexus_orchestrate',
                        hint: 'Nexus Prime must own planning, token budgeting, memory recall, hooks, and review gates before work starts.',
                    }, null, 2),
                }],
        };
    }
    extractGoalLikeText(args) {
        const candidates = [args.goal, args.task, args.prompt, args.query, args.action, args.content]
            .map((value) => String(value ?? '').trim())
            .filter(Boolean);
        return candidates[0] ?? '';
    }
    isMemoryTool(toolName) {
        return toolName === 'nexus_store_memory'
            || toolName === 'nexus_recall_memory'
            || toolName === 'nexus_memory_stats'
            || toolName.startsWith('nexus_memory_');
    }
    async injectMemoryContext(toolName, args, result) {
        const policy = this.lifecyclePolicy.evaluate('memoryInjection', {
            toolName,
            hasArgs: Object.keys(args ?? {}).length,
        });
        if (!policy.enabled || this.isMemoryTool(toolName))
            return result;
        const query = this.extractGoalLikeText(args);
        if (query.length < 10)
            return result;
        const now = Date.now();
        if (now - this.lastMemoryInjectionAt < 30_000)
            return result;
        const cacheWindowMs = Number(process.env.NEXUS_MEMORY_INJECTION_CACHE_MS ?? 30_000);
        const minScore = Number(process.env.NEXUS_MEMORY_INJECTION_MIN_SCORE ?? 0.4);
        const cacheKey = createHash('sha1').update(query.toLowerCase()).digest('hex').slice(0, 16);
        const cached = this.memoryInjectionCache.get(cacheKey);
        let matches = [];
        if (cached && cached.expiresAt > now) {
            matches = cached.matches;
        }
        else {
            try {
                const memory = this.getOrchestrator().getMemoryEngine();
                const recalled = await memory.recallWithMetadata(query, 3, { minScore });
                matches = recalled
                    .filter((entry) => Number(entry.score) >= minScore)
                    .map((entry) => ({ content: entry.content, score: entry.score }));
                this.memoryInjectionCache.set(cacheKey, {
                    expiresAt: now + cacheWindowMs,
                    matches,
                });
            }
            catch (error) {
                console.warn('[MCP] Memory context injection skipped:', error?.message ?? error);
                return result;
            }
        }
        if (matches.length === 0)
            return result;
        this.lastMemoryInjectionAt = now;
        const contextBlock = {
            type: 'text',
            text: [
                '[Memory Context]',
                ...matches.map((entry, index) => `${index + 1}. ${entry.content.slice(0, 220)}${entry.content.length > 220 ? '…' : ''} (score ${entry.score.toFixed(2)})`),
            ].join('\n'),
        };
        return {
            ...result,
            content: [...result.content, contextBlock],
        };
    }
    describeClientInstructionStatus(profile) {
        return profile === 'autonomous'
            ? 'Autonomous MCP profile active. REQUIRED start: nexus_session_bootstrap, then nexus_orchestrate. During-work and session-close lifecycle tools remain mandatory.'
            : 'Full MCP profile active. Low-level and authoring tools are exposed.';
    }
    finalizeToolDefinitions(tools, profile = this.getToolProfile()) {
        const taskContext = this.currentTask || this.runtime?.getUsageSnapshot()?.orchestration?.lastPrompt || '';
        const userTier = getSharedLicenseManager().getStatus().tier;
        const profileFiltered = profile === 'full'
            ? tools.slice()
            : tools.filter((tool) => AUTONOMOUS_TOOL_SET.has(tool.name));
        // Filter by license tier on the autonomous profile only.
        // The full profile is used for diagnostics, CI, and admin tooling — it always shows all tools.
        const scoped = profile === 'full'
            ? profileFiltered
            : profileFiltered.filter((tool) => isToolAllowed(tool.name, userTier));
        const order = profile === 'full'
            ? [...AUTONOMOUS_TOOL_ORDER, ...scoped.map((tool) => tool.name).filter((name) => !AUTONOMOUS_TOOL_SET.has(name))]
            : [...AUTONOMOUS_TOOL_ORDER];
        const orderIndex = new Map(order.map((name, index) => [name, index]));
        const decorated = scoped
            .map((tool) => ({
            ...tool,
            description: this.decorateToolDescription(tool.name, tool.description, profile),
        }))
            .sort((left, right) => {
            const leftIndex = orderIndex.get(left.name) ?? Number.MAX_SAFE_INTEGER;
            const rightIndex = orderIndex.get(right.name) ?? Number.MAX_SAFE_INTEGER;
            return leftIndex - rightIndex || left.name.localeCompare(right.name);
        });
        const discovery = getSharedContextDiscovery();
        discovery.registerTools(decorated);
        return discovery.applyToTools(taskContext, decorated);
    }
    getDecoratedToolDefinitions(profile = this.getToolProfile()) {
        const scoped = profile === 'full'
            ? this.buildToolDefinitions().slice()
            : this.buildToolDefinitions().filter((tool) => AUTONOMOUS_TOOL_SET.has(tool.name));
        const order = profile === 'full'
            ? [...AUTONOMOUS_TOOL_ORDER, ...scoped.map((tool) => tool.name).filter((name) => !AUTONOMOUS_TOOL_SET.has(name))]
            : [...AUTONOMOUS_TOOL_ORDER];
        const orderIndex = new Map(order.map((name, index) => [name, index]));
        return scoped
            .map((tool) => ({
            ...tool,
            description: this.decorateToolDescription(tool.name, tool.description, profile),
        }))
            .sort((left, right) => {
            const leftIndex = orderIndex.get(left.name) ?? Number.MAX_SAFE_INTEGER;
            const rightIndex = orderIndex.get(right.name) ?? Number.MAX_SAFE_INTEGER;
            return leftIndex - rightIndex || left.name.localeCompare(right.name);
        });
    }
    decorateToolDescription(name, description, profile) {
        if (name === 'nexus_session_bootstrap') {
            return 'REQUIRED: Call this BEFORE any other Nexus tool or file operation. Recovers memory, context, catalog health, and execution plan. Returns recommended next step.';
        }
        if (name === 'nexus_orchestrate') {
            return 'REQUIRED after bootstrap: Pass the user\'s raw request. Nexus handles task decomposition, skill selection, worker allocation, and execution automatically. Do NOT manually explore files instead.';
        }
        if (name === 'nexus_plan_execution') {
            return 'Optional: Inspect the execution ledger before calling nexus_orchestrate. Skip unless you need pre-run visibility into what Nexus will choose.';
        }
        if (name === 'nexus_optimize_tokens') {
            return 'Always-on token optimization. Automatically generates token-saving reading plans for any file reads. Follow the plan exactly and do not bulk-read the repo.';
        }
        if (name === 'nexus_mindkit_check') {
            return 'MANDATORY before any file modification or destructive operation. Returns PASS/FAIL. Do NOT proceed if it returns FAIL.';
        }
        if (name === 'nexus_store_memory') {
            return 'MANDATORY after significant findings and at session end. Store root causes, architecture decisions, and reusable patterns.';
        }
        if (name === 'nexus_session_dna') {
            return 'MANDATORY at session end. Generates the handover snapshot for this session. Never skip this step.';
        }
        if (name === 'nexus_ghost_pass') {
            return 'MANDATORY before refactoring 3+ files. Provides a safe execution plan, risk map, and worker guidance.';
        }
        if (name === 'nexus_spawn_workers') {
            return 'Call when modifying 3+ interrelated files or when nexus_ghost_pass recommends parallel execution. Prefer nexus_orchestrate for automatic worker management.';
        }
        if (MANUAL_OR_DIAGNOSTIC_TOOLS.has(name) && profile === 'full') {
            return `Advanced/manual surface. ${description}`;
        }
        return description;
    }
    debugListTools(profile = this.getToolProfile()) {
        return this.finalizeToolDefinitions(this.buildToolDefinitions(), profile);
    }
    listTools(profile = this.getToolProfile()) {
        return this.finalizeToolDefinitions(this.buildToolDefinitions(), profile);
    }
    buildToolDefinitions() {
        return buildMcpToolDefinitions();
    }
    setupToolHandlers() {
        this.server.setRequestHandler(ListToolsRequestSchema, async () => {
            const profile = this.getToolProfile();
            setTimeout(() => {
                try {
                    this.getRuntime().recordClientInstructionStatus({
                        clientId: this.name,
                        clientFamily: this.name === 'openclaw' || this.name === 'antigravity' ? 'antigravity' : this.name,
                        toolProfile: profile,
                        status: profile === 'autonomous' ? 'guided' : 'manual',
                        summary: this.describeClientInstructionStatus(profile),
                        updatedAt: Date.now(),
                    });
                }
                catch {
                    // Tool enumeration must stay fast and side-effect best-effort.
                }
            }, 0);
            return {
                tools: this.listTools(profile),
            };
        });
        this.server.setRequestHandler(CallToolRequestSchema, async (request) => this.executeToolRequest(request));
        // Capture the MCP client's identity from the initialize handshake.
        // `oninitialized` fires after the server responds to `initialize` AND
        // the client sends the `initialized` notification — the handshake is
        // complete and `getClientVersion()` is reliable at this point.
        this.server.oninitialized = () => {
            const clientName = this.server.getClientVersion()?.name ?? '';
            if (clientName) {
                setMcpClientName(clientName);
                // Synchronously upgrade this.name so the very first tool call
                // already uses the authoritative client identity.
                void detectCallerAsync().then((n) => { this.name = n; });
            }
        };
    }
    async invokeTool(name, args = {}) {
        return this.executeToolRequest({
            params: {
                name,
                arguments: args,
            },
        });
    }
    async executeToolRequest(request) {
        if (!this.nexusRef) {
            throw new McpError(ErrorCode.InternalError, 'NexusPrime reference not set in MCP adapter.');
        }
        this.telemetry.recordCall();
        this.sessionDNA.recordToolCall();
        const toolName = String(request.params?.name ?? '');
        // Nested-loop guard via Architects sentinel — refuses nexus_session_bootstrap
        // when NEXUS_PARENT_DISPATCH is set, preventing infinite agent loops.
        const nestedGuard = ArchitectsSentinel.guardNestedDispatch(toolName);
        if (nestedGuard.ok === false) {
            return envelopeToMcpResponse({
                ok: false,
                error: nestedGuard.error,
                meta: { toolName, durationMs: 0, attempts: 1, runtimeHotAt: null },
            });
        }
        if (toolName === 'nexus_session_bootstrap') {
            this.telemetry.bootstrapped = true;
            // Emit a one-time dashboard URL banner on the first bootstrap call in this process.
            // Subsequent calls (e.g. from /loop or Antigravity) are silent.
            if (!process.env.NEXUS_BANNER_PRINTED) {
                process.env.NEXUS_BANNER_PRINTED = '1';
                const port = process.env.NEXUS_DASHBOARD_PORT ?? '3377';
                let repoBase = '';
                try {
                    repoBase = path.basename(resolveWorkspaceContext().repoRoot);
                }
                catch { /* ignore */ }
                drawCard({
                    title: 'NEXUS PRIME · READY',
                    lines: [
                        `Dashboard  http://localhost:${port}`,
                        `PID        ${process.pid}`,
                        `Adapter    ${this.name}`,
                        ...(repoBase ? [`Repo       ${repoBase}`] : []),
                    ],
                    color: '36',
                });
            }
        }
        const args = request.params.arguments ?? (request.params.arguments = {});
        // Gate: block non-bootstrap tools BEFORE executing them to avoid wasted work and side effects.
        // Check both the in-memory flag AND the durable bootstrap receipt (survives restarts).
        const hasDurableReceipt = (() => {
            try {
                const ws = resolveWorkspaceContext();
                const stateRoot = resolveWorkspaceStateRoot(ws.workspaceRoot);
                return hasValidBootstrapReceipt(stateRoot);
            }
            catch {
                return false;
            }
        })();
        if (hasDurableReceipt && !this.telemetry.bootstrapped) {
            // Restore in-memory flag from durable receipt
            this.telemetry.bootstrapped = true;
        }
        if (!this.telemetry.bootstrapped &&
            !PRE_BOOTSTRAP_ALLOWED_TOOLS.has(toolName)) {
            // Auto-bootstrap was removed in v6.0.0-rc.7 because it stacked
            // the cold n-gram index cost twice on the same tool call and
            // triggered Codex's 120s tool-call timeout. Required Sequence
            // already says the model must call nexus_session_bootstrap first;
            // we just stop the silent bypass here. Phase 1 will wire a
            // UserPromptSubmit hook so Claude Code never sees this in normal
            // operation. CLAUDE.md §2 hard rule #1 + §2 hard rule #4.
            return {
                content: [{
                        type: 'text',
                        text: JSON.stringify({
                            status: 'blocked',
                            reason: 'nexus_session_bootstrap has not been called for this session',
                            action: 'Call nexus_session_bootstrap(goal="<your task>") first, then retry this tool. Auto-bootstrap was removed in v6.0.0-rc.7 because it stacked the cold-index cost twice and triggered Codex\'s 120s timeout.',
                            hint: 'Bootstrap recovers memory and context — skipping it means working blind',
                        }, null, 2),
                    }],
            };
        }
        const preOrchestrationBlock = this.buildPreOrchestrationBlock(toolName);
        if (preOrchestrationBlock)
            return preOrchestrationBlock;
        const callId = `mcp_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
        const startTimeMs = Date.now();
        nexusEventBus.emit('mcp.call.start', {
            callId,
            serverName: 'nexus-prime',
            toolName,
            args,
        });
        const executionContext = {
            toolName,
            args: args,
            adapterName: this.name,
            projectHash: this.getToolProjectHash(args),
            currentTask: this.currentTask,
            startTimeMs,
            meta: {},
        };
        const envelope = await runHandler(toolName, () => this.middlewarePipeline.execute(executionContext, () => this.handleToolCall(request, executionContext)), {
            callerName: this.name,
            runtimeHotAt: this.nexusRef?.getRuntimeHotAt?.() ?? null,
            timeoutMs: toolName === 'nexus_session_bootstrap' ? 90_000 : undefined,
        });
        // Unified dispatch — envelopeToMcpResponse handles both ok and error shapes.
        // On success, if data is already a ToolResult ({ content: [...] }) it is
        // passed through directly; otherwise it is JSON-serialised into one text block.
        const result = envelopeToMcpResponse(envelope);
        const durationMs = Date.now() - startTimeMs;
        const resultPreview = envelope.ok ? buildResultPreview(result) : null;
        const asyncReceipt = envelope.ok ? this.parseAsyncReceipt(result) : null;
        nexusEventBus.emit('mcp.call.complete', {
            callId,
            serverName: 'nexus-prime',
            toolName,
            durationMs,
            resultByteSize: resultPreview
                ? Buffer.byteLength(JSON.stringify(resultPreview), 'utf8')
                : 0,
            ...(resultPreview
                ? { result: resultPreview }
                : { error: envelope.error.message }),
        });
        if (envelope.ok) {
            if (asyncReceipt?.queued) {
                this.telemetry.observeQueuedToolCall(toolName, args);
            }
            else {
                this.telemetry.observeSuccessfulToolCall(toolName, args);
                if (toolName === 'nexus_session_dna' && String(args.action ?? 'load') === 'generate') {
                    this.sessionLifecycleFinalized = true;
                }
            }
        }
        // Surface meaningful tool calls on the SSE feed; persist only high-signal outcomes.
        if (this.nexusRef) {
            const repoRoot = this.nexusRef.getWorkspaceContext?.()?.repoRoot ?? '';
            const repoName = repoRoot ? path.basename(repoRoot) : '';
            if (!isReadOnlyMcpTool(toolName)) {
                try {
                    nexusEventBus.emit('memory.snapshot', {
                        kind: 'tool-invocation',
                        toolName,
                        repo: repoName,
                        summary: buildAutoMemorySummary(toolName, args),
                    });
                }
                catch { /* best-effort */ }
            }
            this.maybeStoreToolOutcomeMemory(toolName, args, envelope, durationMs, repoName, asyncReceipt);
        }
        const decorated = this.decorateLifecycleResponse(toolName, result);
        return this.injectMemoryContext(toolName, args, decorated);
    }
    parseAsyncReceipt(result) {
        const text = result.content?.[0]?.text;
        if (!text)
            return null;
        try {
            const parsed = JSON.parse(String(text).split('\n\n')[0] ?? '{}');
            return parsed && typeof parsed === 'object' && parsed.queued === true ? parsed : null;
        }
        catch {
            return null;
        }
    }
    maybeStoreToolOutcomeMemory(toolName, args, envelope, durationMs, repoName, asyncReceipt) {
        if (!this.nexusRef || toolName === 'nexus_store_memory')
            return;
        if (asyncReceipt?.queued)
            return;
        const shouldPersistSuccess = [
            'nexus_orchestrate',
            'nexus_plan_execution',
            'nexus_session_dna',
            'nexus_ghost_pass',
            'nexus_mindkit_check',
        ].includes(toolName);
        if (envelope.ok && !shouldPersistSuccess)
            return;
        const error = envelope.error;
        const summary = envelope.ok
            ? `MCP ${toolName} completed in ${durationMs}ms. ${buildAutoMemorySummary(toolName, args)}`
            : `MCP ${toolName} failed after ${durationMs}ms: ${error?.code ?? 'error'} ${error?.message ?? 'unknown failure'}. ${buildAutoMemorySummary(toolName, args)}`;
        const tags = [
            '#mcp-result',
            '#auto',
            `#tool:${toolName}`,
            ...(repoName ? [`repo:${repoName}`, `#repo:${repoName}`] : []),
            ...(envelope.ok ? ['#workspace'] : ['#failure-mode', '#runtime-result', '#inbox']),
        ];
        try {
            this.nexusRef.storeMemory(summary.slice(0, 1200), envelope.ok ? 0.55 : 0.72, tags);
        }
        catch { /* best-effort */ }
    }
    buildHandlerCtx(args = {}) {
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        const self = this;
        return {
            get nexusRef() { return self.nexusRef; },
            getOrchestrator: (a = {}) => self.getOrchestrator(a),
            getRuntime: (a = {}) => self.getRuntime(a),
            getWorkspace: (a = {}) => self.getWorkspaceContextForArgs(a),
            getRepoNgramIndex: (a = {}) => self.getRepoNgramIndex(a),
            getDarwinLoop: (a = {}) => self.getDarwinLoop(a),
            getToolProfile: () => self.getToolProfile(),
            getDecoratedToolDefinitions: (profile) => self.getDecoratedToolDefinitions(profile),
            isPackageLikeWorkspace: (target) => self.isPackageLikeWorkspace(target),
            resolveToolPath: (p, a = {}) => self.resolveToolPath(p, a),
            telemetry: self.telemetry,
            sessionDNA: self.sessionDNA,
            box: (title, lines, color) => self.box(title, lines, color),
            sciFiMatrixLog: (title, metrics, intent) => self.sciFiMatrixLog(title, metrics, intent),
            scanSourceFiles: (cwd) => self.scanSourceFiles(cwd),
            formatProtocolChecklist: () => self.formatProtocolChecklist(),
            formatRemainingProtocolSteps: () => self.formatRemainingProtocolSteps(),
            getCurrentTask: () => self.currentTask,
            setCurrentTask: (task) => { self.currentTask = task; },
        };
    }
    async handleToolCall(request, ctx) {
        if (!this.nexusRef) {
            throw new McpError(ErrorCode.InternalError, 'NexusPrime reference not set.');
        }
        const args = request.params.arguments ?? {};
        const goal = String(args.goal ?? args.task ?? args.prompt ?? '');
        const toolName = String(request.params.name ?? '');
        const detailLevel = normalizeDetailLevel(args.detailLevel);
        const requestedIntent = normalizeResponseIntent(args.intent);
        const requestedTopology = normalizeResponseTopology(args.topology);
        const workspace = buildWorkspaceEnvelope(this.nexusRef.getWorkspaceContext());
        this.getRuntime().recordClientToolCall(toolName, {
            bootstrapCalled: toolName === 'nexus_session_bootstrap',
            orchestrateCalled: toolName === 'nexus_orchestrate',
            plannerCalled: toolName === 'nexus_plan_execution',
            tokenOptimizationApplied: toolName === 'nexus_optimize_tokens',
            detailLevel,
            intent: requestedIntent,
            topology: requestedTopology,
            toolProfile: this.getToolProfile(),
        });
        const toolCategory = this.classifyToolCategory(toolName);
        const showRichHeader = goal
            && goal.length > 20
            && !['nexus_session_bootstrap', 'nexus_plan_execution', 'nexus_memory_stats'].includes(request.params.name);
        if (showRichHeader) {
            const frameCount = Math.max(4, Math.min(8, Number(ASCII_ART.sciFiLoaderFrames?.length ?? 8)));
            for (let i = 0; i < frameCount; i++) {
                console.error(`\x1b[35m${ASCII_ART.sciFiLoader(i)}\x1b[0m`);
            }
            this.sciFiToolHeader(toolCategory, toolName, goal);
        }
        else if (toolName) {
            console.error(`\x1b[90m[NEXUS:${toolCategory.toUpperCase()}] ◈ ${toolName} ◈\x1b[0m`);
        }
        if (goal && goal.length > 50 && !['nexus_session_bootstrap', 'nexus_plan_execution'].includes(request.params.name)) {
            const swarm = await this.getOrchestrator().induce(goal);
            nexusEventBus.emit('nexusnet.sync', { newItemsCount: swarm.length });
        }
        // v6: Lazy-init Synapse/Architects on first tool call if not yet initialized.
        // Mandate + hire tools need Architects live so coordination bridge is set before
        // executeMandatePipeline runs (otherwise worklistId is null, orphaned teams).
        if (toolName.startsWith('nexus_synapse_')) {
            this.nexusRef.ensureSynapseInit();
            if (toolName === 'nexus_synapse_mandate' || toolName === 'nexus_synapse_assign_mission' || toolName === 'nexus_synapse_hire') {
                this.nexusRef.ensureArchitectsInit();
            }
        }
        const synapseResponse = await handleSynapseToolCall(toolName, args, this.nexusRef.getSynapse?.() ?? null);
        if (synapseResponse) {
            return synapseResponse;
        }
        if (toolName.startsWith('nexus_architects_')) {
            this.nexusRef.ensureArchitectsInit();
        }
        const architectsResponse = await handleArchitectsToolCall(toolName, args, this.nexusRef.getArchitects?.() ?? null);
        if (architectsResponse) {
            return architectsResponse;
        }
        // Handle embedded gstack skill calls
        const gstackBridge = getGstackBridge();
        if (gstackBridge.detect() && gstackBridge.listSkills().some(s => s.toolName === toolName)) {
            const result = await gstackBridge.execute(toolName, args);
            return {
                content: [{
                        type: 'text',
                        text: result.success
                            ? result.output
                            : `Execution failed (exit ${result.exitCode}): ${result.output.slice(0, 500)}`,
                    }],
            };
        }
        const hctx = this.buildHandlerCtx(args);
        // A1: auto-invoke optimize-tokens heuristic. When a file-heavy tool call
        // arrives and we haven't already optimized this session, run the token
        // engine inline (one-shot, never blocks the real call). Real savings
        // land in the token_ledger so the dashboard "lifetime tokens saved"
        // KPI starts incrementing on day-one work, not just after orchestrate.
        // Closes CLAUDE.md known-bug #10 properly.
        await this.maybeAutoOptimizeTokens(toolName, args);
        const dispatchResult = await dispatchMcpToolCall(hctx, request, args, ctx);
        if (dispatchResult)
            return dispatchResult;
    }
    /**
     * Auto-invoke `nexus_optimize_tokens` for file-heavy tool calls.
     * Triggers when args carry >=5 file paths AND optimization wasn't already
     * applied this session (per the runtime usage flag set by an explicit
     * nexus_optimize_tokens / nexus_orchestrate call).
     *
     * Skips: the optimize tool itself, bootstrap-allowed tools, read-only
     * tools (no file mutation), and the explicit `nexus_session_bootstrap`
     * (already optimized in its own path).
     *
     * Engine-level call (`getTokenEngine().plan`) — no MCP recursion. On
     * success, persists savings to token_ledger via memory.insertTokenTelemetry
     * and emits `tokens.optimized` so the dashboard KPI updates live.
     */
    async maybeAutoOptimizeTokens(toolName, args) {
        try {
            if (toolName === 'nexus_optimize_tokens' || toolName === 'nexus_session_bootstrap')
                return;
            if (PRE_BOOTSTRAP_ALLOWED_TOOLS.has(toolName))
                return;
            if (isReadOnlyMcpTool(toolName))
                return;
            // Skip if an explicit optimize/orchestrate already ran this session.
            const usage = this.nexusRef?.getRuntime?.()?.getUsageSnapshot?.();
            if (usage?.tokenOptimizationApplied) {
                this.telemetry.markTokenAutoApplied?.();
                return;
            }
            const fileRefs = this.extractFileRefsFromArgs(args);
            if (fileRefs.length < 5)
                return;
            const goal = String(args?.goal ?? args?.task ?? args?.prompt ?? toolName);
            const plan = await getTokenEngine().plan(goal, fileRefs);
            const savings = Number(plan?.savings ?? 0);
            if (savings <= 0)
                return;
            const totalEstimated = Number(plan?.totalEstimatedTokens ?? 0);
            const grossInput = totalEstimated + savings;
            const compressionRatio = grossInput > 0 ? totalEstimated / grossInput : 0;
            const pct = grossInput > 0 ? Math.round((savings / grossInput) * 100) : 0;
            // Mark applied so we don't repeat this for every subsequent tool call.
            this.telemetry.markTokenAutoApplied?.();
            try {
                this.nexusRef?.getRuntime?.()?.recordClientToolCall?.(toolName, {
                    tokenOptimizationApplied: true,
                    toolProfile: this.getToolProfile(),
                });
            }
            catch { /* runtime may not be wired in test harnesses */ }
            // Persist to global token_ledger so lifetime KPI grows.
            try {
                const memory = this.nexusRef?.getOrchestrator?.()?.getMemoryEngine?.();
                if (memory && typeof memory.insertTokenTelemetry === 'function') {
                    memory.insertTokenTelemetry({
                        sessionId: `auto-heuristic-${toolName}`,
                        task: goal.slice(0, 200),
                        model: 'auto-heuristic',
                        tokensOptimized: totalEstimated,
                        tokensSaved: savings,
                        tokensForwarded: totalEstimated,
                        compressionRatio,
                        fileCount: fileRefs.length,
                        usdValueSaved: (savings / 1000) * 0.0006,
                    });
                }
            }
            catch { /* best-effort */ }
            try {
                nexusEventBus.emit('tokens.optimized', {
                    savings,
                    pct,
                    files: fileRefs.length,
                    source: 'auto-heuristic',
                });
            }
            catch { /* best-effort */ }
        }
        catch { /* never fail the host tool call */ }
    }
    /** Pull file path lists out of common arg shapes. Returns up to 50 refs. */
    extractFileRefsFromArgs(args) {
        const candidates = [
            args?.files,
            args?.candidate_files,
            args?.candidateFiles,
            args?.file_paths,
            args?.filePaths,
            args?.paths,
        ];
        for (const cand of candidates) {
            if (!Array.isArray(cand))
                continue;
            const refs = cand
                .filter((p) => typeof p === 'string' && p.length > 0)
                .slice(0, 50)
                .map((p) => ({ path: p, sizeBytes: 0 }));
            if (refs.length > 0)
                return refs;
        }
        return [];
    }
    async scanSourceFiles(cwd) {
        return scanSourceFilesUtil(cwd, this.scanCache);
    }
    async connect() {
        const transport = new StdioServerTransport(getMcpStdioInput(), process.stdout);
        await this.server.connect(transport);
        this.connected = true;
        startWatchdog();
        console.error('[MCP Adapter] Connected — runtime tools active');
    }
    async finalizeSessionLifecycle(reason) {
        if (this.sessionLifecycleFinalized && reason !== 'explicit')
            return;
        const telemetry = this.telemetry.snapshot();
        try {
            const promoted = this.nexusRef?.runTierPromotion?.();
            if (promoted)
                nexusEventBus.emit('memory.tier.promoted', promoted);
        }
        catch (e) {
            nexusEventBus.emit('mcp.handler.failed', {
                toolName: 'nexus_session_dna.disconnect.tierPromotion',
                code: 'internal',
                attempts: 1,
                durationMs: 0,
            });
            console.error('[MCP Adapter] Failed to run Session DNA tier promotion:', e);
        }
        try {
            this.sessionDNA.syncFromTelemetry({
                callCount: telemetry.callCount,
                memoriesStored: telemetry.memoriesStored,
                memoriesRecalled: telemetry.memoriesRecalled,
                tokensOptimized: telemetry.tokensOptimized,
            });
            const dna = this.sessionDNA.flush();
            this.sessionLifecycleFinalized = true;
            try {
                const runtimeTokens = this.runtime?.getUsageSnapshot().tokens?.grossInputTokens ?? 0;
                this.getOrchestrator({}).persistSessionSummary(telemetry.tokensOptimized || runtimeTokens || 0);
            }
            catch (e) {
                nexusEventBus.emit('mcp.handler.failed', {
                    toolName: 'nexus_session_dna.disconnect.persistSessionSummary',
                    code: 'internal',
                    attempts: 1,
                    durationMs: 0,
                });
                console.error('[MCP Adapter] Failed to persist Session DNA summary:', e);
            }
            await getSharedTelemetry().trackSessionEnd(this.telemetry.elapsedMs(), {
                projectId: this.nexusRef?.getWorkspaceContext?.()?.repoRoot ?? '',
                tokensSaved: telemetry.tokensOptimized,
                memoriesCreated: telemetry.memoriesStored,
                memoriesRecalled: telemetry.memoriesRecalled,
                toolsUsed: telemetry.callCount,
                sessionId: dna.sessionId,
            }).catch((e) => {
                nexusEventBus.emit('mcp.handler.failed', {
                    toolName: 'nexus_session_dna.disconnect.trackSessionEnd',
                    code: 'internal',
                    attempts: 1,
                    durationMs: 0,
                });
                console.error('[MCP Adapter] Failed to track Session DNA session end:', e);
            });
            console.error(`[MCP Adapter] Session DNA finalized on ${reason}`);
        }
        catch (e) {
            console.error('[MCP Adapter] Failed to finalize Session DNA:', e);
        }
    }
    async disconnect() {
        await this.finalizeSessionLifecycle('disconnect');
        await this.server.close();
        this.connected = false;
        console.error('[MCP Adapter] Disconnected');
    }
    async send(message) {
        if (!this.connected)
            return;
        try {
            await this.server.notification({
                method: 'nexus/network_message',
                params: { message }
            });
        }
        catch (e) {
            console.error('[MCP Adapter] Failed to route outbound message', e);
        }
    }
    receive(message) {
        try {
            console.error(`[MCP Adapter] Received network message from ${message.sender} of type ${message.type}`);
        }
        catch (e) {
            console.error('[MCP Adapter] Failed to process incoming network message:', e);
        }
    }
}
