/** * Funnel message registry and content pipeline — ADR-301 signed content * boundaries. * * Messages are inert data. Regardless of how a message reached this process * (in-package today; the signed helper channel later), the renderer treats * it as untrusted and enforces, before display: * - schema validation (invalid → dropped, never repaired) * - length bound (≤ 80 display columns → over-length dropped) * - URL host allowlist (exact hosts, in code — lookalikes/IPs dropped) * - expiry * - zero terminal control sequences (any control char, ANSI/OSC/DCS * escape, or bidi override → dropped, not stripped-and-shown) * * There is no eval path and no styling in the payload: color comes only * from the renderer's own fixed styles. */ import type { FunnelMessage } from './types.js'; export declare const MAX_MESSAGE_COLUMNS = 80; export declare function containsForbiddenSequences(text: string): boolean; /** Approximate terminal display width: wide CJK/emoji count 2. */ export declare function displayWidth(text: string): number; export declare function isAllowedUrl(url: string): boolean; /** * Full validation gate. Returns true only when every ADR-301 content * boundary passes. Failures are silent drops by design — a bad message * must never produce a visible error in the statusline. */ export declare function isValidMessage(msg: unknown, now?: Date): msg is FunnelMessage; /** * Local promo/message content: cold-start seed (ADR-311 amendment * revisited, issue #2787). * * The remote pool is authoritative — `eligibleMessagesFromPools` in * `rotation.ts` merges by id with remote winning — but on new installs * the remote fetch races the very first render and the promo row shows * NOTHING until the pool has been fetched at least once. The disclosure * gate can't unlock either, so several 20-second slots go by blank. * * The fix is a small local seed of educational tips, one bootstrap * disclosure, and a single sponsor promo, all validating cleanly through * `isValidMessage` and using only URLs on the exact-host allowlist. Each * carries a stable id so the remote pool can override or retire any of * them without a CLI release. */ export declare const MESSAGES: FunnelMessage[]; /** Messages that survive every content boundary right now. */ export declare function eligibleMessages(now?: Date): FunnelMessage[]; /** * Merge the remote (cached) message pool with the in-code fallback pool. * The remote pool is authoritative when populated; the in-code pool * covers cold starts and API-down periods. Deduplication is by `id` — * remote wins over in-code for a given id so admins can override without * a client release. */ export declare function eligibleMessagesFromPools(inCodePool: readonly FunnelMessage[], remotePool: readonly FunnelMessage[], now?: Date): FunnelMessage[]; //# sourceMappingURL=messages.d.ts.map