import { type ContentBlock, type PromptResponse, type SessionUpdate } from "@agentclientprotocol/sdk"; import type { ImageContent } from "@earendil-works/pi-ai"; import type { SessionStats } from "../../core/agent-session.ts"; import type { RpcEvent, RpcSlashCommand } from "../rpc/rpc-types.ts"; /** Product limit for the fully rendered prompt text, including resource labels and separators. */ export declare const MAX_PROMPT_BYTES: number; /** * Maximum decoded bytes accepted for one prompt image. Selesai's own inline-image pipeline resizes * anything above 4.5 MiB decoded (`src/utils/image-resize-core.ts`), so an accepted image is always * forwarded unchanged instead of being silently re-encoded by the child. */ export declare const MAX_PROMPT_IMAGE_BYTES: number; /** * Maximum decoded bytes across every image in one prompt (two maximum-size images). Images travel in * the separate RPC `images` array, so the 1 MiB text rule does not apply: a single ordinary phone * photo already exceeds it once base64-encoded. */ export declare const MAX_PROMPT_IMAGE_TOTAL_BYTES: number; /** MIME types accepted for a prompt image. T3 sends exactly these four (`packages/contracts/src/chatAttachment.ts:12-27`). */ export declare const PROMPT_IMAGE_MIME_TYPES: readonly ["image/png", "image/jpeg", "image/gif", "image/webp"]; /** T3 drops untrimmed or longer command names and caps the list at 128 (`probe.ts:41-54,214-245`). */ export declare const MAX_COMMAND_NAME_LENGTH = 128; export declare const MAX_COMMAND_DESCRIPTION_LENGTH = 1024; export declare const MAX_AVAILABLE_COMMANDS = 128; /** Maximum UTF-8 bytes of text displayed for one tool call. */ export declare const MAX_TOOL_CONTENT_BYTES: number; /** Rendered prompt text plus the images that ride beside it in the RPC prompt. */ export interface RenderedPromptContent { text: string; images: ImageContent[]; } /** * Render text, attributed resource links and inline images; this never resolves or fetches a URI. * Images are forwarded in block order and do not consume the text byte budget. Text is required * unless an image is present: T3 sends a later turn whose prompt holds image blocks only once its * interaction instructions are no longer repeated (`provider-acp/src/server/adapter.ts:7347-7352,7366-7368`), * and `AgentSession.prompt` accepts an empty message beside images, so a screenshot sent alone must * not fail. Whitespace never survives as the text, matching Selesai's TUI. */ export declare function renderPromptContent(content: readonly ContentBlock[]): RenderedPromptContent; /** * Product ceiling for one explicitly displayed extension message. The rendered projection — fixed * label and truncation marker included — never exceeds this, so a report that fits the TUI may still * be cut in ACP. Live mapping and transcript replay share both the helper and the ceiling. */ export declare const MAX_EXTENSION_OUTPUT_BYTES: number; /** * Project one explicitly displayed custom message into safe transcript text, or `undefined` when it * must not be published. Only `display === true` publishes; `customType` and `details` are never * forwarded, non-text blocks are dropped and empty projections emit nothing. The result fits * `MAX_EXTENSION_OUTPUT_BYTES` and carries a fixed label plus a visible truncation marker. */ export declare function projectExtensionOutput(message: unknown): string | undefined; /** * Convert Selesai's slash commands into one ACP command list. T3 drops a name that is untrimmed or * longer than 128 characters, treats a `$`-prefixed name as a skill and inserts it as `$name`, and * keeps the first 128 accepted entries (`probe.ts:41-54,214-245`); `ChatComposer.tsx:1912-1930` * inserts `$name` and `/name` respectively. T3 sends a picked skill as plain `$name` text, so skills * are advertised as `$name` (what T3's `$` menu lists) and the adapter rewrites a leading `$name` * to the `/skill:name` Selesai expands (`agent-session.ts:2160-2192,2289-2291`). Other `$`-prefixed * names are dropped instead of being shown as dead skills. */ export declare function createAvailableCommandsUpdate(commands: readonly RpcSlashCommand[]): Extract; /** Names of the skills advertised as `$name`, used to rewrite a leading `$name` mention. */ export declare function skillNames(update: ReturnType): Set; /** Rewrite a leading `$skill args` mention of a known skill to Selesai's `/skill:skill args`. */ export declare function expandSkillMention(text: string, skills: ReadonlySet): string; /** * Convert Selesai's context accounting into an ACP usage sample. The context window must be a * positive size because T3 only shows a meter for `size > 0`; a null estimate skips the sample. * Cost is Selesai's cumulative session cost in USD, matching its own `formatCost` display. */ export declare function createUsageUpdate(stats: SessionStats): Extract | undefined; export interface RpcEventMapper { /** Map one JSON-RPC event into zero or more stable ACP session updates. */ mapEvent(event: RpcEvent): SessionUpdate[]; } /** * Create one mapper per ACP session turn. `sessionNamespace` and `turnNamespace` must be unique * within their respective scopes (UUIDs are suitable); this keeps ACP tool IDs unique across turns. */ export declare function createRpcEventMapper(sessionNamespace: string, turnNamespace: string, cwd?: string): RpcEventMapper; /** * Compaction is projected through ACP's ordinary tool convention, because T3 never advertises the * `session.compaction` capability (`findings/T01.md` NEG-4): a `tool_call` whose id starts with * `acp-compaction:` becomes T3's compaction timeline item, the summary is read from `rawOutput`, and * only `status: "completed"` clears its context meter. A session keeps one counter so every card has * a stable, session-unique id across turns, and a terminal frame always follows an announced card. */ export declare const COMPACTION_TOOL_CALL_PREFIX = "acp-compaction:"; export interface CompactionMapper { /** Map one compaction lifecycle event into zero or more ordinary tool-call updates. */ mapEvent(event: RpcEvent): SessionUpdate[]; /** Fail a card that was opened but never completed, so a handled turn cannot leave one running. */ close(): SessionUpdate[]; } /** * Create the session-scoped compaction projector. Callers own the buffering decision; this mapper * only translates, and it treats a repeated start as a failure of the previous card so no running * synthetic card is ever left behind. */ export declare function createCompactionMapper(sessionNamespace: string): CompactionMapper; export interface TerminalResultMapper { /** Returns a prompt result only when the input event is agent_settled; agent_end is not terminal. */ mapEvent(event: RpcEvent, options?: { cancelRequested?: boolean; }): PromptResponse | undefined; } /** Track the last assistant outcome, ignoring intermediate errors that a later retry replaces. */ export declare function createTerminalResultMapper(): TerminalResultMapper; /** T3 clamps notice titles to 1024 and descriptions to 4096 characters (`findings/T01.md` NOTICE-6). */ export declare const MAX_NOTICE_TITLE_LENGTH = 1024; export declare const MAX_NOTICE_DESCRIPTION_LENGTH = 4096; export type NoticeUpdate = Extract; /** * Project one RPC lifecycle event into a single notice, or nothing. Notices are fire-and-forget * system events, so only a fixed vocabulary and validated numbers survive: provider retry bodies * (`errorMessage`/`finalError`) and extension payloads (message, error, path) never reach a frame. * An exhausted retry is deliberately silent — the terminal result mapper remains the only source of * a final failure, so a failure is never reported twice. */ export declare function noticeForEvent(event: RpcEvent): NoticeUpdate | undefined;