/**
 * openConversationBrief — builds the topic NAME + first-message SUMMARY for a
 * Threadline conversation being promoted into its own topic via "open this"
 * (CMT-567, continuation of the deterministic intercept in CMT-529).
 *
 * Three tiers, best-quality-first, each degrading WITHOUT ever failing the bind
 * (the caller `bindHubConversation` must always get a non-empty name + summary):
 *
 *   Tier A — LLM:      a real conversation (>=2 messages) → a Haiku call using the
 *                      proven PURPOSE-line convention (NOT JSON — see TopicSummarizer).
 *                      PURPOSE → name, body → summary. Runs on the `interactive`
 *                      lane (the operator is watching for the topic to appear).
 *   Tier B — template: deterministic brief from data already on hand (peer,
 *                      message count, last activity, last inbound line). Zero cost,
 *                      zero latency. Used when the LLM tier is skipped or fails.
 *   Tier C — slug:     no backing conversation at all (a hub-notice-only entry) →
 *                      slug name + the legacy tie-marker.
 *
 * The LLM is a GENERATOR, never a gate: any failure degrades the result, never
 * blocks the bind. Spec: docs/specs/THREADLINE-OPEN-THIS-LLM-NAMING-SPEC.md.
 */
import type { IntelligenceProvider } from '../core/types.js';
import type { LlmQueue } from '../monitoring/LlmQueue.js';
/** A scrubbed, never-empty name + summary for a newly-opened topic. */
export interface ConversationBrief {
    /** ≤ 40 chars, scrubbed, NEVER empty. */
    topicName: string;
    /** ≤ 600 chars, scrubbed, NEVER empty. */
    summary: string;
    /** Where each field actually came from (observability). */
    nameSource: 'llm' | 'template' | 'slug';
    summarySource: 'llm' | 'template' | 'slug';
    /** One-word reason for any non-LLM outcome — feeds the Fix-1d log line. */
    reason: 'ok' | 'no-conversation' | 'too-few-messages' | 'no-deps' | 'llm-timeout' | 'llm-abort' | 'llm-capped' | 'llm-error' | 'parse-empty' | 'credential-scrub';
    /** Wall-clock ms spent in this call (mostly the LLM wait). */
    latencyMs: number;
}
/** Minimal shape of a threadline message row (subset of ThreadlineMessageRow). */
interface BriefMessageRow {
    direction: 'in' | 'out';
    text: string;
    remoteAgentName: string;
    timestamp: string;
}
export interface BriefDeps {
    observability: {
        getThread(threadId: string): {
            messages: BriefMessageRow[];
        } | null;
    } | null;
    llmQueue: LlmQueue | null;
    intelligence: Pick<IntelligenceProvider, 'evaluate'> | null;
    /**
     * Existing topicNameFor() — always returns a non-empty slug. Optional: the
     * server-built `briefDeps` doesn't carry it (topicNameFor is private to
     * hubCommands); `bindHubConversation` injects the real one so the brief's slug
     * matches the legacy path. A built-in default guards direct callers.
     */
    topicNameFallback?: (conv: unknown, threadId: string) => string;
    now?: () => number;
}
/** Conversation fields the brief reads (subset of the ConversationStore record). */
export interface BriefConversation {
    subject?: string;
    participants?: {
        peers?: string[];
    };
    remoteAgent?: string;
    lastInboundHash?: string;
    messageCount?: number;
    lastActivityAt?: string;
}
/** Scrub + cap a candidate topic name. Returns null if it must degrade. */
declare function scrubName(raw: string): string | null;
/** Scrub + cap a candidate summary. Returns null if it must degrade. */
declare function scrubSummary(raw: string): string | null;
declare function relativeTime(iso: string | undefined, now: number): string;
/**
 * Tier-B deterministic template summary. Always non-empty; scrubs the inbound
 * snippet (a cold first message could carry a secret).
 */
declare function templateSummary(conv: BriefConversation | null, messages: BriefMessageRow[], now: number): string;
declare function buildLlmPrompt(messages: BriefMessageRow[]): string;
/**
 * Build the name + summary for a conversation being opened into its own topic.
 * NEVER throws — always returns a usable, non-empty ConversationBrief.
 */
export declare function generateConversationBrief(threadId: string, conv: BriefConversation | null, deps: BriefDeps, opts?: {
    timeoutMs?: number;
}): Promise<ConversationBrief>;
export declare const __testing: {
    scrubName: typeof scrubName;
    scrubSummary: typeof scrubSummary;
    templateSummary: typeof templateSummary;
    buildLlmPrompt: typeof buildLlmPrompt;
    relativeTime: typeof relativeTime;
    LEGACY_MARKER: string;
};
export {};
//# sourceMappingURL=openConversationBrief.d.ts.map