// ---------------------------------------------------------------------------
// In-memory session focus store (keyed by sessionId or projectId)
// ---------------------------------------------------------------------------
const focusStore = new Map();
// Tools that are always on-topic — never trigger a drift warning.
const ALWAYS_ALLOWED_TOOLS = new Set([
    'list_specs',
    'summarize_spec',
    'consult_docs',
    'discover_mcps',
    'suggest_mcps',
    'check_versions',
    'set_locale',
]);
// ---------------------------------------------------------------------------
// Semantic similarity helpers (keyword-based — no ML dependency)
// ---------------------------------------------------------------------------
function tokenize(text) {
    return new Set(text
        .toLowerCase()
        .replace(/[^a-z0-9\s]/g, ' ')
        .split(/\s+/)
        .filter((w) => w.length > 2));
}
function jaccardSimilarity(a, b) {
    if (a.size === 0 && b.size === 0) {
        return 1;
    }
    let intersection = 0;
    for (const token of a) {
        if (b.has(token)) {
            intersection++;
        }
    }
    const union = a.size + b.size - intersection;
    return union === 0 ? 0 : intersection / union;
}
/** Similarity between two text strings (0–1). */
function textSimilarity(textA, textB) {
    return jaccardSimilarity(tokenize(textA), tokenize(textB));
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Sets the current focus for a session.
 * Call this when the user declares what they are working on.
 */
export function setFocus(sessionId, focus) {
    focusStore.set(sessionId, { ...focus });
}
/**
 * Returns the current focus for a session, or undefined if none is set.
 */
export function getFocus(sessionId) {
    return focusStore.get(sessionId);
}
/**
 * Clears the focus for a session (e.g., when user explicitly changes objective).
 */
export function clearFocus(sessionId) {
    focusStore.delete(sessionId);
}
/**
 * Checks whether a new request is aligned with the current session focus.
 * Returns a FocusShift descriptor if drift is detected, or null if on-topic.
 *
 * @param sessionId - The session / project identifier.
 * @param requestText - The incoming request description.
 * @param requestedTool - The tool being called (used for whitelist check).
 */
export function checkFocusDrift(sessionId, requestText, requestedTool) {
    // Always-allowed tools never trigger drift.
    if (requestedTool && ALWAYS_ALLOWED_TOOLS.has(requestedTool)) {
        return null;
    }
    const focus = focusStore.get(sessionId);
    if (!focus) {
        return null; // No focus set — nothing to drift from.
    }
    const similarity = textSimilarity(focus.objective, requestText);
    const DRIFT_THRESHOLD = 0.12; // < 12% token overlap = off-topic
    if (similarity >= DRIFT_THRESHOLD) {
        return null;
    }
    const toolLabel = requestedTool ? ` (tool: ${requestedTool})` : '';
    return {
        offTopicRequest: requestText + toolLabel,
        currentObjective: focus.objective,
        redirectMessage: buildRedirectMessage(focus, requestText),
        allowProceed: true, // Focus guardian warns but never blocks.
    };
}
function buildRedirectMessage(focus, newRequest) {
    const specNote = focus.specId ? ` (working on ${focus.specId})` : '';
    return (`Focus notice: You were working on "${focus.objective}"${specNote}. ` +
        `The new request ("${newRequest.slice(0, 80)}") seems unrelated. ` +
        `You can continue if intended — or say "change focus to <new objective>" to update it.`);
}
/**
 * Parses a "change focus" directive from a request string.
 * Returns the new objective text, or null if no directive found.
 */
export function parseChangeFocusDirective(request) {
    const patterns = [
        /change focus(?:\s+to)?\s*[:\-–]?\s*(.+)/i,
        /now (?:i'?m?\s*)?(?:working on|focusing on)\s+(.+)/i,
        /switch(?:ing)? (?:focus )?to\s+(.+)/i,
    ];
    for (const re of patterns) {
        const match = re.exec(request);
        if (match?.[1]) {
            return match[1].trim();
        }
    }
    return null;
}
/**
 * Returns the number of sessions currently tracked (for testing / debug).
 */
export function getActiveFocusCount() {
    return focusStore.size;
}
//# sourceMappingURL=focus-tracker.js.map