1- import { describe , expect , it } from "vitest" ;
1+ import fs from "node:fs" ;
2+ import os from "node:os" ;
3+ import path from "node:path" ;
4+ import {
5+ getSessionEntry ,
6+ readAmbientTranscriptWatermark ,
7+ resolveAmbientTranscriptWatermarkKey ,
8+ updateAmbientTranscriptWatermark ,
9+ upsertSessionEntry ,
10+ } from "openclaw/plugin-sdk/session-store-runtime" ;
11+ import { afterEach , describe , expect , it } from "vitest" ;
212import { buildTelegramMessageContextForTest } from "./bot-message-context.test-harness.js" ;
313import type { TelegramPromptContextEntry } from "./bot-message-context.types.js" ;
414
@@ -20,6 +30,20 @@ const telegramChatWindowContext: TelegramPromptContextEntry = {
2030 } ,
2131} ;
2232
33+ const tempDirs : string [ ] = [ ] ;
34+
35+ function createTempSessionStorePath ( ) : string {
36+ const tempDir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , "openclaw-telegram-watermark-" ) ) ;
37+ tempDirs . push ( tempDir ) ;
38+ return path . join ( tempDir , "sessions.json" ) ;
39+ }
40+
41+ afterEach ( ( ) => {
42+ for ( const tempDir of tempDirs . splice ( 0 ) ) {
43+ fs . rmSync ( tempDir , { recursive : true , force : true } ) ;
44+ }
45+ } ) ;
46+
2347describe ( "buildTelegramMessageContext prompt context" , ( ) => {
2448 it ( "omits Telegram chat-window context for existing unthreaded private DM sessions" , async ( ) => {
2549 const ctx = await buildTelegramMessageContextForTest ( {
@@ -180,6 +204,7 @@ describe("buildTelegramMessageContext prompt context", () => {
180204 readAmbientTranscriptWatermark : ( { key } ) =>
181205 key === '["telegram","default","-1001234567890",""]'
182206 ? {
207+ sessionId : "session-current" ,
183208 messageId : "11" ,
184209 timestampMs : 1_700_000_001_000 ,
185210 updatedAt : 1_700_000_003_000 ,
@@ -237,6 +262,7 @@ describe("buildTelegramMessageContext prompt context", () => {
237262 ] ) ,
238263 sessionRuntime : {
239264 readAmbientTranscriptWatermark : ( ) => ( {
265+ sessionId : "session-current" ,
240266 messageId : "11" ,
241267 timestampMs : 1_700_000_001_000 ,
242268 updatedAt : 1_700_000_003_000 ,
@@ -286,6 +312,7 @@ describe("buildTelegramMessageContext prompt context", () => {
286312 readAmbientTranscriptWatermark : ( { key } ) =>
287313 key === '["telegram","default","-1001234567890",""]'
288314 ? {
315+ sessionId : "session-current" ,
289316 messageId : "11" ,
290317 timestampMs : 1_700_000_001_000 ,
291318 updatedAt : 1_700_000_003_000 ,
@@ -306,4 +333,94 @@ describe("buildTelegramMessageContext prompt context", () => {
306333 expect ( ctx . ctxPayload . InboundHistory ) . toBeUndefined ( ) ;
307334 expect ( ctx . ctxPayload . UntrustedStructuredContext ) . toBeUndefined ( ) ;
308335 } ) ;
336+
337+ it ( "backfills Telegram group history when the ambient watermark belongs to a reset session" , async ( ) => {
338+ const storePath = createTempSessionStorePath ( ) ;
339+ const sessionKey = "agent:main:telegram:group:-1001234567890" ;
340+ const key = resolveAmbientTranscriptWatermarkKey ( {
341+ channel : "telegram" ,
342+ accountId : "default" ,
343+ conversationId : "-1001234567890" ,
344+ } ) ;
345+
346+ await upsertSessionEntry ( {
347+ storePath,
348+ sessionKey,
349+ entry : { sessionId : "before-reset" , updatedAt : 1_700_000_000_000 } ,
350+ } ) ;
351+ await updateAmbientTranscriptWatermark ( {
352+ storePath,
353+ sessionKey,
354+ key,
355+ messageId : "11" ,
356+ timestampMs : 1_700_000_001_000 ,
357+ } ) ;
358+ const persistedEntry = getSessionEntry ( { storePath, sessionKey } ) ;
359+ if ( ! persistedEntry ) {
360+ throw new Error ( "Expected persisted session entry" ) ;
361+ }
362+ await upsertSessionEntry ( {
363+ storePath,
364+ sessionKey,
365+ entry : {
366+ ...persistedEntry ,
367+ sessionId : "after-reset" ,
368+ updatedAt : 1_700_000_002_000 ,
369+ } ,
370+ } ) ;
371+
372+ const ctx = await buildTelegramMessageContextForTest ( {
373+ message : {
374+ message_id : 13 ,
375+ chat : { id : - 1001234567890 , type : "supergroup" , title : "Forum" } ,
376+ from : { id : 1234 , first_name : "Pat" } ,
377+ text : "@bot what happened?" ,
378+ entities : [ { type : "mention" , offset : 0 , length : 4 } ] ,
379+ } ,
380+ historyLimit : 10 ,
381+ groupHistories : new Map ( [
382+ [
383+ "-1001234567890" ,
384+ [
385+ {
386+ messageId : "10" ,
387+ sender : "Sam" ,
388+ timestamp : 1_700_000_000_000 ,
389+ body : "persisted ambient one" ,
390+ } ,
391+ {
392+ messageId : "11" ,
393+ sender : "Lee" ,
394+ timestamp : 1_700_000_001_000 ,
395+ body : "persisted ambient two" ,
396+ } ,
397+ {
398+ messageId : "12" ,
399+ sender : "Mira" ,
400+ timestamp : 1_700_000_002_000 ,
401+ body : "unpersisted gap" ,
402+ } ,
403+ ] ,
404+ ] ,
405+ ] ) ,
406+ sessionRuntime : {
407+ readAmbientTranscriptWatermark,
408+ resolveAmbientTranscriptWatermarkKey,
409+ resolveStorePath : ( ) => storePath ,
410+ } ,
411+ } ) ;
412+
413+ expect ( ctx ?. ctxPayload . UntrustedStructuredContext ) . toEqual ( [
414+ expect . objectContaining ( {
415+ type : "chat_window" ,
416+ payload : expect . objectContaining ( {
417+ messages : [
418+ expect . objectContaining ( { message_id : "10" , body : "persisted ambient one" } ) ,
419+ expect . objectContaining ( { message_id : "11" , body : "persisted ambient two" } ) ,
420+ expect . objectContaining ( { message_id : "12" , body : "unpersisted gap" } ) ,
421+ ] ,
422+ } ) ,
423+ } ) ,
424+ ] ) ;
425+ } ) ;
309426} ) ;
0 commit comments