import type { TelegramCredentials } from './telegramApproval'; /** * Runs shorter than this are not worth a notification. * * The problem this solves is walking away — starting something substantial, * making coffee, and not knowing it finished. A `git status` that returned in * two seconds was never going to outlast your attention, and a phone that * buzzes for every one of those gets muted within a day, taking the * notifications that mattered with it. */ export declare const NOTIFY_AFTER_MS = 60000; export declare function shouldNotify(elapsedMs: number, enabled: boolean): boolean; /** `92s` / `4m 12s` / `1h 03m` — short enough to read on a lock screen. */ export declare function formatDuration(ms: number): string; /** * Telegram refuses a message over 4096 characters outright. * * Kept well under, because the summary head shares the first message. */ export declare const MAX_ANSWER_LENGTH = 3000; /** * How many messages one answer may become. * * A long answer is worth several; an enormous one is not worth a phone buzzing * eleven times, and past a point nobody is reading it there anyway. Three is * enough for an explanation and short of a flood. */ export declare const MAX_ANSWER_PARTS = 3; /** * An answer as the messages to send, in order. * * Cutting at the limit was honest — it said it had cut — but lossy in the place * it hurts: an answer that lists files or walks through a change passes 3000 * characters easily, and the conclusion is at the end. So it is split instead, * on a paragraph or line boundary where there is one nearby, and only what * exceeds three messages is cut. * * Mirrors the Mac app's `TelegramAnswerText.partsForPhone` deliberately: two * implementations of "what does the phone get" that can disagree is worse than * either. */ export declare function splitAnswer(text: string): string[]; /** * Markdown out, plain words in. * * The agent writes for a terminal that renders markdown, so its answer arrives * on a phone as `**140 datoteka**` — asterisks and backticks read as noise * exactly where the answer should be easiest to read. * * Stripped rather than handed to Telegram as `parse_mode`, which would be the * obvious fix and the wrong one: Telegram rejects a whole message whose markup * is unbalanced, and an agent's answer is arbitrary text. One stray asterisk * and the notice does not arrive at all. Ugly beats missing, and this is * neither. * * Deliberately conservative. Only paired markers are touched, and single * underscores are left alone entirely — `execute_command` and `snake_case` * appear in these answers constantly, and mangling an identifier to italicise * nothing is worse than leaving a marker visible. */ export declare function stripMarkdown(text: string): string; export interface RunSummary { /** What the run was called — the session display name, not the full prompt. */ task: string; elapsedMs: number; /** Set when the run ended on an error, so the message can say so. */ failure?: string; tokens?: number; /** Pay-per-use dollars. Omitted on a flat-fee plan, where any figure is invented. */ costUsd?: number; /** * The agent's own reply, for a run that was started from the phone. * * Omitted for a run started at the terminal, where the answer is already on * the screen the person is sitting at and sending it would put file contents * or a command line into a chat for nothing. */ answer?: string; } /** * The notification, as the messages to send in order. * * One message when the answer fits, which is the common case. A longer answer * continues into further messages rather than being cut at the first limit — * see `splitAnswer`. The head shares the first message, so the reply is not a * bare wall of text with no idea which run it belongs to. * * Carries the agent's answer only when the run was started from the phone. A * finished run can end with anything in it — a file it read, a command it ran, * a secret inside an error — and this goes to a chat that syncs to Telegram's * servers, so it travels only where it was actually asked for. Start a run at * the terminal and this says there is a result to come back to, and no more. */ export declare function composeRunMessages(summary: RunSummary): string[]; /** * Send it, and say nothing if it fails. * * A notification that could not be delivered is not worth interrupting the * terminal for — the run already finished and its result is on screen. Returns * whether it went, so a caller that does care can look. */ export declare function sendTelegramNotice(credentials: TelegramCredentials, text: string): Promise;