/** * Mapping between Core's server-side message versions and this transcript's indices. * * Core stores the alternatives created by editing a turn as archived branches, addressed by the * user turn they hang off (`forkUserOrdinal`) rather than by transcript position — position is not * stable across a fork, since everything after the anchor is archived and restored wholesale. Both * functions here translate between that addressing and the indices the widget renders, and both are * pure so the edge cases (a turn with no id of its own, a fork whose anchor is currently archived) * are testable without a server. * * Ported from Core's `shared/lib/branchPager.js` and `shared/lib/sessionEditing.js` so the widget's * ‹ n/m › control resolves to the same turn as the app's. */ import type { MessageType } from '@/widget/types'; import type { TorukSessionBranchGroup, TorukSessionMessage } from '@/sessions/sessions.types'; /** Pager state for one user turn: 1-based position among its versions. */ export type BranchPager = { current: number; total: number; /** The message the fork hangs off, as the switch call needs it. Null at the first turn. */ forkAfterMessageId: string | null; }; /** * Attach each version group to the transcript index of the user turn it belongs to. * * Groups are keyed by ordinal, so the walk counts user turns rather than trusting positions: a * transcript may open with a welcome turn the server never stored, which would put every index one * step out if they were compared directly. */ export declare function buildBranchPagerByIndex(groups: readonly TorukSessionBranchGroup[] | undefined, messages: readonly MessageType[] | undefined): Record; /** * Find the stored message id to fork at when editing the turn at `index`. * * A turn sent in this page's lifetime has no id of its own — the widget appends it locally and only * the *answer* comes back with one — so the id has to be recovered from the persisted rows. In * order of confidence: * * 1. the turn's own id, when it was loaded from Core rather than typed here; * 2. the row before this turn's answer, whose id the streaming response did return; * 3. the row at the same user-turn ordinal, when its text still matches; * 4. a content match, but only when exactly one row could be meant. * * Returning null means "do not fork": better to leave the conversation whole than to archive the * wrong half of it. */ export declare function resolveEditAnchorId(rows: readonly TorukSessionMessage[] | undefined, messages: readonly MessageType[] | undefined, index: number): string | null;