/**
 * ThreadlineObservability — read-only views over the canonical threadline
 * inbox + outbox + bridge bindings, powering the dashboard "Threadline" tab.
 *
 * Sources of truth (single source per class of data — no duplication):
 *   - .instar/threadline/inbox.jsonl.active   — every inbound message (PR #113).
 *   - .instar/threadline/outbox.jsonl.active  — every outbound message (this PR).
 *   - .instar/threadline/telegram-bridge-bindings.json — thread → topic links (PR #117).
 *   - .instar/threadline/known-agents.json     — fingerprint → display name.
 *
 * This class is **observational only** — it reads files, computes summaries,
 * and answers queries. It never writes, mutates, blocks, or gates.
 *
 * Performance: reads are lazy and stream-friendly. The inbox and outbox
 * files are append-only JSONL, so we do a full-file scan with line parsing
 * on every query. For agents with millions of messages this becomes
 * slow — at that point the right answer is to add an FTS5 index, but
 * today every agent in production has <10K threadline messages and
 * sub-100ms scans are fine.
 */
export interface ThreadlineMessageRow {
    /** "in" or "out" — direction relative to this agent. */
    direction: 'in' | 'out';
    id: string;
    timestamp: string;
    threadId: string;
    /** Counterparty agent — for inbound this is the sender; for outbound the recipient. */
    remoteAgent: string;
    /** Display name of the counterparty (resolved from known-agents.json when possible). */
    remoteAgentName: string;
    text: string;
    /** Trust level of the inbound sender, or 'self' for outbound. */
    trustLevel: string;
    outcome?: string;
}
export interface ThreadSummary {
    threadId: string;
    remoteAgent: string;
    remoteAgentName: string;
    messageCount: number;
    inboundCount: number;
    outboundCount: number;
    firstSeen: string;
    lastSeen: string;
    /** Average ms latency between an inbound message and the next outbound on the same thread. Null when no pair. */
    avgResponseLatencyMs: number | null;
    bridge: {
        topicId: number;
        topicName: string;
        createdAt: string;
        lastMessageAt: string;
    } | null;
    /** Whether a spawn-session record exists for this thread (heuristic). */
    hasSpawnedSession: boolean;
}
export interface ThreadDetail extends ThreadSummary {
    messages: ThreadlineMessageRow[];
}
export interface SearchHit {
    message: ThreadlineMessageRow;
    /** Snippet around the match, with the matched substring marked between «» (Telegraph-style). */
    snippet: string;
}
export interface ThreadlineObservabilityOptions {
    stateDir: string;
}
export declare class ThreadlineObservability {
    private readonly stateDir;
    private nameCache;
    private nameCacheReadAt;
    constructor(opts: ThreadlineObservabilityOptions);
    listThreads(filters?: {
        remoteAgent?: string;
        sinceIso?: string;
        untilIso?: string;
        /** "yes" → only threads with a Telegram topic; "no" → only without; undefined → both. */
        hasTopic?: 'yes' | 'no';
    }): ThreadSummary[];
    getThread(threadId: string): ThreadDetail | null;
    searchMessages(query: string, limit?: number): SearchHit[];
    private inboxPath;
    private outboxPath;
    private bindingsPath;
    private knownAgentsPath;
    private conversationsPath;
    private readJsonl;
    private loadBindings;
    /**
     * Phase 2a (CMT-497): the resume state now lives in conversations.json (the
     * single store). Return threads that have a spawned session (a sessionUuid) so
     * the dashboard's `hasSpawnedSession` check is preserved.
     */
    private loadThreadResume;
    private resolveAgentName;
    private loadKnownAgentsCache;
    private mapInbound;
    private mapOutbound;
    private computeAvgResponseLatencyMs;
}
//# sourceMappingURL=ThreadlineObservability.d.ts.map