export interface IngestReject {
    /** 1-based index of the offending line within the batch. */
    line: number;
    reason: string;
}
export interface IngestResult {
    written: number;
    rejected: IngestReject[];
    /** Per-store counts, so a caller/test can assert routing without reading files. */
    routed: {
        operational: number;
        activity: number;
    };
}
export interface IngestOptions {
    /**
     * Producer name, stamped as `module` on operational records so
     * `agents events --module <source>` filters to this producer. Required: an
     * unattributed external event is not auditable.
     */
    source: string;
    /** Validate and report without writing either store. */
    dryRun?: boolean;
    /** Override the activity root (tests). */
    activityRoot?: string;
}
/**
 * Route one already-validated line.
 *
 * The rule is forced by the stores, not chosen:
 *   - The activity store is keyed by session id ON DISK (one file per session),
 *     so an event with no session has nowhere to live there.
 *   - Activity records are read back with `module: 'activity'` hardcoded, so
 *     anything that must stay filterable by its producer has to be operational.
 * Therefore: milestone + usable sessionId -> activity; everything else -> ops.
 * `readUnifiedEvents` merges the two at read time, so `agents events` sees both.
 */
export declare function routeFor(event: string, sessionId: unknown): 'activity' | 'operational';
/**
 * Ingest a JSONL batch.
 *
 * Rejection is PER LINE and lossless: a single bad line never discards the rest
 * of the batch. One typo in a 100-event flush must not silently drop 99 real
 * events, and the caller still learns exactly which line failed and why.
 */
export declare function ingestBatch(input: string, opts: IngestOptions): IngestResult;
