/**
 * TopicIntent — per-topic semantic state tracking via continuous confidence.
 *
 * Layer 1 of the Topic Intent Layer spec (v14 CLEAN, approved 2026-05-22).
 * See docs/specs/topic-intent-layer.md.
 *
 * Tracks candidate facts and decisions extracted from conversation. Each
 * EstablishedRef accumulates evidence over multi-turn exchange; confidence
 * is computed on read as a deterministic projection over the append-only
 * event log.
 *
 * Framework-agnostic: pure JSON persistence, pure math projection. Reachable
 * from Claude Code and Codex sessions alike.
 *
 * Storage: {stateDir}/topic-intent/<topicId>.json
 */
export type RefKind = 'fact' | 'decision' | 'method' | 'audience' | 'goal';
/**
 * Task-context kinds (rung 1) describe the *working frame* of the active task
 * — how it's being done, who it's for, what it's trying to achieve — as opposed
 * to facts/decisions the conversation asserts. They are the category that caused
 * the founding methodology-drift incident ("we're testing over Telegram").
 */
export declare const TASK_CONTEXT_KINDS: ReadonlySet<RefKind>;
export declare function isTaskContextKind(kind: RefKind): boolean;
export type RefStatus = 'live' | 'conflicted';
export type EvidenceKind = 'extract-user' | 'extract-agent' | 'user-reref' | 'agent-reref' | 'user-affirm' | 'pending-confirm-positive' | 'pending-confirm-negative' | 'contradiction' | 'conflict-mark' | 'sharpen-retry-issued';
export interface EvidenceEvent {
    eventId: string;
    refId: string;
    kind: EvidenceKind;
    sourceMessageId: string;
    userAuthored: boolean;
    at: string;
    delta: number;
    meta?: Record<string, unknown>;
}
export interface EstablishedRef {
    refId: string;
    arcId: string;
    topicId: number;
    kind: RefKind;
    text: string;
    confidence: number;
    evidence: EvidenceEvent[];
    lastReinforcedAt: string;
    status: RefStatus;
    createdAt: string;
    updatedAt: string;
}
export interface PendingConfirmation {
    pendingId: string;
    topicId: number;
    arcId: string;
    refId: string;
    propositionText: string;
    questionText: string;
    sentAtTurn: number;
    sentAtTime: string;
    ttl: {
        turns: number;
        hours: number;
    };
    retries: number;
    maxRetries: number;
    status: 'pending' | 'answered' | 'expired' | 'abandoned';
    queuedAtTime?: string;
    dequeuedAtTime?: string;
    answeredAtTime?: string;
    answerVerdict?: 'positive' | 'negative' | 'ambiguous' | 'non-responsive';
}
export interface TopicIntentFile {
    topicId: number;
    /** Per-topic monotonic user-turn counter (v1: single arc per topic). Additive; defaulted on read. */
    turn?: number;
    refs: Record<string, EstablishedRef>;
    pending: {
        outstanding: PendingConfirmation | null;
        queue: PendingConfirmation[];
    };
    telemetry: TelemetryCounters;
    schemaVersion: 1;
}
export interface TelemetryCounters {
    extraction_total: Record<string, number>;
    evidence_event_total: Record<string, number>;
    confidence_clamp_authority_total: number;
    pending_confirm_created_total: number;
    pending_confirm_queue_dropped_total: number;
    pending_confirm_abandoned_total: number;
    pending_confirm_expired_total: number;
    pending_confirm_answered_total: Record<string, number>;
    /**
     * Capture-loop funnel counters (spec §10 — observability across the WHOLE
     * loop: captured → surfaced → used → corrected). Additive; defaulted on read
     * for back-compat with pre-capture-loop files.
     */
    capture?: CaptureCounters;
}
/**
 * Whole-loop capture funnel (spec §10). Capture side meters what we extracted;
 * surface side meters whether what we captured actually reached the agent and
 * changed anything. Pairing this with the HumanAsDetectorLog miss-heat-map is
 * the effectiveness read.
 */
export interface CaptureCounters {
    turns_seen: number;
    prefilter_skipped: number;
    extractions_attempted: number;
    extractions_emitted: number;
    refs_created: number;
    degraded_no_intelligence: number;
    degraded_cap_or_error: number;
    degraded_shed: number;
    rate_limited: number;
    briefing_served: number;
    briefing_refs_settled: number;
    briefing_refs_tentative: number;
    arccheck_fired: number;
    arccheck_signalled: number;
    last_capture_at: string | null;
    refkind_created?: Record<string, number>;
}
/**
 * Per-kind decay profiles (rung 1 — the short/medium/long horizon hierarchy).
 * Facts/decisions keep the original long profile EXACTLY (grace 30 / half-life
 * 180), so rung-0 confidence math is provably unchanged. Task-context kinds
 * fade faster: a method ("testing over Telegram") matters intensely this task
 * and should demote in days, not survive 180. Numbers are tunable knobs (the
 * Observability funnel lets us tune them from real data); the per-kind
 * *mechanism* is the design. Spec: docs/specs/topic-intent-task-context-capture.md §3.
 */
export interface DecayProfile {
    graceDays: number;
    halfLifeDays: number;
}
/** A partial per-kind override: any subset of kinds, any subset of {graceDays, halfLifeDays}. */
export type DecayProfileOverrides = Partial<Record<RefKind, Partial<DecayProfile>>>;
/**
 * Apply config overrides on top of the defaults (existence-checked: only the
 * fields/kinds present are changed; everything else keeps the default). Invalid
 * values (non-finite, ≤ 0) are ignored so a bad config can never break decay.
 * Idempotent — always re-derives from DEFAULTS, so calling twice with different
 * overrides doesn't compound. Returns the resolved active profiles.
 */
export declare function configureDecayProfiles(overrides?: DecayProfileOverrides): Record<RefKind, DecayProfile>;
/** Restore the built-in defaults (primarily for test isolation). */
export declare function resetDecayProfiles(): void;
/** Resolve the decay profile for a kind; missing/unknown kind → long profile (rung-0 default). */
export declare function decayProfileFor(kind?: RefKind): DecayProfile;
export declare const SIGNAL_DELTA: Record<EvidenceKind, number>;
export declare const USER_AUTHORED_BY_DEFAULT: Record<EvidenceKind, boolean>;
export interface ProjectionResult {
    confidence: number;
    tier: 'observation' | 'tentative' | 'authoritative';
    authorityClampApplied: boolean;
    decayApplied: number;
    evidenceCount: number;
    userAuthoredEpisodes: number;
}
/**
 * Compute the confidence projection for a single EstablishedRef from its
 * evidence array. Pure function — no I/O, no state, fully deterministic.
 *
 * Order of operations:
 *   1. Per-message dedup by (refId, sourceMessageId): on collision, keep the
 *      single largest applicable delta. (Multiple signals from the same user
 *      message about the same refId count as ONE episode.)
 *   2. Apply signal-specific caps (user-reref cumulative <= +0.30, etc.)
 *   3. Apply affirmation caps (per-refId per 24h, per single user message).
 *   4. Sum applicable deltas (with caps).
 *   5. Apply time decay if (now - lastReinforcedAt) > 30 days.
 *   6. Authority hard clamp: if would-be >= 0.7 and no qualifying
 *      user-authored episode exists, clamp at 0.69.
 *   7. Clamp final to [0.0, 1.0].
 */
export declare function projectConfidence(evidence: EvidenceEvent[], lastReinforcedAt: string, nowMs?: number, refKind?: RefKind): ProjectionResult;
/**
 * Which evidence kinds count as user-authored EPISODES that qualify for
 * authority? (Distinct from the userAuthored boolean, which is broader.)
 *
 * Per spec: "user-authored episodes are the unit of evidence." Only
 * specific kinds qualify — extraction from user, user re-reference,
 * anchored affirmation, positive pending-confirm answer, contradiction
 * (which is also a user-authored episode in the negative direction).
 */
export declare function qualifiesAsUserAuthoredEpisode(kind: EvidenceKind): boolean;
export declare function buildEvent(refId: string, kind: EvidenceKind, sourceMessageId: string, opts?: {
    at?: string;
    userAuthored?: boolean;
    delta?: number;
    meta?: Record<string, unknown>;
}): EvidenceEvent;
export declare class TopicIntentStore {
    private dir;
    constructor(stateDir: string);
    private filePath;
    /** Load a topic's intent file, returning an empty skeleton if missing or corrupt. */
    load(topicId: number): TopicIntentFile;
    /**
     * Persist the topic's intent file ATOMICALLY (write to a unique temp file +
     * rename — rename is atomic on POSIX, so a concurrent reader never sees a
     * torn/half-written file). This is the corruption-safety half of the
     * concurrent-write guard; appendEvidence adds the lost-update guard (CAS).
     */
    save(file: TopicIntentFile): void;
    /**
     * Append an evidence event to a refId. Creates the ref if it doesn't exist.
     * Updates lastReinforcedAt if the event has positive delta.
     * Updates telemetry counters.
     */
    appendEvidence(topicId: number, refId: string, ev: EvidenceEvent, refInit?: Partial<EstablishedRef>): TopicIntentFile;
    /**
     * Acquire a per-topic lock (atomic mkdir), run fn, release. Bounded spin with
     * stale-lock steal so a crashed holder can't wedge the file forever. Capture
     * runs off the delivery path (queued), so brief contention spin is acceptable.
     */
    private withTopicLock;
    /** Single load→mutate→atomic-save pass (runs under withTopicLock). */
    private applyEvidence;
    /** The (single, v1) arc id for a topic. */
    arcIdFor(topicId: number): string;
    /** Atomically increment and return the per-topic user-turn counter. */
    bumpTurn(topicId: number): number;
    /**
     * Atomically increment one or more capture-funnel counters (spec §10).
     * Runs under the per-topic lock so concurrent captures / briefing fetches /
     * arccheck calls don't clobber each other's counter writes. Best-effort —
     * metering must never throw into the path that calls it, so all failures are
     * swallowed (the counter is a diagnostic, not correctness-critical).
     *
     * `at` (when provided) sets `last_capture_at`.
     */
    bumpCaptureCounters(topicId: number, deltas: Partial<Record<NumericCaptureKey, number>>, at?: string, refKindsCreated?: RefKind[]): void;
    /** Get the live projection for a refId (recomputed from evidence). */
    getProjection(topicId: number, refId: string, nowMs?: number): ProjectionResult | null;
    /** Get all refs for a topic at current tier or above. */
    getRefsAtOrAbove(topicId: number, minTier: 'observation' | 'tentative' | 'authoritative', nowMs?: number): Array<EstablishedRef & {
        projection: ProjectionResult;
    }>;
    /** Full read of a topic's file (for diagnostics endpoint). */
    read(topicId: number): TopicIntentFile;
}
export declare function defaultCaptureCounters(): CaptureCounters;
/** Numeric (additive) capture-counter keys — excludes the timestamp field. */
export type NumericCaptureKey = Exclude<keyof CaptureCounters, 'last_capture_at' | 'refkind_created'>;
export declare const TOPIC_INTENT_CONSTANTS: {
    DECAY_HALF_LIFE_DAYS: number;
    DECAY_GRACE_DAYS: number;
    DECAY_LAMBDA: number;
    AUTHORITY_THRESHOLD: number;
    AUTHORITY_CLAMP: number;
    TENTATIVE_THRESHOLD: number;
    SIGNAL_CAPS: Partial<Record<EvidenceKind, number>>;
    AFFIRM_PER_REF_PER_24H_LIMIT: number;
    AFFIRM_PER_MESSAGE_REF_LIMIT: number;
    MS_PER_DAY: number;
};
//# sourceMappingURL=TopicIntent.d.ts.map