// engine/triagier/classifier.ts — SPEC-726: Haiku-backed intent classifier
//
// If an LLM provider is available, calls Haiku for classification.
// Falls back to a deterministic heuristic classifier on timeout, error, or
// when no provider is configured.
import { runWithGuard } from './cost-guard.js';
import { routeToNextStep } from './router.js';
/**
 * Rule-based classifier using simple regex heuristics.
 * Provides a fast, free, deterministic fallback when Haiku is unavailable.
 *
 * Rules (first match wins):
 *   "what if" / "someday" / "imagine"       → idea
 *   "fix typo" / "rename" / "typo"          → quick-fix
 *   "bug" / "error" / "crash" / "broken"    → bug-spec
 *   "add" / "implement" / "integrate" / ... → feature-spec
 *   "epic" / "platform" / "migrate"         → epic
 *   (default)                               → feature-spec
 */
export function heuristicClassify(description) {
    const lower = description.toLowerCase();
    if (/what if|someday|imagine|wouldn't it be|could we ever/.test(lower)) {
        return {
            kind: 'idea',
            confidence: 0.75,
            rationale: 'Description uses speculative language ("what if", "someday") indicating an exploratory idea rather than a concrete feature request.',
        };
    }
    if (/fix\s+typo|\brename\b|typo|s\/\w+\/\w+|spelling|whitespace|minor\s+fix|small\s+fix/.test(lower)) {
        return {
            kind: 'quick-fix',
            confidence: 0.8,
            rationale: 'Description describes a small, self-contained change (typo, rename, whitespace) that can be implemented directly without requirements elicitation.',
        };
    }
    if (/\bbugs?\b|\berrors?\b|\bcrash(es|ed|ing)?\b|\bbroken\b|\bfails?\b|\bdoes not work\b|\bisn't working\b/.test(lower)) {
        return {
            kind: 'bug-spec',
            confidence: 0.75,
            rationale: 'Description contains bug-related keywords (bug, error, crash, broken) indicating a defect that needs a formal bug spec.',
        };
    }
    if (/\bepic\b|\bplatform\b|\bmigrat(e|ion)\b|\boverhaul\b|\brewrite\b|\brefactor\s+entire\b/.test(lower)) {
        return {
            kind: 'epic',
            confidence: 0.7,
            rationale: 'Description involves large-scale structural changes or platform-level work indicating an epic.',
        };
    }
    if (/\badd\b|\bimplement\b|\bintegrat(e|ion)\b|\bbuild\b|\bcreate\b|\bintroduce\b|\bsupport\b/.test(lower)) {
        return {
            kind: 'feature-spec',
            confidence: 0.7,
            rationale: 'Description uses feature-oriented verbs (add, implement, integrate, build) indicating a standard feature spec.',
        };
    }
    // Default
    return {
        kind: 'feature-spec',
        confidence: 0.5,
        rationale: 'Could not confidently classify from description alone; defaulting to feature-spec for further elicitation.',
    };
}
// ---------------------------------------------------------------------------
// GUARD_FALLBACK constant
// ---------------------------------------------------------------------------
const GUARD_FALLBACK_RESULT = {
    kind: 'feature-spec',
    confidence: 0.5,
    rationale: 'GUARD_FALLBACK',
    nextStep: { tool: 'elicit_requirements', mode: 'interactive' },
};
// ---------------------------------------------------------------------------
// LLM-backed classifier (stub — uses heuristic until Haiku adapter is wired)
// ---------------------------------------------------------------------------
/**
 * Try to classify using an LLM provider (Haiku-class).
 * Currently implemented as a deterministic heuristic because no Haiku API
 * adapter is wired in src/engine/llm-providers/. When a Haiku transport is
 * available, replace this stub with a real call. The interface is unchanged.
 *
 * Architectural note: the spec says "If LLM provider unavailable, use
 * deterministic heuristic as fallback." We document the stub explicitly and
 * wrap it in runWithGuard so the timeout/fallback path is always exercised
 * correctly when a real LLM call is wired in.
 */
// eslint-disable-next-line @typescript-eslint/require-await -- async signature required by cost-guard interface
async function tryLlmClassify(input) {
    // TODO: Wire real Haiku call here when llm-providers has a text-generation adapter.
    // For now: deterministic heuristic (same result, no API cost).
    return heuristicClassify(input.description);
}
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * Classify the intent of a user request into a TriageKind.
 * Wraps the LLM call (or heuristic) in a cost guard (5s wall-clock).
 * On guard trip, returns a safe fallback (feature-spec, confidence 0.5, rationale GUARD_FALLBACK).
 */
export async function classifyIntent(input) {
    // Respect any hint provided by the caller
    if (input.hint?.kind) {
        const hintKind = input.hint.kind;
        return {
            kind: hintKind,
            confidence: 1.0,
            rationale: `Hint provided by caller: ${hintKind}`,
            nextStep: routeToNextStep(hintKind),
        };
    }
    const guardResult = await runWithGuard(() => tryLlmClassify(input), {
        wallClockMs: 5_000,
        maxOutputTokens: 1_500,
    });
    if ('fallback' in guardResult) {
        return GUARD_FALLBACK_RESULT;
    }
    const { kind, confidence, rationale } = guardResult;
    return {
        kind,
        confidence,
        rationale,
        nextStep: routeToNextStep(kind),
    };
}
//# sourceMappingURL=classifier.js.map