import type { SendMessageOptions } from './types.js'; import { type CanonClient } from './client.js'; export interface SendMessageRetryOptions { maxAttempts?: number; baseDelayMs?: number; maxDelayMs?: number; sleep?: (ms: number) => Promise; } export declare const CANON_MESSAGE_TEXT_MAX_BYTES: number; export declare const DEFAULT_CHUNKED_MESSAGE_TEXT_MAX_BYTES = 3800; export interface SendMessageChunkingOptions { maxTextBytes?: number; /** * Opt in to resumable chunked delivery. * * With it on, a mid-sequence failure throws a `ChunkedSendMessageError` * carrying the ids of the parts that ARE durable, so a later attempt can pick * up the tail. Off by default so callers that do not resume keep today's * behaviour: the raw send error, unwrapped. * * Implied by a non-empty `deliveredMessageIds` — passing progress back IS * opting in. */ resumable?: boolean; /** * Ids of the parts an earlier attempt already made durable, in part order. * Those parts are skipped and their ids are carried into the result, so a * resumed send still reports the whole answer. */ deliveredMessageIds?: readonly string[]; } export interface ChunkedSendMessageResult { messageIds: string[]; } /** * The server's message-id idempotency guard rejecting a send. * * A byte-identical replay of a message succeeds (`created: false`), so a 409 * `MESSAGE_ID_EXISTS` means a message already exists under this id whose stored * content diverges from what is being sent now. Either way the id is TAKEN: * that message is durable in Canon and re-sending it is impossible. */ export declare function isCanonMessageIdConflict(error: unknown): boolean; /** * A chunked send that stopped part-way, with the progress needed to resume it. * * Parts 1..`deliveredMessageIds.length` are durable in Canon; only the tail is * missing. Handing `deliveredMessageIds` back to `sendMessageWithRetryChunked` * restarts at the next part instead of re-sending the answer's head — which the * server would reject or duplicate, and which the reader has already seen. */ export declare class ChunkedSendMessageError extends Error { /** Ids of the parts that are durable in Canon, in part order. */ readonly deliveredMessageIds: string[]; /** The part the send stopped on (1-based). */ readonly failedPart: number; /** How many parts the text splits into in total. */ readonly totalParts: number; /** The underlying send failure — what callers should classify. */ readonly cause: unknown; constructor(input: { cause: unknown; deliveredMessageIds: readonly string[]; failedPart: number; totalParts: number; }); /** How many parts are durable — a resume starts at the next one. */ get deliveredParts(): number; } /** * Structural check as well as `instanceof`, so progress survives a caller that * ended up with a second copy of this module (a workspace package resolved to * both `dist` and source, say) instead of silently degrading to "no progress". */ export declare function isChunkedSendMessageError(error: unknown): error is ChunkedSendMessageError; export declare function isRetryableCanonDeliveryError(error: unknown): boolean; export declare function sendMessageWithRetry(client: CanonClient, conversationId: string, text: string, options?: SendMessageOptions, retryOptions?: SendMessageRetryOptions): Promise<{ messageId: string; }>; export declare function utf8ByteLength(value: string): number; export declare function splitTextByUtf8Bytes(text: string, maxBytes?: number): string[]; export interface ChunkedMessagePartMetadataInput { /** Stable group id shared by every part, normally the unsuffixed message id. */ groupId?: string; metadata?: Record; index: number; count: number; } /** * Build metadata for one visible part of a logically single message. * * Earlier parts are progress-only and cannot trigger replies or notifications. * The last part preserves the caller's terminal semantics while removing * streamed text from the trail, because that text is already visible in the * preceding message parts. */ export declare function buildChunkedMessagePartMetadata(input: ChunkedMessagePartMetadataInput): Record; /** * Send `text` as one message, or as an ordered sequence of `-part-N` messages * when it exceeds the byte budget. * * Sequential by construction, so a failure leaves parts 1..k durable and the * tail unsent. Callers that opt into `resumable` get that progress back (see * `ChunkedSendMessageError`) and can hand it to a later attempt, which then * picks up at part k+1. The split is a pure function of the text and the byte * budget, so part N of a resumed attempt is byte-identical to part N of the * attempt that delivered it — which is what makes the `-part-N` ids a safe * resume cursor. * * A byte-identical replay succeeds at the server. Any idempotency conflict * therefore means the stored part differs and is always reported; treating it * as delivered would stitch two different answers together. */ export declare function sendMessageWithRetryChunked(client: CanonClient, conversationId: string, text: string, options?: SendMessageOptions, retryOptions?: SendMessageRetryOptions, chunkingOptions?: SendMessageChunkingOptions): Promise;