| 1 | // ── Windowed history paging (desktop/history_slice.go) ────────────────────── |
| 2 | // HistorySliceForTab pages toward older history with an opaque cursor; the |
| 3 | // first call uses cursor "" for the newest page. Entry IDs are stable for the |
| 4 | // life of a session revision (s<file>:r<epoch>:m<msgIndex>:o<subOrder>). |
| 5 | import type { HistoryMessage } from "./types"; |
| 6 | |
| 7 | export interface HistorySliceRequest { |
| 8 | cursor: string; // "" = newest page; pass nextCursor to page older |
| 9 | turns?: number; |
| 10 | entries?: number; |
| 11 | bytes?: number; |
| 12 | /** Page toward newer history from `cursor` instead of older (window only). */ |
| 13 | newer?: boolean; |
| 14 | /** Direct anchors require history-native-navigation-v1 on a bound reader. */ |
| 15 | anchor?: HistoryWindowRequestView["anchor"]; |
| 16 | turn?: number; |
| 17 | messageId?: string; |
| 18 | generation?: string; |
| 19 | snapshotSequence?: number; |
| 20 | } |
| 21 | |
| 22 | // HistoryContentRef marks a string field replaced inline by a ≤4KiB preview; |
| 23 | // the full value is fetchable in chunks via HistoryContentForTab. |
| 24 | export interface HistoryContentRef { |
| 25 | readHandleId?: string; |
| 26 | transcriptRef?: import("./transcriptProtocol").TranscriptContentRef; |
| 27 | entryId: string; |
| 28 | field: string; // content|reasoning|submitText|detail|code|summary|archive|toolResultError|toolArguments|toolSubject|toolSummary|toolDiff |
| 29 | size: number; |
| 30 | chunks: number; |
| 31 | toolCallId?: string; |
| 32 | revision: number; |
| 33 | revKnown?: boolean; |
| 34 | digest: string; |
| 35 | /** Canonical v4 content identity. Present on the unified locator protocol. */ |
| 36 | canonicalRef?: { digest: string; bytes: number; mediaType?: string; name?: string; indexDigest?: string; integrityBlockBytes?: number }; |
| 37 | } |
| 38 | |
| 39 | export interface HistoryEntry { |
| 40 | entryId: string; |
| 41 | turn: number; // 1-based visible turn (0 = before the first turn) |
| 42 | order: number; // absolute provider-message index |
| 43 | message: HistoryMessage; |
| 44 | refs: HistoryContentRef[]; |
| 45 | } |
| 46 | |
| 47 | export interface SessionClearResult { |
| 48 | sessionPath: string; |
| 49 | sessionId?: string; |
| 50 | session?: { hostId: string; sessionId: string } | null; |
| 51 | sessionRevision?: number; |
| 52 | sessionDigest?: string; |
| 53 | sessionGeneration: number; |
| 54 | } |
| 55 | |
| 56 | export interface HistorySlice { |
| 57 | entries: HistoryEntry[]; |
| 58 | nextCursor: string; // toward older; empty when none |
| 59 | hasOlder: boolean; |
| 60 | /** |
| 61 | * history-window-v1 only. Protocol 7 has no newer cursor: an old service |
| 62 | * keeps the bounded newest page and its forward paging rather than being |
| 63 | * asked to simulate a bidirectional window through full downloads. |
| 64 | */ |
| 65 | newerCursor?: string; |
| 66 | hasNewer?: boolean; |
| 67 | totalTurns: number; |
| 68 | startTurn: number; |
| 69 | endTurn: number; |
| 70 | stale: boolean; // cursor bound to an older session revision: discard + reload |
| 71 | revision: number; |
| 72 | revisionKnown?: boolean; |
| 73 | digest?: string; |
| 74 | // Diagnostic read path: index|scan|event-log|live-index|live-fallback. |
| 75 | source?: string; |
| 76 | error?: string; // failed read; empty entries alone are not an error |
| 77 | } |
| 78 | |
| 79 | // ── history-window-v1 (Go session.ReadHistoryWindow) ──────────────────────── |
| 80 | // One bounded page located around an anchor rather than walked from the newest |
| 81 | // position, plus the cursors that keep reading in both directions. Cursors pin |
| 82 | // a fixed snapshot: appends keep them valid, a storage replacement or |
| 83 | // projection rebuild answers stale_cursor. |
| 84 | export interface HistoryWindowRequestView { |
| 85 | snapshotSequence?: number; |
| 86 | generation?: string; |
| 87 | anchor: "newest" | "message" | "turn" | "cursor"; |
| 88 | messageId?: string; |
| 89 | turn?: number; |
| 90 | cursor?: string; |
| 91 | direction?: "older" | "newer"; |
| 92 | limit?: number; |
| 93 | } |
| 94 | |
| 95 | /** |
| 96 | * "unsupported" is the peer answering that it never negotiated |
| 97 | * history-window-v1: the reader keeps its protocol-7 pages and the surface |
| 98 | * offers the upgrade hint, rather than the tab losing its history. |
| 99 | */ |
| 100 | export type HistoryWindowStatus = "preparing" | "ready" | "failed" | "stale_cursor" | "not_found" | "unsupported"; |
| 101 | |
| 102 | export interface HistoryWindowPageView { |
| 103 | entries: HistoryEntry[]; |
| 104 | status: HistoryWindowStatus; |
| 105 | /** Cursor fetching the page immediately older than this one ("" when none). */ |
| 106 | olderCursor: string; |
| 107 | /** Cursor fetching the page immediately newer than this one ("" when none). */ |
| 108 | newerCursor: string; |
| 109 | hasOlder: boolean; |
| 110 | hasNewer: boolean; |
| 111 | totalTurns: number; |
| 112 | startTurn: number; |
| 113 | endTurn: number; |
| 114 | revision: number; |
| 115 | revisionKnown: boolean; |
| 116 | digest: string; |
| 117 | } |
| 118 | |
| 119 | /** history-window-v1 per-field body read: one bounded, aligned fragment. */ |
| 120 | export interface MessageFieldView { |
| 121 | status: "ready" | "not_found" | "preparing"; |
| 122 | messageId: string; |
| 123 | version: number; |
| 124 | field: string; |
| 125 | /** Length of the field's JSON source, not of the decoded value. */ |
| 126 | totalBytes: number; |
| 127 | offset: number; |
| 128 | /** Body fragment; empty on a finished or absent field. */ |
| 129 | data: string; |
| 130 | /** Next range start; 0 means the field is fully read. */ |
| 131 | nextOffset: number; |
| 132 | encoding: string; |
| 133 | } |
| 134 | |
| 135 | export interface HistoryContentChunk { |
| 136 | entryId: string; |
| 137 | field: string; |
| 138 | chunk: number; |
| 139 | chunks: number; |
| 140 | data: string; |
| 141 | done: boolean; |
| 142 | stale: boolean; |
| 143 | } |
| 144 |