import type { AttentionMode, ChannelMode } from "@cotal-ai/core"; import type { CloseReport, ConnectionState, PublishResult, Result, SessionEvent, SessionIdentity, SessionMessage } from "./session.js"; /** Bounded transcript retention. A dropped entry leaves a visible gap marker. Provisional. */ export declare const MAX_TRANSCRIPT = 500; /** Bounded draft length, in UTF-16 code units. Provisional. */ export declare const MAX_DRAFT = 65536; /** `dm` is a typed name or principal the session resolves. `peer` is an exact principal taken from a * received message, sent as is with no name lookup; its `name` is display only. */ export type Recipient = { readonly kind: "channel"; readonly channel: string; } | { readonly kind: "dm"; readonly to: string; } | { readonly kind: "peer"; readonly principal: string; readonly name: string; readonly replyTo?: string; } | { readonly kind: "role"; readonly role: string; }; export type TranscriptEntry = { readonly kind: "message"; readonly message: SessionMessage; } | { readonly kind: "sent"; readonly to: Recipient; readonly text: string; readonly result: PublishResult; } | { readonly kind: "notice"; readonly tone: "ok" | "waiting" | "failed" | "hint"; readonly text: string; } | { readonly kind: "gap"; readonly scope: string; readonly reason: string; }; export interface ChatState { readonly identity: SessionIdentity; readonly connection: ConnectionState; readonly recipient: Recipient | undefined; readonly draft: string; /** Set while a send is in flight. A second Enter does nothing until its result arrives. `draft` is * the composer text at submit, so the result clears it only if the user has not typed on. */ readonly pending: { readonly to: Recipient; readonly text: string; readonly draft: string; } | undefined; /** Leave was requested with an unsent draft: Keep editing / Discard and leave. */ readonly confirmLeave: boolean; readonly transcript: readonly TranscriptEntry[]; readonly dropped: number; readonly attention: AttentionMode; readonly channelModes: Readonly>; readonly ended: CloseReport | undefined; /** The session reported `ended` before a close report arrived. Only leaving is possible. */ readonly endedByEvent: boolean; } export declare function initialState(identity: SessionIdentity, recipient?: Recipient): ChatState; /** Record a front-end notice (picker disclosure, terminal limits). `text` is escaped when rendered. */ export declare function addNotice(state: ChatState, tone: "ok" | "waiting" | "failed" | "hint", text: string): ChatState; /** Fold one session event. A message becomes visible state here; the caller acknowledges it only * after the frame that shows it is written. */ export declare function applyEvent(state: ChatState, event: SessionEvent): ChatState; /** What the caller must do against its admitted session. Every intent acts on that session only. */ export type Intent = { readonly kind: "publish"; readonly to: Recipient; readonly text: string; } | { readonly kind: "slash"; readonly command: SlashCommand; } | { readonly kind: "leave"; } | { readonly kind: "pick-recipient"; }; /** Normalized key input. The terminal adapter maps raw input and reports whether it can tell * Shift+Enter from Enter. A bracketed paste arrives as one `paste` with all its text. */ export type Key = { readonly kind: "char"; readonly text: string; } | { readonly kind: "paste"; readonly text: string; } | { readonly kind: "enter"; readonly shift: boolean; } | { readonly kind: "backspace"; } | { readonly kind: "tab"; } | { readonly kind: "escape"; } | { readonly kind: "ctrl-c"; } | { readonly kind: "ctrl-d"; }; /** * Fold one key. Enter sends; Shift+Enter inserts a newline only when the terminal can tell them * apart (`shiftEnterDistinct`); a paste is always data and never a send. Ctrl-C / Ctrl-D leave, * through a confirmation when a draft exists. Nothing sends while disconnected or while a send is * in flight, and the draft survives both. */ export declare function applyKey(state: ChatState, key: Key, shiftEnterDistinct: boolean): { state: ChatState; intent?: Intent; }; /** Record a publish outcome. The draft clears only when the message was stored; refused and * unknown keep it and are never resent automatically. */ export declare function applyPublishResult(state: ChatState, result: PublishResult): ChatState; export declare function setRecipient(state: ChatState, recipient: Recipient): ChatState; /** Start a reply to a received DM or role task: to its sender's principal, with no name lookup, * keeping the message id as `replyTo`. A channel message is answered on its channel. */ export declare function replyTo(state: ChatState, m: SessionMessage): ChatState; /** The latest received DM or role task still in the transcript. */ export declare function latestDirected(state: ChatState): SessionMessage | undefined; /** `/reply`: answer the latest received DM or role task. */ export declare function replyToLatest(state: ChatState): ChatState; export declare function applyEnded(state: ChatState, close: CloseReport): ChatState; /** * Fold a slash command's result. A refusal is shown as a denial with its native reason and is never * rendered as an empty list. `describe` turns a successful value into the lines to show; it must * escape untrusted text with `oneLine`. Local state that a command changes (attention, channel * modes) is updated only after the session confirmed it. */ export declare function applySlashResult(state: ChatState, command: SlashCommand, result: Result, describe: (value: T) => readonly string[]): ChatState; export type SlashCommand = { readonly name: "who"; } | { readonly name: "whoami"; } | { readonly name: "status"; readonly status: "idle" | "working" | "waiting"; readonly activity?: string; } | { readonly name: "attention"; readonly mode: AttentionMode; } | { readonly name: "channel-mode"; readonly channel: string; readonly mode: ChannelMode | "normal"; } | { readonly name: "join"; readonly channel: string; } | { readonly name: "leave"; readonly channel: string; } | { readonly name: "info"; readonly channel: string; } | { readonly name: "history"; readonly limit: number; } | { readonly name: "inbox"; readonly peek: boolean; } | { readonly name: "connection"; } | { readonly name: "reconnect"; } | { readonly name: "reply"; } | { readonly name: "help"; } | { readonly name: "quit"; }; /** Whether a slash command runs while disconnected: the reads of this session's own state, `/reply`, * which only picks a recipient, and `/help`. */ export declare function runsOffline(command: SlashCommand): boolean; /** The spec's slash set, plus `/whoami` (spec "History and presence reads"), `/reply`, `/help` and `/quit`. */ export declare const SLASH_COMMANDS: readonly ["/who", "/whoami", "/status [activity]", "/attention ", "/quiet ", "/mute ", "/normal ", "/join ", "/leave ", "/info ", "/history [n]", "/inbox [--peek]", "/connection", "/reconnect", "/reply", "/help", "/quit"]; /** The `/help` answer: every slash command with its arguments. */ export declare const slashHelp: string; /** Parse one slash line. Channel names are checked by the session against native policy, not here. */ export declare function parseSlash(line: string): { ok: true; command: SlashCommand; } | { ok: false; error: string; }; export type Tone = "ok" | "waiting" | "failed" | "hint" | "label" | "selected" | "body"; /** One rendered row: plain words that stay meaningful without color, and a tone the renderer may color. */ export interface Row { readonly text: string; readonly tone: Tone; } export declare function recipientLabel(r: Recipient | undefined): string; /** A principal and the display name it carries. */ export interface Person { readonly id: string; readonly name: string; } /** Each display name more than one principal carries among `people`, with those principals. Names * compare trimmed and case-insensitively. */ export declare function sharedNames(people: Iterable): ReadonlyMap; /** A person's display name, followed by their principal when another principal shares the name. * Unescaped: the caller escapes it like any other untrusted text. */ export declare function personLabel(p: Person, shared: ReadonlyMap): string; /** The senders of the received messages still in the transcript. */ export declare function heardFrom(state: ChatState): Person[]; /** How long before a session's set end the view says it is ending. */ export declare const ENDING_NOTICE_MS = 60000; /** The notice a session with a set end gets {@link ENDING_NOTICE_MS} before it, and when it is due. * Undefined for a session without a set end. */ export declare function endingNotice(identity: SessionIdentity): { readonly at: number; readonly text: string; } | undefined; /** The header: the mesh first, as every command's report opens, then who this session is and under * which mode's guarantees. Never a credential. */ export declare function headerRows(state: ChatState): Row[]; /** Transcript lines fitted to `width` columns. Untrusted text is escaped; a body's own newlines * become separate indented lines, never a forged new entry. `shared` defaults to the names shared * within `state`; a caller rendering part of a transcript passes the whole one's. */ export declare function transcriptRows(state: ChatState, width: number, shared?: ReadonlyMap): Row[]; /** The composer and key hints. */ export declare function composerRows(state: ChatState, width: number, shiftEnterDistinct: boolean): Row[]; /** The exit summary. It reports only what the session observed, and never says messages were * erased: retention follows the space policy. */ export declare function closeSummary(identity: SessionIdentity, close: CloseReport): string; //# sourceMappingURL=view.d.ts.map