export declare function resolveFinalAssistantText(params: { finalText?: string | null; streamedText?: string | null; }): string; export declare function composeThinkingAndContent(params: { thinkingText?: string; contentText?: string; showThinking?: boolean; }): string; /** * Extract ONLY thinking blocks from message content. * Model-agnostic: returns empty string if no thinking blocks exist. */ export declare function extractThinkingFromMessage(message: unknown): string; /** * Extract ONLY text content blocks from message (excludes thinking). * Model-agnostic: works for any model with text content blocks. */ export declare function extractContentFromMessage(message: unknown): string; /** * Channel inbound DMs collapse into koi:main:main and are stored as user turns * carrying the channel envelope ("[WhatsApp +1832… +32s 2026-06-10 22:41 CDT]") * and/or the inbound prefix ("[SKYKOI] …"). The owner already gets a clean * "📨 WhatsApp · Name: text" heads-up for those, so the raw envelope turn should * NOT also clutter their chat. Their OWN typed messages never lead with such a * header, so detecting it cleanly tells them apart (display-only — the koi still * receives the full message). */ /** * Strip the runtime's silent-control tokens (NO_REPLY, HEARTBEAT_OK) from text * shown to the owner. These are control signals — NO_REPLY tells the runtime to * send NOTHING back to a contact, HEARTBEAT_OK acks an internal poll — and must * NEVER render in the owner's chat. The koi may emit such a token alongside a * natural heads-up meant for the owner; we drop only the token(s) and keep the * human text. A turn that is ONLY a token collapses to empty, so the caller (which * guards on `if (text)`) renders nothing for it. */ export declare function stripSilentControlTokens(text: string): string; export declare function isChannelInboundEcho(text: string): boolean; export declare function extractTextFromMessage(message: unknown, opts?: { includeThinking?: boolean; }): string; export declare function isCommandMessage(message: unknown): boolean; export declare function formatTokens(total?: number | null, context?: number | null): string; export declare function formatContextUsageLine(params: { total?: number | null; context?: number | null; remaining?: number | null; percent?: number | null; }): string; /** A few-word "what it's doing now" label for the status line (shown instead of "running"). */ export declare function friendlyToolLabel(name: string): string; export declare function asString(value: unknown, fallback?: string): string; /** * Parse a per-message timestamp as the server actually sends it: a NUMBER of epoch * milliseconds (live chat events) OR an ISO-8601 string (some history rows), tolerating * numeric strings. Returns `undefined` for anything unparseable so callers can fall back * to "now" rather than render an Invalid Date. This is the single source of truth so a * bubble shows the message's REAL time instead of render-time `Date.now()`. */ export declare function parseMessageTime(ts: number | string | undefined | null): Date | undefined; /** Local-clock "HH:MM" for a message bubble's timestamp (no color). */ export declare function formatClock(date: Date): string; /** * Resolve a koi id to its display name for the assistant divider label, matching the * global label logic in tui.ts (`Koi` for the default `main` koi, else the configured * name or the raw id). Returns `undefined` when no koiId is given so callers can fall * back to the current label. */ export declare function resolveKoiDisplayName(koiNames: Map, koiId?: string): string | undefined;