/**
 * Cross-session failure clustering + time-wasted attribution for the traces
 * insight engine (PHNX-3141) — the piece that turns per-tool error counts
 * into "here is your #1 systemic problem and what it cost."
 *
 * Pure and SQL-shaped: it consumes the same `SyncRow[]` + `tool_calls` rows
 * `buildIndexShard` already loads (no re-parsing of transcripts), so cost
 * stays proportional to this sync's row count, never the full corpus.
 *
 * `tool_calls` rows carry `ordinal`/`timestamp` per call within a session
 * (`db.ts`'s `idx_tool_calls_session ON tool_calls(session_id, ordinal)`), which
 * is enough to reconstruct per-session call order and inter-call gaps without a
 * full `SessionTrajectory` — that is what makes this incremental at scale.
 *
 * Failure phenotype (false-termination / out-of-order / premature-completion /
 * failure-to-act, `phenotype.ts`) is folded in as a fourth grouping dimension
 * (PHNX-3327). Classifying it needs the full derived trajectory, so it is NOT
 * computed here — the caller passes a per-session `phenotypes` map that
 * `buildIndexShard` fills from the persisted, mtime+size-keyed
 * `session_phenotypes` cache for the WHOLE corpus. Keying the group on the
 * cached-per-session phenotype (never on this run's incremental batch) is what
 * keeps two identically-signatured sessions in one cluster regardless of when
 * each was synced. The `signature` OUTPUT is unchanged (`{ tool, cause, key }`);
 * phenotype is an added dimension carried alongside it, so callers that never
 * pass a map (the unit tests, a pre-phenotype caller) see the exact prior
 * grouping.
 */
import { type TraceFailureCause } from './classify.js';
import type { FailurePhenotype } from './phenotype.js';
import { type LatencyInsight } from './segments.js';
import { type SyncRow, type ToolCallRow, type TracesIndexShard } from './sync.js';
import type { InsightFacets } from '../session/insights.js';
export interface FailureSignature {
    tool: string;
    cause: TraceFailureCause;
    /** Normalized error text — volatile tokens (ids, counts, countdowns) stripped so instances fold together. */
    key: string;
}
export interface FailurePattern {
    /** Stable hash of the signature (incl. phenotype) — deep-linkable, unaffected by row order. */
    id: string;
    label: string;
    signature: FailureSignature;
    /**
     * Dominant failure phenotype of the sessions in this cluster, or `null` when
     * none was classifiable. A fourth grouping dimension (PHNX-3327): two failures
     * with the same `(tool, cause, key)` but different phenotypes are distinct
     * patterns.
     */
    phenotype: FailurePhenotype | null;
    /** Distinct sessions this pattern occurred in. */
    sessions: number;
    /** Total failing calls matching this signature. */
    occurrences: number;
    /** Estimated ms of retry/stall time attributable to this pattern (see attribution rule below). */
    wastedMs: number;
    /** Bounded example session ids for drill-down. */
    exampleSessionIds: string[];
    /** Movement vs the same pattern id in the previous shard. */
    drift: 'up' | 'flat' | 'down';
}
export interface ComputedInsights {
    /** Top-K patterns ranked by wastedMs (impact) — a rare 1-occurrence/8h loop still surfaces. */
    failurePatterns: FailurePattern[];
    /** Sum of wastedMs across every cluster found this sync, not just the top-K rows above. */
    wastedMsTotal: number;
    latency: LatencyInsight;
}
/** Strip volatile tokens from a failure's evidence text so repeat instances hash identically. */
export declare function normalizeErrorKey(desc: string, raw: string | null): string;
/**
 * Cluster failed tool calls into ranked patterns and estimate the wasted time
 * behind each, plus device-wide time-to-first-tool latency.
 *
 * wastedMs attribution has two parts that sum:
 *
 * (1) The failed call's OWN blocking duration — `end_timestamp - timestamp`
 * (PHNX-3437). A call that hung for minutes and then failed wasted that whole
 * time even if it was the last call in its session or was followed quickly by an
 * unrelated call — the case the gap heuristic alone booked as ~0. This is what
 * makes a fail-fast fix measurable: a channel that stops hanging 5.5min on stdin
 * and instead fails in <1s (PHNX-3407) drops from ~5.5min of attributed waste to
 * ~0. Bounded by MAX_GAP_ATTRIBUTION_MS so a corrupt end timestamp can't dominate.
 *
 * (2) The idle gap AFTER the call, before the NEXT call in the same session,
 * counted when either (a) the next call repeats the same signature (a retry
 * loop) or (b) the gap is a stall (≥60s) before an unrelated next call — each
 * bounded by MAX_GAP_ATTRIBUTION_MS so one failure can't absorb hours of
 * human-away idle in an async channel session, whether as a lone stall or a
 * same-signature re-ask hours later; a genuine active retry loop is many short
 * gaps that each clear the cap and still sum large. When the end timestamp is
 * known, this gap is measured from the call's END, so the blocking time counted
 * in (1) is never double-counted; for a NULL end (rows an older extractor stored,
 * or a call still pending at scan end) it falls back to the original
 * gap-from-START heuristic unchanged — no crash, no NaN, no regression.
 *
 * An idle gap unrelated to a nearby failure is never counted. This is an
 * estimate, not ground truth; it is not inflated by folding in ordinary
 * processing time between unrelated calls.
 */
export declare function computeInsights(rows: readonly SyncRow[], calls: readonly ToolCallRow[], prevShard?: TracesIndexShard | null, phenotypes?: ReadonlyMap<string, FailurePhenotype | null>, behavioralPatterns?: readonly FailurePattern[]): ComputedInsights;
/**
 * Promote the per-session silent-stall friction signals — already computed by
 * `computeInsightFacets` in `session/insights.ts` and keyed `silent stall: <bucket>`
 * — into cross-session behavioral `FailurePattern`s. These are silent failures
 * with NO error code: the agent went idle after its last event and a human had to
 * nudge it. They never surface through `computeInsights` (which only clusters
 * `outcome === 'error'` tool calls) and were previously only a `needsAttention`
 * friction counter, never a ranked issue. Cause is `behavioral`; the signature
 * `tool` is the synthetic `silent-stall` and `key` is the duration bucket.
 */
export declare function computeBehavioralPatterns(facetsBySession: ReadonlyMap<string, Pick<InsightFacets, 'frictionSignals'>>, prevShard?: TracesIndexShard | null): FailurePattern[];
