@@ -12,6 +12,7 @@ import {
1212 makeAgentUserMessage ,
1313} from "openclaw/plugin-sdk/test-fixtures" ;
1414import { afterEach , describe , expect , it } from "vitest" ;
15+ import { readSessionMessages } from "../../../../src/gateway/session-utils.fs.js" ;
1516import { mirrorCodexAppServerTranscript } from "./transcript-mirror.js" ;
1617
1718const tempDirs : string [ ] = [ ] ;
@@ -119,6 +120,74 @@ describe("mirrorCodexAppServerTranscript", () => {
119120 expect ( records . slice ( 1 ) ) . toHaveLength ( 2 ) ;
120121 } ) ;
121122
123+ it ( "keeps previous turns on the active transcript branch when consecutive app-server turns mirror with different scopes" , async ( ) => {
124+ const sessionFile = await createTempSessionFile ( ) ;
125+ const storePath = path . join ( path . dirname ( sessionFile ) , "sessions.json" ) ;
126+
127+ await mirrorCodexAppServerTranscript ( {
128+ sessionFile,
129+ sessionKey : "session-1" ,
130+ messages : [
131+ makeAgentUserMessage ( {
132+ content : [ { type : "text" , text : "turn one user" } ] ,
133+ timestamp : 1000 ,
134+ } ) ,
135+ makeAgentAssistantMessage ( {
136+ content : [ { type : "text" , text : "turn one assistant" } ] ,
137+ timestamp : 1001 ,
138+ } ) ,
139+ ] ,
140+ idempotencyScope : "codex-app-server:thread-1:turn-1" ,
141+ } ) ;
142+
143+ await mirrorCodexAppServerTranscript ( {
144+ sessionFile,
145+ sessionKey : "session-1" ,
146+ messages : [
147+ makeAgentUserMessage ( {
148+ content : [ { type : "text" , text : "turn two user" } ] ,
149+ timestamp : 2000 ,
150+ } ) ,
151+ makeAgentAssistantMessage ( {
152+ content : [ { type : "text" , text : "turn two assistant" } ] ,
153+ timestamp : 2001 ,
154+ } ) ,
155+ ] ,
156+ idempotencyScope : "codex-app-server:thread-1:turn-2" ,
157+ } ) ;
158+
159+ const raw = await fs . readFile ( sessionFile , "utf8" ) ;
160+ const records = raw
161+ . trim ( )
162+ . split ( "\n" )
163+ . filter ( Boolean )
164+ . map (
165+ ( line ) =>
166+ JSON . parse ( line ) as {
167+ type ?: string ;
168+ id ?: string ;
169+ parentId ?: string | null ;
170+ message ?: { role ?: string } ;
171+ } ,
172+ )
173+ . filter ( ( record ) => record . type === "message" ) ;
174+ expect ( records . map ( ( record ) => record . message ?. role ) ) . toEqual ( [
175+ "user" ,
176+ "assistant" ,
177+ "user" ,
178+ "assistant" ,
179+ ] ) ;
180+ for ( let index = 1 ; index < records . length ; index += 1 ) {
181+ expect ( records [ index ] ?. parentId ) . toBe ( records [ index - 1 ] ?. id ) ;
182+ }
183+
184+ const visibleHistory = readSessionMessages ( "session" , storePath , sessionFile ) ;
185+ expect ( JSON . stringify ( visibleHistory ) ) . toContain ( "turn one user" ) ;
186+ expect ( JSON . stringify ( visibleHistory ) ) . toContain ( "turn one assistant" ) ;
187+ expect ( JSON . stringify ( visibleHistory ) ) . toContain ( "turn two user" ) ;
188+ expect ( JSON . stringify ( visibleHistory ) ) . toContain ( "turn two assistant" ) ;
189+ } ) ;
190+
122191 it ( "runs before_message_write before appending mirrored transcript messages" , async ( ) => {
123192 initializeGlobalHookRunner (
124193 createMockPluginRegistry ( [
0 commit comments