@@ -4,13 +4,26 @@ import {
44} from "./server-methods/chat.js" ;
55import { attachOpenClawTranscriptMeta , readSessionMessages } from "./session-utils.js" ;
66
7+ type SessionHistoryTranscriptMeta = {
8+ seq ?: number ;
9+ } ;
10+
11+ export type SessionHistoryMessage = Record < string , unknown > & {
12+ __openclaw ?: SessionHistoryTranscriptMeta ;
13+ } ;
14+
715export type PaginatedSessionHistory = {
8- items : unknown [ ] ;
9- messages : unknown [ ] ;
16+ items : SessionHistoryMessage [ ] ;
17+ messages : SessionHistoryMessage [ ] ;
1018 nextCursor ?: string ;
1119 hasMore : boolean ;
1220} ;
1321
22+ export type SessionHistorySnapshot = {
23+ history : PaginatedSessionHistory ;
24+ rawTranscriptSeq : number ;
25+ } ;
26+
1427type SessionHistoryTranscriptTarget = {
1528 sessionId : string ;
1629 storePath ?: string ;
@@ -26,20 +39,33 @@ function resolveCursorSeq(cursor: string | undefined): number | undefined {
2639 return Number . isFinite ( value ) && value > 0 ? value : undefined ;
2740}
2841
29- export function resolveMessageSeq ( message : unknown ) : number | undefined {
30- if ( ! message || typeof message !== "object" || Array . isArray ( message ) ) {
31- return undefined ;
32- }
33- const meta = ( message as { __openclaw ?: unknown } ) . __openclaw ;
34- if ( ! meta || typeof meta !== "object" || Array . isArray ( meta ) ) {
35- return undefined ;
36- }
37- const seq = ( meta as { seq ?: unknown } ) . seq ;
42+ function toSessionHistoryMessages ( messages : unknown [ ] ) : SessionHistoryMessage [ ] {
43+ return messages . filter (
44+ ( message ) : message is SessionHistoryMessage =>
45+ Boolean ( message ) && typeof message === "object" && ! Array . isArray ( message ) ,
46+ ) ;
47+ }
48+
49+ function buildPaginatedSessionHistory ( params : {
50+ messages : SessionHistoryMessage [ ] ;
51+ hasMore : boolean ;
52+ nextCursor ?: string ;
53+ } ) : PaginatedSessionHistory {
54+ return {
55+ items : params . messages ,
56+ messages : params . messages ,
57+ hasMore : params . hasMore ,
58+ ...( params . nextCursor ? { nextCursor : params . nextCursor } : { } ) ,
59+ } ;
60+ }
61+
62+ export function resolveMessageSeq ( message : SessionHistoryMessage | undefined ) : number | undefined {
63+ const seq = message ?. __openclaw ?. seq ;
3864 return typeof seq === "number" && Number . isFinite ( seq ) && seq > 0 ? seq : undefined ;
3965}
4066
4167export function paginateSessionMessages (
42- messages : unknown [ ] ,
68+ messages : SessionHistoryMessage [ ] ,
4369 limit : number | undefined ,
4470 cursor : string | undefined ,
4571) : PaginatedSessionHistory {
@@ -58,30 +84,36 @@ export function paginateSessionMessages(
5884 }
5985 }
6086 const start = typeof limit === "number" && limit > 0 ? Math . max ( 0 , endExclusive - limit ) : 0 ;
61- const items = messages . slice ( start , endExclusive ) ;
62- const firstSeq = resolveMessageSeq ( items [ 0 ] ) ;
63- return {
64- items,
65- messages : items ,
87+ const paginatedMessages = messages . slice ( start , endExclusive ) ;
88+ const firstSeq = resolveMessageSeq ( paginatedMessages [ 0 ] ) ;
89+ return buildPaginatedSessionHistory ( {
90+ messages : paginatedMessages ,
6691 hasMore : start > 0 ,
6792 ...( start > 0 && typeof firstSeq === "number" ? { nextCursor : String ( firstSeq ) } : { } ) ,
68- } ;
93+ } ) ;
6994}
7095
71- function sanitizeRawTranscriptMessages ( params : {
96+ export function buildSessionHistorySnapshot ( params : {
7297 rawMessages : unknown [ ] ;
7398 maxChars ?: number ;
7499 limit ?: number ;
75100 cursor ?: string ;
76- } ) : PaginatedSessionHistory {
77- return paginateSessionMessages (
78- sanitizeChatHistoryMessages (
79- params . rawMessages ,
80- params . maxChars ?? DEFAULT_CHAT_HISTORY_TEXT_MAX_CHARS ,
101+ } ) : SessionHistorySnapshot {
102+ const history = paginateSessionMessages (
103+ toSessionHistoryMessages (
104+ sanitizeChatHistoryMessages (
105+ params . rawMessages ,
106+ params . maxChars ?? DEFAULT_CHAT_HISTORY_TEXT_MAX_CHARS ,
107+ ) ,
81108 ) ,
82109 params . limit ,
83110 params . cursor ,
84111 ) ;
112+ const rawHistoryMessages = toSessionHistoryMessages ( params . rawMessages ) ;
113+ return {
114+ history,
115+ rawTranscriptSeq : resolveMessageSeq ( rawHistoryMessages . at ( - 1 ) ) ?? rawHistoryMessages . length ,
116+ } ;
85117}
86118
87119export class SessionHistorySseState {
@@ -92,6 +124,22 @@ export class SessionHistorySseState {
92124 private sentHistory : PaginatedSessionHistory ;
93125 private rawTranscriptSeq : number ;
94126
127+ static fromRawSnapshot ( params : {
128+ target : SessionHistoryTranscriptTarget ;
129+ rawMessages : unknown [ ] ;
130+ maxChars ?: number ;
131+ limit ?: number ;
132+ cursor ?: string ;
133+ } ) : SessionHistorySseState {
134+ return new SessionHistorySseState ( {
135+ target : params . target ,
136+ maxChars : params . maxChars ,
137+ limit : params . limit ,
138+ cursor : params . cursor ,
139+ initialRawMessages : params . rawMessages ,
140+ } ) ;
141+ }
142+
95143 constructor ( params : {
96144 target : SessionHistoryTranscriptTarget ;
97145 maxChars ?: number ;
@@ -104,13 +152,14 @@ export class SessionHistorySseState {
104152 this . limit = params . limit ;
105153 this . cursor = params . cursor ;
106154 const rawMessages = params . initialRawMessages ?? this . readRawMessages ( ) ;
107- this . sentHistory = sanitizeRawTranscriptMessages ( {
155+ const snapshot = buildSessionHistorySnapshot ( {
108156 rawMessages,
109157 maxChars : this . maxChars ,
110158 limit : this . limit ,
111159 cursor : this . cursor ,
112160 } ) ;
113- this . rawTranscriptSeq = resolveMessageSeq ( rawMessages . at ( - 1 ) ) ?? rawMessages . length ;
161+ this . sentHistory = snapshot . history ;
162+ this . rawTranscriptSeq = snapshot . rawTranscriptSeq ;
114163 }
115164
116165 snapshot ( ) : PaginatedSessionHistory {
@@ -133,28 +182,31 @@ export class SessionHistorySseState {
133182 if ( sanitized . length === 0 ) {
134183 return null ;
135184 }
136- const sanitizedMessage = sanitized [ 0 ] ;
137- this . sentHistory = {
138- items : [ ...this . sentHistory . items , sanitizedMessage ] ,
139- messages : [ ...this . sentHistory . items , sanitizedMessage ] ,
185+ const [ sanitizedMessage ] = toSessionHistoryMessages ( sanitized ) ;
186+ if ( ! sanitizedMessage ) {
187+ return null ;
188+ }
189+ const nextMessages = [ ...this . sentHistory . messages , sanitizedMessage ] ;
190+ this . sentHistory = buildPaginatedSessionHistory ( {
191+ messages : nextMessages ,
140192 hasMore : false ,
141- } ;
193+ } ) ;
142194 return {
143195 message : sanitizedMessage ,
144196 messageSeq : resolveMessageSeq ( sanitizedMessage ) ,
145197 } ;
146198 }
147199
148200 refresh ( ) : PaginatedSessionHistory {
149- const rawMessages = this . readRawMessages ( ) ;
150- this . rawTranscriptSeq = resolveMessageSeq ( rawMessages . at ( - 1 ) ) ?? rawMessages . length ;
151- this . sentHistory = sanitizeRawTranscriptMessages ( {
152- rawMessages,
201+ const snapshot = buildSessionHistorySnapshot ( {
202+ rawMessages : this . readRawMessages ( ) ,
153203 maxChars : this . maxChars ,
154204 limit : this . limit ,
155205 cursor : this . cursor ,
156206 } ) ;
157- return this . sentHistory ;
207+ this . rawTranscriptSeq = snapshot . rawTranscriptSeq ;
208+ this . sentHistory = snapshot . history ;
209+ return snapshot . history ;
158210 }
159211
160212 private readRawMessages ( ) : unknown [ ] {
0 commit comments