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