/** * Writing-style-matched draft reply composer. * * This module is PURE and DETERMINISTIC, no Date.now(), Math.random(), * or I/O. It takes a corpus of prior sent messages and an inbound message * and produces a draft body that mirrors the user's writing style. * * BEFORE-SEND REVIEW BOUNDARY * ────────────────────────────────────────────────────────────────────────── * This composer produces a DRAFT only. No send path is included here. * Delivery MUST go through the existing confirmed SMTP / connector send * route (EmailService.sendMail with confirm:true, or the equivalent MCP * connector action with explicit user confirmation). The lane descriptor * in style-reply-lane.ts enforces this boundary at the Personal Ops level. * * The secret-like-text check is injected rather than imported: which strings * count as credentials is a product policy, and the composer must run against * whichever predicate the surface already uses on the rest of its text. */ import type { SecretLikeTextPredicate } from '../google/account-registry.js'; import type { EmailSummary } from './email-service.js'; export interface StyleProfile { /** Most common greeting prefix found in sent messages, e.g. 'Hi', 'Hello', 'Hey'. */ readonly greeting: string; /** Most common sign-off found in sent messages, e.g. 'Thanks', 'Best', 'Cheers'. */ readonly signOff: string; /** Median sentence count across sent messages (whole number, ≥1). */ readonly medianSentenceCount: number; /** Dominant tone inferred from token analysis: 'formal' | 'casual' | 'neutral'. */ readonly tone: 'formal' | 'casual' | 'neutral'; /** True when the corpus is empty, all values are defaults. */ readonly isDefault: boolean; } /** * Count how many sentences a text body contains. * Splits on '. ', '! ', '? ' and trailing punctuation. * Returns at least 1 for any non-empty string. */ export declare function countSentences(text: string): number; /** * Compute the median of a sorted number array. * Returns `fallback` when the array is empty. */ export declare function median(sorted: readonly number[], fallback: number): number; /** * Find the most frequently occurring token in a list. * When there are ties, the token appearing earliest in `candidates` wins. * Returns `fallback` when no candidates match. */ export declare function mostFrequent(corpus: readonly string[], candidates: readonly string[], fallback: string): string; /** * Classify tone from a corpus of message bodies. * Returns 'formal', 'casual', or 'neutral'. */ export declare function classifyTone(bodies: readonly string[]): 'formal' | 'casual' | 'neutral'; /** * Extract a StyleProfile from a corpus of the user's prior sent messages. * * When `sentMessages` is empty, returns a neutral default profile. * Never reads the clock; fully deterministic. */ export declare function extractStyleProfile(sentMessages: readonly EmailSummary[]): StyleProfile; export interface DraftReplyResult { /** Fully composed draft body. Never includes credentials or secret-looking text. */ readonly body: string; /** The style profile used to compose this draft. */ readonly profile: StyleProfile; /** Subject line for the reply, prefixed with 'Re: ' if not already. */ readonly subject: string; /** BEFORE-SEND REVIEW BOUNDARY: always true, this is a draft, never auto-sent. */ readonly requiresBeforeSendReview: true; /** Human-readable reminder of the review boundary. */ readonly reviewBoundary: string; } /** * Compose a draft reply to `inbound` in the user's style as described by * `profile`. An optional `context` string (e.g. key points the user wants * to include) is woven into the body. * * SAFETY GUARANTEES * - Throws if the composed body or context contains secret-like text. * - Never sends, returns a DraftReplyResult with requiresBeforeSendReview: true. * - No Date.now() / Math.random(), deterministic output for a given input. * * @param inbound The email the user received and wants to reply to. * @param profile Writing-style profile extracted from prior sent messages. * @param context Key points / instructions to fold into the draft body; '' for none. * @param containsSecretLikeText The surface's own credential-shaped-text * predicate. Required rather than defaulted: a default would have to be * permissive, and a permissive default here silently disables the check. */ export declare function composeDraftReply(inbound: EmailSummary, profile: StyleProfile, context: string, containsSecretLikeText: SecretLikeTextPredicate): DraftReplyResult; /** * Extract a first name from an RFC-5322-style From header value. * Handles both "Display Name " and "addr" forms. * Returns empty string when no usable name is found. */ export declare function extractSenderName(from: string): string; /** Ensure subject is prefixed with 'Re: ' exactly once. */ export declare function replySubject(subject: string): string; //# sourceMappingURL=style-reply.d.ts.map