/** * Starting work from the phone. * * Approval lets the phone answer a question the agent asked. This lets the * phone ask one — which is a different thing, and a larger one. A chat that can * only say Run, Skip or Cancel is bounded by what the agent already decided to * do; a chat that can send a prompt is a keyboard attached to this machine. * That is why it is off by default and has its own switch rather than riding on * the approval one. */ import { type TelegramCredentials } from './telegramUpdates'; /** Longest prompt accepted. Past this it is a paste, not an instruction. */ export declare const MAX_PROMPT_LENGTH = 2000; /** * How old a message may be and still run. * * Telegram keeps undelivered updates for 24 hours. Without this, a message * typed at midnight to a machine that was switched off would be handed to the * agent the moment the CLI next started — hours later, in a repository that has * moved on, with nobody watching. A prompt is an instruction for now. */ export declare const MAX_MESSAGE_AGE_MS: number; export type PromptRejection = 'not-from-owner' | 'not-text' | 'empty' | 'too-long' | 'stale' | 'command'; export type PromptResult = { ok: true; text: string; } | { ok: false; reason: PromptRejection; }; /** * Whether a message update came from the one chat allowed to drive this agent. * * A bot's username is discoverable, so without this anyone who found it could * type into somebody else's terminal. Mirrors the approval check deliberately: * both gate the same machine, and they should not be able to disagree. */ export declare function messageFromOwner(message: unknown, chatID: string): boolean; /** * Turn a Telegram message into a prompt, or say why it is not one. * * Returns a reason rather than null throughout: every rejection here has a * different thing to tell the sender, and a bot that goes quiet is * indistinguishable from one that is switched off. */ export declare function extractPrompt(message: unknown, chatID: string, now?: number): PromptResult; /** What to say back, so silence never has to be interpreted. */ export declare function describeRejection(reason: PromptRejection): string | null; /** * The one instruction waiting for the agent to be free. * * One slot, and a newer message replaces an older one. A queue that grows is a * queue that runs things you have forgotten asking for: send two corrections * while a run is busy and you meant the second, not both in order. Replacing * says so out loud rather than quietly dropping either. */ export declare class InboxQueue { private pending; /** @returns whether this displaced an instruction that had not run yet. */ offer(text: string): { replaced: boolean; }; /** Hand over what is waiting, and empty the slot. */ take(): string | null; get waiting(): boolean; clear(): void; } /** Confirmation for a prompt that will run later, so the sender can stop wondering. */ export declare function describeQueued(replaced: boolean): string; /** Confirmation for a prompt that starts immediately. */ export declare function describeStarted(text: string): string; export declare function markRunFromPhone(): void; /** True once per run that came from the phone, then false again. */ export declare function takeRunFromPhone(): boolean; export interface InboxHost { /** True while a run owns the agent, so a prompt has to wait its turn. */ isBusy: () => boolean; /** Run a prompt as though it had been typed into the input. */ submit: (text: string) => void; /** Say something back on the phone. Failures here are not worth surfacing. */ reply: (text: string) => void; } /** * Listen for instructions from the phone until the returned function is called. * * The queue is drained by `drain`, which the host calls when a run ends — * rather than polled from here — so that "the agent is free" is decided in one * place instead of two that can disagree. */ export declare function attachTelegramInbox(credentials: TelegramCredentials, host: InboxHost): { stop: () => void; drain: () => void; };