import * as fs from 'fs';
import * as path from 'path';
import { createHash } from 'crypto';
import { resolveWorkspaceContext } from './workspace-resolver.js';
export const PACKET_TOKEN_LIMIT = 3500;
export const CLIENT_BOOTSTRAP_TOKEN_LIMITS = {
    antigravity: 1200,
    hermes: 1600,
    nanoclaw: 1200,
    picoclaw: 1200,
    cursor: 1800,
    windsurf: 1800,
    'claude-code': 2000,
    opencode: 2000,
    codex: 1800,
    mcp: 1800,
};
export const DEFAULT_REQUIRED_SEQUENCE = [
    'identify-client-session',
    'recall-memory',
    'memory-stats',
    'planner-selection',
    'catalog-shortlist',
    'candidate-file-discovery',
    'knowledge-fabric',
    'token-optimization',
    'governance-preflight',
    'compile-instruction-packet',
    'runtime-execution',
    'structured-learning',
];
export const CLIENT_BOOTSTRAP_SEQUENCE = [
    'MUST call `nexus_session_bootstrap(goal, files?)` then `nexus_orchestrate(prompt)` before any non-trivial task.',
    'MUST call `nexus_mindkit_check(action, files)` before mutations; `nexus_ghost_pass(goal, files)` before 3+ file refactors.',
    'Memory hooks are supported: MUST call `nexus_store_memory(content, priority, tags)` after significant findings, user preferences, decisions, pivots, and verification results; never store secrets; call `nexus_session_dna("generate")` at session end.',
    'Use fenced code blocks for work packets, implementation notes, verification output, and memory write-back so downstream agents can parse the handoff cleanly.',
    'Use `nexus_describe_tool(id)` to load skill/workflow details on demand. Let Nexus select assets by default.',
    'Use `nexus_list_skills`, `nexus_list_specialists`, and `nexus_work_hire`/`nexus_work_enqueue` when a client needs skills, specialists, Synapse-style hiring, or workforce delegation.',
    'When workers are selected, state exactly what is being hired: crew, specialists, workflow, model tier, budget guardrail, and verification gate.',
    'If a tool returns `{ "queued": true, "runId": "..." }`, call `nexus_run_status(runId)` every ~5 seconds until status is `completed` or `failed`, then inspect the `result` field.',
    'Repo graph is internal to Nexus. Do not expose or call a separate `code-review-graph` MCP server from client configs.',
    'The n-gram index is active as a bounded lexical fallback for memory, skills, repo search, and graph-unavailable routing.',
    'A mutate run with no mutation bindings and no candidate diff is advisory inspected, not a failed implementation swarm.',
];
const LEDGER_STEP_LABELS = {
    'identify-client-session': 'Identify client and session',
    'recall-memory': 'Recall relevant memory',
    'memory-stats': 'Inspect memory stats',
    'planner-selection': 'Run planner selection',
    'catalog-shortlist': 'Build catalog shortlist',
    'candidate-file-discovery': 'Discover candidate files',
    'knowledge-fabric': 'Assemble knowledge fabric bundle',
    'token-optimization': 'Apply token optimization',
    'governance-preflight': 'Run governance preflight',
    'compile-instruction-packet': 'Compile instruction packet',
    'runtime-execution': 'Execute runtime',
    'structured-learning': 'Store structured learning',
};
const PROTOCOL_SOURCES = [
    { source: 'AGENTS.md', relativePath: 'AGENTS.md' },
    { source: '.agent/rules/core-rules.md', relativePath: path.join('.agent', 'rules', 'core-rules.md') },
    { source: '.agent/rules/quality-gates.md', relativePath: path.join('.agent', 'rules', 'quality-gates.md') },
    { source: '.agent/rules/agent-guardrails.md', relativePath: path.join('.agent', 'rules', 'agent-guardrails.md') },
];
const SKILL_BUNDLE_CLIENT_FAMILIES = new Set(['antigravity', 'hermes', 'nanoclaw', 'picoclaw']);
export function estimateInstructionTokens(value) {
    return Math.max(1, Math.ceil(String(value || '').length / 4));
}
export function createExecutionLedger(input) {
    const now = Date.now();
    return {
        sessionId: input.sessionId,
        task: input.task,
        executionMode: input.executionMode,
        clientId: input.clientId,
        clientFamily: input.clientFamily,
        plannerApplied: false,
        tokenOptimizationApplied: false,
        lastUpdatedAt: now,
        steps: DEFAULT_REQUIRED_SEQUENCE.map((id) => ({
            id: id,
            label: LEDGER_STEP_LABELS[id],
            status: 'pending',
            updatedAt: now,
        })),
    };
}
export function markExecutionLedgerStep(ledger, stepId, status, patch = {}) {
    const now = Date.now();
    const step = ledger.steps.find((entry) => entry.id === stepId);
    if (!step) {
        ledger.steps.push({
            id: stepId,
            label: LEDGER_STEP_LABELS[stepId],
            status,
            updatedAt: now,
            ...patch,
        });
    }
    else {
        step.status = status;
        step.updatedAt = now;
        step.reason = patch.reason ?? step.reason;
        step.summary = patch.summary ?? step.summary;
        step.details = patch.details ?? step.details;
    }
    if (stepId === 'planner-selection' && status === 'completed') {
        ledger.plannerApplied = true;
    }
    if (stepId === 'token-optimization' && status === 'completed') {
        ledger.tokenOptimizationApplied = true;
    }
    ledger.lastUpdatedAt = now;
    return ledger;
}
function renderSelection(summary) {
    if (!summary.id && !summary.name)
        return undefined;
    return {
        id: String(summary.id ?? summary.name ?? ''),
        name: String(summary.name ?? summary.id ?? ''),
        summary: summary.summary,
    };
}
function normalizeSelectionList(values, summaryField) {
    return values.map((value) => ({
        id: String(value.skillId ?? value.workflowId ?? value.hookId ?? value.automationId ?? value.specialistId ?? value.crewId ?? value.name ?? ''),
        name: String(value.name ?? value.specialistName ?? value.crewName ?? value.skillId ?? value.workflowId ?? value.hookId ?? value.automationId ?? value.specialistId ?? ''),
        summary: summaryField && typeof value[summaryField] === 'string' ? String(value[summaryField]) : undefined,
    }));
}
function normalizeHeading(value) {
    return value.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
}
function hashValue(value) {
    return createHash('sha1').update(value).digest('hex');
}
function renderList(values, fallback = 'none') {
    return values.length ? values.join(', ') : fallback;
}
function buildCodeQualityBlocks(input) {
    const selectedFiles = dedupeStrings(input.tokenPolicy?.selectedFiles ?? []);
    const verifyCommands = dedupeStrings([
        ...(input.manualOverrides ?? []).filter((entry) => /npm|pnpm|yarn|test|lint|build|cargo|pytest/i.test(entry)),
    ]);
    const crewName = input.selectedCrew?.name ?? 'baseline';
    const specialists = (input.selectedSpecialists ?? []).map((specialist) => specialist.name).filter(Boolean);
    return [
        {
            id: 'work-packet',
            title: 'Work Packet',
            language: 'yaml',
            purpose: 'Stable handoff for a hired worker or sub-agent.',
            content: [
                `goal: ${JSON.stringify(input.goal)}`,
                `crew: ${JSON.stringify(crewName)}`,
                `specialists: [${specialists.map((item) => JSON.stringify(item)).join(', ')}]`,
                `owned_files: [${selectedFiles.map((item) => JSON.stringify(item)).join(', ')}]`,
                `non_goals: ["unrelated refactors", "secret reads", "destructive git changes"]`,
                `done_when: ["diff is scoped", "verification recorded", "memory write-back prepared"]`,
            ].join('\n'),
        },
        {
            id: 'implementation-notes',
            title: 'Implementation Notes',
            language: 'markdown',
            purpose: 'Keep coding quality explicit before patching.',
            content: [
                '### Why',
                '<one paragraph explaining the product/runtime reason>',
                '',
                '### What Changed',
                '<short list of files and behavior changed>',
                '',
                '### Risk',
                '<blast radius, rollback path, and compatibility notes>',
            ].join('\n'),
        },
        {
            id: 'verification-output',
            title: 'Verification Output',
            language: 'text',
            purpose: 'Separate real command proof from assumptions.',
            content: [
                `commands: ${renderList(verifyCommands, 'project-specific targeted tests, then build/lint when feasible')}`,
                'passed: <true|false>',
                'known_failures: <pre-existing or new failure with file/test name>',
                'not_run: <commands skipped and why>',
            ].join('\n'),
        },
        {
            id: 'memory-writeback',
            title: 'Memory Write-Back',
            language: 'markdown',
            purpose: 'Reusable project learning without raw secrets or noisy logs.',
            content: [
                'Session YYYY-MM-DD: <what changed, why, and what remains>.',
                '',
                'Tags: #session-summary #token-budget #orchestration',
                'Priority: 0.85',
                'Do not store secrets, raw credentials, or full logs.',
            ].join('\n'),
        },
    ];
}
function sectionPriority(source, heading) {
    const normalized = normalizeHeading(`${source} ${heading}`);
    if (normalized.includes('default operating rule'))
        return 0;
    if (normalized.includes('context acquisition order'))
        return 1;
    if (normalized.includes('before reading files'))
        return 2;
    if (normalized.includes('before risky operations'))
        return 3;
    if (normalized.includes('anti patterns'))
        return 4;
    if (normalized.includes('quality gate'))
        return 5;
    if (normalized.includes('guardrail'))
        return 6;
    if (normalized.includes('memory'))
        return 7;
    return 20;
}
function splitMarkdownSections(source, markdown) {
    const lines = markdown.split('\n');
    const sections = [];
    let current = { heading: source, lines: [] };
    for (const line of lines) {
        if (/^#{1,6}\s+/.test(line)) {
            if (current.lines.length > 0)
                sections.push(current);
            current = { heading: line.replace(/^#{1,6}\s+/, '').trim(), lines: [] };
            continue;
        }
        current.lines.push(line);
    }
    if (current.lines.length > 0)
        sections.push(current);
    return sections
        .map((section) => {
        const content = section.lines.join('\n').trim();
        const signature = `${normalizeHeading(section.heading)}\n${content}`;
        return {
            source,
            heading: section.heading,
            content,
            estimatedTokens: estimateInstructionTokens(content),
            priority: sectionPriority(source, section.heading),
            hash: hashValue(signature),
        };
    })
        .filter((section) => section.content.length > 0);
}
function truncateSection(section, maxTokens) {
    if (section.estimatedTokens <= maxTokens)
        return section;
    if (maxTokens < 48)
        return undefined;
    const maxChars = Math.max(160, maxTokens * 4);
    return {
        ...section,
        content: `${section.content.slice(0, maxChars).trim()}\n\n[truncated to fit packet budget]`,
        estimatedTokens: estimateInstructionTokens(section.content.slice(0, maxChars)),
        hash: hashValue(`${section.heading}:${section.content.slice(0, maxChars)}`),
    };
}
export class InstructionGateway {
    repoRoot;
    packetsDir;
    constructor(repoRoot = resolveWorkspaceContext().repoRoot, stateRoot) {
        const workspace = resolveWorkspaceContext({ workspaceRoot: repoRoot });
        this.repoRoot = workspace.repoRoot;
        this.packetsDir = path.join(stateRoot ?? workspace.stateRoot, 'instruction-packets');
        void fs.promises.mkdir(this.packetsDir, { recursive: true });
    }
    compile(input) {
        const protocolSections = this.selectProtocolSections(input);
        const packet = {
            session: {
                runtimeId: input.runtimeId,
                sessionId: input.sessionId,
                objectiveHistory: (input.objectiveHistory ?? []).slice(0, 8),
                phases: input.phases ?? [],
            },
            client: input.client ? {
                clientId: input.client.clientId,
                family: this.toClientFamily(input.client.clientId),
                displayName: input.client.displayName,
                state: input.client.state,
                source: input.client.source,
            } : undefined,
            task: {
                goal: input.goal,
                executionMode: input.executionMode,
                manualOverrides: dedupeStrings(input.manualOverrides ?? []),
            },
            operatingRule: 'Treat Nexus Prime as an orchestrator-first control plane. Use the compiled packet, not raw repo-wide docs, as the execution brief.',
            requiredSequence: input.requiredSequence ?? DEFAULT_REQUIRED_SEQUENCE,
            selectedCrew: renderSelection({
                id: input.selectedCrew?.crewId,
                name: input.selectedCrew?.name,
                summary: input.selectedCrew?.summary,
            }),
            selectedSpecialists: normalizeSelectionList((input.selectedSpecialists ?? []), 'mission'),
            selectedSkills: normalizeSelectionList((input.selectedSkills ?? []), ''),
            selectedWorkflows: normalizeSelectionList((input.selectedWorkflows ?? []), ''),
            selectedHooks: normalizeSelectionList((input.selectedHooks ?? []), ''),
            selectedAutomations: normalizeSelectionList((input.selectedAutomations ?? []), ''),
            governance: {
                passed: input.governance?.passed ?? true,
                score: input.governance?.score ?? 0,
                violations: input.governance?.violations ?? [],
                suggestions: input.governance?.suggestions ?? [],
            },
            federation: {
                activePeerLinks: Number(input.federation?.activePeerLinks ?? 0),
                knownPeers: Number(input.federation?.knownPeers ?? 0),
                tracesPublished: Number(input.federation?.tracesPublished ?? 0),
                relayConfigured: Boolean(input.federation?.relay.configured),
                relayMode: input.federation?.relay.mode ?? 'degraded',
                relayLastError: input.federation?.relay.lastError,
            },
            tokenPolicy: {
                applied: Boolean(input.tokenPolicy?.applied),
                reason: input.tokenPolicy?.reason ?? 'No token policy recorded.',
                candidateFiles: (input.tokenPolicy?.candidateFiles ?? []).slice(0, 16),
                selectedFiles: (input.tokenPolicy?.selectedFiles ?? []).slice(0, 16),
                estimatedSavings: Number(input.tokenPolicy?.estimatedSavings ?? 0),
                estimatedCompressionPct: Number(input.tokenPolicy?.estimatedCompressionPct ?? 0),
                codeBlockTemplates: (input.tokenPolicy?.codeBlockTemplates ?? [
                    'work-packet',
                    'implementation-notes',
                    'verification-output',
                    'memory-writeback',
                ]).slice(0, 8),
                codeBlockReservedTokens: Number(input.tokenPolicy?.codeBlockReservedTokens ?? 0),
            },
            codeQualityBlocks: buildCodeQualityBlocks(input),
            memoryContext: {
                matches: (input.memoryMatches ?? []).slice(0, 3),
                stats: input.memoryStats ? {
                    prefrontal: input.memoryStats.prefrontal,
                    hippocampus: input.memoryStats.hippocampus,
                    cortex: input.memoryStats.cortex,
                    totalLinks: input.memoryStats.totalLinks,
                } : undefined,
            },
            manualOverrides: dedupeStrings(input.manualOverrides ?? []),
            catalogShortlist: {
                skills: dedupeStrings(input.catalogShortlist?.skills ?? []).slice(0, 2),
                workflows: dedupeStrings(input.catalogShortlist?.workflows ?? []).slice(0, 2),
                hooks: dedupeStrings(input.catalogShortlist?.hooks ?? []).slice(0, 2),
                automations: dedupeStrings(input.catalogShortlist?.automations ?? []).slice(0, 2),
                specialists: dedupeStrings(input.catalogShortlist?.specialists ?? []).slice(0, 3),
                crews: dedupeStrings(input.catalogShortlist?.crews ?? []).slice(0, 2),
            },
            knowledgeFabric: input.knowledgeFabric ? {
                summary: input.knowledgeFabric.summary,
                dominantSource: input.knowledgeFabric.sourceMix.dominantSource,
                sourceMix: input.knowledgeFabric.sourceMix,
                attachedCollections: input.knowledgeFabric.rag.attachedCollections.map((collection) => ({
                    collectionId: collection.collectionId,
                    name: collection.name,
                })).slice(0, 4),
                patternHits: input.knowledgeFabric.patterns.selected.map((pattern) => ({
                    patternId: pattern.patternId,
                    name: pattern.name,
                    score: pattern.score,
                })).slice(0, 5),
                modelTierTrace: input.knowledgeFabric.modelTierTrace.slice(0, 8),
            } : undefined,
            protocol: {
                sources: PROTOCOL_SOURCES.map((entry) => entry.source),
                sections: protocolSections.map((section) => ({
                    source: section.source,
                    heading: section.heading,
                    content: section.content,
                })),
                markdown: protocolSections
                    .map((section) => `## ${section.heading}\n${section.content}`)
                    .join('\n\n')
                    .trim(),
            },
            estimatedTokens: 0,
            packetHash: '',
        };
        const unsigned = {
            ...packet,
            estimatedTokens: undefined,
            packetHash: undefined,
        };
        const estimatedTokens = estimateInstructionTokens(JSON.stringify(unsigned));
        const packetHash = hashValue(JSON.stringify(unsigned));
        return {
            ...packet,
            estimatedTokens,
            packetHash,
        };
    }
    persist(packet, repoRoot = this.repoRoot) {
        const runtimeDir = path.join(this.packetsDir, packet.session.runtimeId);
        const stateJsonPath = path.join(runtimeDir, 'packet.json');
        const stateMarkdownPath = path.join(runtimeDir, 'packet.md');
        // Fire-and-forget: packet files are advisory metadata, callers only need the paths.
        void fs.promises.mkdir(runtimeDir, { recursive: true }).then(() => Promise.all([
            fs.promises.writeFile(stateJsonPath, JSON.stringify(packet, null, 2), 'utf8'),
            fs.promises.writeFile(stateMarkdownPath, renderInstructionPacketMarkdown(packet), 'utf8'),
        ])).catch(() => { });
        let workspaceJsonPath;
        let workspaceMarkdownPath;
        const isRoot = repoRoot === '/' || (repoRoot && path.resolve(repoRoot) === path.resolve('/'));
        if (repoRoot && !isRoot) {
            const workspaceRuntimeDir = path.join(repoRoot, '.agent', 'runtime');
            workspaceJsonPath = path.join(workspaceRuntimeDir, 'packet.json');
            workspaceMarkdownPath = path.join(workspaceRuntimeDir, 'packet.md');
            // Sync write: callers read this file immediately after execute() returns.
            // Fire-and-forget async races the reader and causes truncated JSON.parse failures.
            try {
                fs.mkdirSync(workspaceRuntimeDir, { recursive: true });
                fs.writeFileSync(workspaceJsonPath, JSON.stringify(packet, null, 2), 'utf8');
                fs.writeFileSync(workspaceMarkdownPath, renderInstructionPacketMarkdown(packet), 'utf8');
            }
            catch (err) {
                console.error(`[nexus-prime] Failed to persist instruction packet to workspace: ${err}`);
            }
        }
        else if (isRoot) {
            console.error('[nexus-prime] Persistence to system root is disabled for stability.');
        }
        return { stateJsonPath, stateMarkdownPath, workspaceJsonPath, workspaceMarkdownPath };
    }
    renderEnvelope(packet, clientId) {
        const family = this.toClientFamily(clientId ?? packet.client?.clientId);
        const markdown = renderInstructionPacketMarkdown(packet);
        if (SKILL_BUNDLE_CLIENT_FAMILIES.has(family)) {
            return {
                clientFamily: family,
                format: 'skill-md',
                packetHash: packet.packetHash,
                content: `# Nexus Prime Runtime Packet\n\n${markdown}`,
            };
        }
        if (family === 'cursor') {
            return {
                clientFamily: family,
                format: 'mdc',
                packetHash: packet.packetHash,
                content: `---\ndescription: Nexus Prime compiled instruction packet\nalwaysApply: true\n---\n\n${markdown}`,
            };
        }
        if (family === 'windsurf') {
            return {
                clientFamily: family,
                format: 'windsurfrules',
                packetHash: packet.packetHash,
                content: `# Nexus Prime Packet (${packet.packetHash})\n\n${markdown}`,
            };
        }
        return {
            clientFamily: family,
            format: 'markdown',
            packetHash: packet.packetHash,
            content: markdown,
        };
    }
    renderClientBootstrapBundle(clientId, options = {}) {
        const family = this.toClientFamily(clientId);
        const displayName = this.displayNameForClient(family);
        const packet = this.compile({
            runtimeId: `client-bootstrap-${family}`,
            sessionId: `client-bootstrap-${family}`,
            goal: options.goal ?? `Bootstrap ${displayName} to use Nexus Prime through the default MCP sequence.`,
            executionMode: 'autonomous',
            packetTokenLimit: CLIENT_BOOTSTRAP_TOKEN_LIMITS[family] ?? 1800,
            objectiveHistory: [
                'Prefer nexus_session_bootstrap first',
                'Prefer nexus_orchestrate for non-trivial work',
            ],
            phases: ['bootstrap', 'orchestrate', 'close'],
            requiredSequence: CLIENT_BOOTSTRAP_SEQUENCE,
            client: {
                clientId: family,
                clientFamily: family,
                displayName,
                state: 'installed',
                source: 'setup',
                confidence: 1,
                evidence: [`tool-profile:${options.toolProfile ?? 'autonomous'}`],
            },
            governance: {
                passed: true,
                score: 100,
                violations: [],
                suggestions: [],
            },
            tokenPolicy: {
                applied: true,
                reason: `client-bootstrap-${options.toolProfile ?? 'autonomous'}`,
                candidateFiles: [],
                selectedFiles: [],
                estimatedSavings: 0,
                estimatedCompressionPct: 0,
            },
            manualOverrides: ['client-bootstrap'],
            memoryMatches: [],
        });
        const envelope = this.renderEnvelope(packet, family);
        return {
            clientId,
            clientFamily: family,
            packetHash: packet.packetHash,
            estimatedTokens: packet.estimatedTokens,
            artifacts: this.renderBootstrapArtifacts(family, envelope, CLIENT_BOOTSTRAP_TOKEN_LIMITS[family] ?? 1800),
        };
    }
    selectProtocolSections(input) {
        const allSections = [];
        const seen = new Set();
        const seenContent = new Set(); // Deduplicate identical content across sources
        for (const source of PROTOCOL_SOURCES) {
            const target = path.join(this.repoRoot, source.relativePath);
            if (!fs.existsSync(target))
                continue;
            const markdown = fs.readFileSync(target, 'utf8');
            for (const section of splitMarkdownSections(source.source, markdown)) {
                const key = `${normalizeHeading(section.heading)}:${section.hash}`;
                if (seen.has(key))
                    continue;
                // Content-level dedup: skip sections whose content appeared under a different heading
                if (seenContent.has(section.hash))
                    continue;
                seen.add(key);
                seenContent.add(section.hash);
                allSections.push(section);
            }
        }
        allSections.sort((left, right) => left.priority - right.priority
            || left.source.localeCompare(right.source)
            || left.heading.localeCompare(right.heading));
        const baseOverhead = estimateInstructionTokens(JSON.stringify({
            goal: input.goal,
            selectedSkills: input.selectedSkills?.map((skill) => skill.name),
            selectedWorkflows: input.selectedWorkflows?.map((workflow) => workflow.name),
            selectedHooks: input.selectedHooks?.map((hook) => hook.name),
            selectedAutomations: input.selectedAutomations?.map((automation) => automation.name),
            selectedSpecialists: input.selectedSpecialists?.map((specialist) => specialist.name),
            manualOverrides: input.manualOverrides ?? [],
            memoryMatches: input.memoryMatches ?? [],
            knowledgeFabric: input.knowledgeFabric ? {
                summary: input.knowledgeFabric.summary,
                dominantSource: input.knowledgeFabric.sourceMix.dominantSource,
                sourceMix: input.knowledgeFabric.sourceMix.weights,
                patternHits: input.knowledgeFabric.patterns.selected.map((pattern) => pattern.name),
            } : undefined,
        }));
        let remaining = Math.max(320, (input.packetTokenLimit ?? PACKET_TOKEN_LIMIT) - baseOverhead);
        const selected = [];
        for (const section of allSections) {
            if (remaining <= 0)
                break;
            const next = truncateSection(section, remaining);
            if (!next)
                continue;
            selected.push(next);
            remaining -= next.estimatedTokens;
        }
        return selected;
    }
    toClientFamily(clientId) {
        const normalized = String(clientId ?? 'codex').toLowerCase();
        if (normalized === 'openclaw' || normalized === 'antigravity')
            return 'antigravity';
        if (normalized === 'hermes')
            return 'hermes';
        if (normalized === 'nanoclaw')
            return 'nanoclaw';
        if (normalized === 'picoclaw')
            return 'picoclaw';
        if (normalized === 'claude-code')
            return 'claude-code';
        if (normalized === 'opencode')
            return 'opencode';
        if (normalized === 'cursor')
            return 'cursor';
        if (normalized === 'windsurf')
            return 'windsurf';
        if (normalized === 'mcp')
            return 'mcp';
        return normalized || 'codex';
    }
    displayNameForClient(clientId) {
        const family = this.toClientFamily(clientId);
        if (family === 'antigravity')
            return 'Antigravity / OpenClaw';
        if (family === 'hermes')
            return 'Hermes';
        if (family === 'nanoclaw')
            return 'NanoClaw';
        if (family === 'picoclaw')
            return 'PicoClaw';
        if (family === 'claude-code')
            return 'Claude Code';
        if (family === 'opencode')
            return 'Opencode';
        if (family === 'cursor')
            return 'Cursor';
        if (family === 'windsurf')
            return 'Windsurf';
        if (family === 'mcp')
            return 'MCP Client';
        return family.charAt(0).toUpperCase() + family.slice(1);
    }
    renderBootstrapArtifacts(family, envelope, tokenLimit) {
        if (SKILL_BUNDLE_CLIENT_FAMILIES.has(family)) {
            const chunks = this.chunkContent(envelope.content, tokenLimit);
            return chunks.map((chunk, index) => ({
                fileName: index === 0 ? 'SKILL.md' : `SKILL-${String(index + 1).padStart(2, '0')}.md`,
                format: 'skill-md',
                content: [
                    '---',
                    `name: nexus-prime-bootstrap${index === 0 ? '' : `-${index + 1}`}`,
                    `description: Nexus Prime bootstrap instructions (${index + 1}/${chunks.length})`,
                    'risk: low',
                    'source: nexus-prime',
                    '---',
                    '',
                    chunk,
                ].join('\n'),
            }));
        }
        if (family === 'cursor') {
            return [{ fileName: 'nexus-prime.mdc', format: 'mdc', content: envelope.content }];
        }
        if (family === 'codex') {
            return [{ fileName: 'AGENTS.md', format: 'markdown', content: this.renderCompactCodexBootstrap() }];
        }
        if (family === 'windsurf') {
            return [{ fileName: '.windsurfrules', format: 'windsurfrules', content: envelope.content }];
        }
        return [{ fileName: `${family}.md`, format: 'markdown', content: envelope.content }];
    }
    chunkContent(content, maxTokens) {
        const maxChars = Math.max(1200, maxTokens * 4);
        if (content.length <= maxChars)
            return [content];
        const blocks = content.split(/\n(?=##\s+)/g);
        const chunks = [];
        let current = '';
        for (const block of blocks) {
            const candidate = current ? `${current}\n${block}` : block;
            if (candidate.length <= maxChars) {
                current = candidate;
                continue;
            }
            if (current.trim())
                chunks.push(current.trim());
            if (block.length <= maxChars) {
                current = block;
                continue;
            }
            const lines = block.split('\n');
            let partial = '';
            for (const line of lines) {
                const nextLine = partial ? `${partial}\n${line}` : line;
                if (nextLine.length > maxChars && partial.trim()) {
                    chunks.push(partial.trim());
                    partial = line;
                }
                else {
                    partial = nextLine;
                }
            }
            current = partial;
        }
        if (current.trim())
            chunks.push(current.trim());
        return chunks;
    }
    renderCompactCodexBootstrap() {
        return [
            '## Nexus Prime Managed Bootstrap',
            '',
            '- REQUIRED session start: invoke the Nexus Prime MCP tool `nexus_session_bootstrap(goal, files?)`, then invoke the Nexus Prime MCP tool `nexus_orchestrate(prompt=<raw user request>)`.',
            '- These are MCP tool invocations, not shell commands.',
            '- `nexus_orchestrate` does NOT replace during-work or session-close lifecycle steps.',
            '- Use `nexus_plan_execution` only when a plan-before-run is requested.',
            '- Prefer repo-local skills/workflows from `.agents/skills` + `.agents/workflows`; fall back to `.agent/skills` + `.agent/workflows` for older repos.',
            '- Discover catalogs only when needed: `nexus_list_skills`, `nexus_list_workflows`, `nexus_list_hooks`, `nexus_list_automations`, `nexus_list_specialists`, `nexus_list_crews`.',
            '- REQUIRED before broad reading when bootstrap did not auto-apply: call `nexus_optimize_tokens(goal, files)`.',
            '- REQUIRED before file modification or destructive work: call `nexus_mindkit_check(action, filesToModify)`.',
            '- REQUIRED before refactoring 3+ files: call `nexus_ghost_pass(goal, files)`.',
            '- Memory hooks are supported: REQUIRED after significant findings, user preferences, decisions, pivots, and verification results: call `nexus_store_memory(content, priority, tags)` without secrets.',
            '- Use fenced code blocks for work packets, implementation notes, verification output, and memory write-back.',
            '- When Nexus selects workers, tell the user exactly what is being hired: crew, specialists, workflow, model tier, budget guardrail, and verification gate.',
            '- REQUIRED before ending the session: call `nexus_session_dna(action="generate")`.',
            '- Repo graph is internal to Nexus; do not expose a separate `code-review-graph` MCP server.',
            '- The n-gram index is active as a bounded lexical fallback for memory, skills, repo search, and graph-unavailable routing.',
            '- No-action/no-diff mutate runs are advisory inspected outcomes, not failed implementation swarms.',
            '- Worker context lives in `.agent/runtime/context.json`; the compiled packet lives in `.agent/runtime/packet.json`.',
        ].join('\n');
    }
}
export function renderInstructionPacketMarkdown(packet) {
    const lines = [
        `# Nexus Prime Instruction Packet`,
        ``,
        `- Packet Hash: ${packet.packetHash}`,
        `- Runtime: ${packet.session.runtimeId}`,
        `- Session: ${packet.session.sessionId}`,
        `- Client: ${packet.client?.displayName ?? 'unknown'}`,
        `- Mode: ${packet.task.executionMode}`,
        ``,
        `## Task`,
        packet.task.goal,
        ``,
        `## Operating Rule`,
        packet.operatingRule,
        ``,
        `## Required Sequence`,
        ...packet.requiredSequence.map((step) => `- ${step}`),
        ``,
        `## Selected Assets`,
        packet.selectedCrew ? `- Crew: ${packet.selectedCrew.name}` : `- Crew: none`,
        `- Specialists: ${packet.selectedSpecialists.map((item) => item.name).join(', ') || 'none'}`,
        `- Skills: ${packet.selectedSkills.map((item) => item.name).join(', ') || 'none'}`,
        `- Workflows: ${packet.selectedWorkflows.map((item) => item.name).join(', ') || 'none'}`,
        `- Hooks: ${packet.selectedHooks.map((item) => item.name).join(', ') || 'none'}`,
        `- Automations: ${packet.selectedAutomations.map((item) => item.name).join(', ') || 'none'}`,
        ``,
        `## Token Policy`,
        `- Applied: ${packet.tokenPolicy.applied}`,
        `- Reason: ${packet.tokenPolicy.reason}`,
        `- Candidate Files: ${packet.tokenPolicy.candidateFiles.join(', ') || 'none'}`,
        `- Selected Files: ${packet.tokenPolicy.selectedFiles.join(', ') || 'none'}`,
        `- Estimated Savings: ${packet.tokenPolicy.estimatedSavings}`,
        `- Estimated Compression: ${packet.tokenPolicy.estimatedCompressionPct}%`,
        `- Code Block Templates: ${packet.tokenPolicy.codeBlockTemplates?.join(', ') || 'none'}`,
        `- Code Block Reserve: ${packet.tokenPolicy.codeBlockReservedTokens ?? 0}`,
        ``,
        `## Coding Quality Blocks`,
        ...packet.codeQualityBlocks.flatMap((block) => [
            `### ${block.title}`,
            block.purpose,
            '```' + block.language,
            block.content,
            '```',
            ``,
        ]),
        ``,
        `## Governance`,
        `- Passed: ${packet.governance.passed}`,
        `- Score: ${packet.governance.score}`,
        `- Violations: ${packet.governance.violations.join(', ') || 'none'}`,
        `- Suggestions: ${packet.governance.suggestions.join(', ') || 'none'}`,
        ``,
        `## Federation`,
        `- Active Links: ${packet.federation.activePeerLinks}`,
        `- Known Peers: ${packet.federation.knownPeers}`,
        `- Relay Configured: ${packet.federation.relayConfigured}`,
        `- Relay Mode: ${packet.federation.relayMode}`,
        packet.federation.relayLastError ? `- Relay Error: ${packet.federation.relayLastError}` : `- Relay Error: none`,
        ``,
        `## Memory Context`,
        `- Matches: ${packet.memoryContext.matches.length}`,
        ...packet.memoryContext.matches.map((match) => `- ${match}`),
        ``,
        `## Manual Overrides`,
        ...((packet.manualOverrides.length > 0 ? packet.manualOverrides : ['none']).map((override) => `- ${override}`)),
        ``,
        `## Catalog Shortlist`,
        `- Skills: ${packet.catalogShortlist.skills.join(', ') || 'none'}`,
        `- Workflows: ${packet.catalogShortlist.workflows.join(', ') || 'none'}`,
        `- Hooks: ${packet.catalogShortlist.hooks.join(', ') || 'none'}`,
        `- Automations: ${packet.catalogShortlist.automations.join(', ') || 'none'}`,
        `- Specialists: ${packet.catalogShortlist.specialists.join(', ') || 'none'}`,
        `- Crews: ${packet.catalogShortlist.crews.join(', ') || 'none'}`,
        ``,
        `## Knowledge Fabric`,
        `- Summary: ${packet.knowledgeFabric?.summary ?? 'No knowledge fabric bundle recorded.'}`,
        `- Dominant Source: ${packet.knowledgeFabric?.dominantSource ?? 'unknown'}`,
        `- Attached Collections: ${packet.knowledgeFabric?.attachedCollections.map((item) => item.name).join(', ') || 'none'}`,
        `- Pattern Hits: ${packet.knowledgeFabric?.patternHits.map((item) => item.name).join(', ') || 'none'}`,
        `- Model Tiers: ${packet.knowledgeFabric?.modelTierTrace.map((item) => `${item.stage}:${item.tier}`).join(', ') || 'none'}`,
        ``,
        `## Protocol`,
        packet.protocol.markdown || 'No protocol sections loaded.',
    ];
    return lines.join('\n').trim();
}
function dedupeStrings(values) {
    return [...new Set(values.filter(Boolean))];
}
