/**
 * Behavioural facets of a coding session, and the cross-session rollup built from them.
 *
 * This is the engine behind `agents insights`. It answers "how do you work" rather than
 * "what did you spend" (`agents insights cost`) or "what shipped" (`agents insights output`), and it is
 * the only surface that splits any of it by the account that produced the work.
 *
 * Everything here is a pure function of a parsed `SessionEvent[]`, so it is testable
 * without a database and cheap to re-run. The expensive part is parsing the transcript,
 * which is why results are cached per session in `session_insights` and recomputed only
 * when the file's (mtime, size) changes — the same staleness contract as `scan_ledger`.
 *
 * Prior art: Claude Code's own `/insights`, which computes a comparable set from
 * `~/.claude/projects` alone. Two deliberate differences:
 *
 *   - It sees ONE account's directory. This reads every indexed session, across every
 *     Claude account and every other harness, and reports them apart.
 *   - It collapses conversation branches before counting. agents-cli is file-per-session
 *     throughout (`discover.ts` keys on the transcript's basename), so session counts
 *     here will read slightly higher than `/insights` on the same machine. Reported as
 *     the raw file count rather than quietly differing.
 */
import type { SessionEvent } from './types.js';
/**
 * Minimum quiet time after the assistant's last event before a user message
 * is classified as an **agent silent stall**: the model went quiet on its own
 * and sat idle until the human resumed. Shorter gaps are normal turn-taking.
 * 5 minutes is long enough to exclude "user typing the next instruction" and
 * short enough to catch "went silent mid-task until I said continue."
 */
export declare const SILENT_STALL_SECONDS = 300;
/** Behavioural facets of one session. Serialized as JSON into `session_insights`. */
export interface InsightFacets {
    toolCounts: Record<string, number>;
    /** Per-model assistant turn counts. `/insights` has no model dimension at all. */
    models: Record<string, number>;
    /**
     * Silent stalls attributed to the model that last spoke before the idle gap
     * (from the nearest preceding `usage`/message model tag). Some models go
     * quiet more often — this is the laziness split. Key = shortened model id.
     */
    silentStallsByModel: Record<string, number>;
    languages: Record<string, number>;
    /** Slash commands the user invoked, by name. */
    slashCommands: Record<string, number>;
    errorCategories: Record<string, number>;
    /** Times the user cut a turn short — the `interrupt` event from parse.ts. */
    interruptions: number;
    /** Seconds between an assistant's last event and the user's next message. */
    responseGaps: number[];
    /** Gaps excluded for exceeding the ceiling — reported, never silently dropped. */
    gapsOverCeiling: number;
    /**
     * Lines in the BEFORE and AFTER text of every edit and write — "lines touched", not
     * a diff. An Edit whose old_string is three unchanged context lines counts them in
     * both; git counts them zero times. Measured against a real commit the added figure
     * ran 19% high and the removed figure 475% high, so this must never be rendered as
     * a diffstat. Computing a true delta needs a line-level diff per edit, which is a
     * different feature.
     */
    linesTouchedBefore: number;
    linesTouchedAfter: number;
    /**
     * Edit/write calls in a vocabulary we recognise. NOT a proxy for measurability:
     * codex renames `apply_patch` to `Edit` but carries no line-bearing arguments, so it
     * reports edit calls with zero lines. Callers decide "measurable" from the line
     * totals themselves.
     */
    editingToolCalls: number;
    filesCreated: number;
    filesModified: number;
    filesDeleted: number;
    gitCommits: number;
    gitPushes: number;
    /**
     * Tool calls that carried an actual command string to search. Not every harness
     * populates one: the codex parser sets `command` for `exec_command` but not for
     * plain `exec`, its dominant tool, so git activity is structurally invisible there.
     * 0 means the commit counts are unmeasurable, not observed-zero.
     */
    shellCommandsSeen: number;
    /** 24 slots, local time, indexed by hour of the user's messages. */
    messageHours: number[];
    userTurns: number;
    assistantTurns: number;
    toolCount: number;
    errorCount: number;
    /**
     * Shell invocations bucketed by the actual executable + first subcommand token, not
     * lumped under the harness tool name. `bucketKey` collapses each command to
     * `git commit`, `gh pr`, `agents ssh`, `find`, `ssh→git pull`, … so the tool mix says
     * which binary ran, not just that `Bash` did. Cross-harness: keyed on `e.command`,
     * which the codex parser sets for `exec_command` too, so it is not Claude-`Bash`-only.
     */
    bashCommands: Record<string, number>;
    /**
     * Same bucketing for the shell command that was running when a tool call failed —
     * turns one `failed tool loop: Bash` line into per-binary failure counts
     * (`git reconcile`, `find`, `gh`) so the friction is attributable to a command.
     */
    bashCommandFailures: Record<string, number>;
    /** Deterministic evidence buckets used by the actions-forward report. */
    frictionSignals: Record<string, number>;
    correctionSignals: Record<string, number>;
    automationSignals: Record<string, number>;
}
/**
 * Compute every behavioural facet of one session from its parsed events.
 *
 * Pure: no I/O, no clock, no filesystem. `timezoneOffsetMinutes` is injected rather
 * than read from the environment so the hour histogram is deterministic in tests and
 * can be re-bucketed for a different display timezone without re-parsing.
 */
export declare function computeInsightFacets(events: SessionEvent[], timezoneOffsetMinutes?: number): InsightFacets;
/**
 * Classify a long quiet gap after the assistant's last event as an agent silent stall.
 * Optionally also marks an explicit resume nudge ("continue", …) after that silence.
 */
export declare function classifySilentStall(gapSeconds: number, userText: string, friction: Record<string, number>, corrections: Record<string, number>): void;
/** Percentile of a numeric sample, nearest-rank. Returns 0 for an empty sample. */
export declare function percentile(values: number[], p: number): number;
/** Bucket response gaps for display. Returns every bucket, including empty ones. */
export declare function bucketGaps(gaps: number[]): Array<{
    bucket: string;
    count: number;
}>;
/** A session's time span, for overlap detection. */
export interface SessionSpan {
    id: string;
    accountKey: string;
    startMs: number;
    endMs: number;
}
/** How much work ran concurrently, and how much of it straddled two accounts. */
export interface OverlapReport {
    /** Pairs of sessions whose spans intersect. */
    overlappingPairs: number;
    /** Of those, pairs belonging to DIFFERENT accounts. */
    crossAccountPairs: number;
    /** Distinct sessions involved in any overlap. */
    sessionsInvolved: number;
}
/**
 * Detect concurrent sessions by interval intersection.
 *
 * This is the metric that makes the account split legible rather than academic: a
 * cross-account overlap is `balanced` rotation actively running two orgs' quota at the
 * same moment. `/insights` has a comparable "multi-clauding" count, but with one
 * account it can only ever report the same-account case.
 *
 * Sweep in start order, keeping only spans that could still intersect, so this is
 * O(n log n + pairs) rather than O(n^2) over ~3k sessions.
 */
export declare function detectOverlap(spans: SessionSpan[]): OverlapReport;
/** Merge a session's facets into a running total. */
export declare function mergeFacets(into: InsightFacets, add: InsightFacets): void;
/** A fresh zeroed accumulator, for callers folding many sessions together. */
export declare function newFacetAccumulator(): InsightFacets;
/** Top-N entries of a count map, highest first, ties broken by name for determinism. */
export declare function topEntries(counts: Record<string, number>, limit: number): Array<{
    name: string;
    count: number;
}>;
export type InsightActionCategory = 'rule' | 'skill' | 'automation' | 'product';
export interface InsightAction {
    priority: 'high' | 'medium' | 'low';
    category: InsightActionCategory;
    action: string;
    evidenceCount: number;
    sampleSessionIds: string[];
}
export interface SessionFacetEvidence {
    id: string;
    facets: InsightFacets;
}
/** Build a stable, evidence-backed action list without exposing transcript text. */
export declare function buildInsightActions(sessions: SessionFacetEvidence[]): InsightAction[];
