@@ -391,6 +391,91 @@ test("sessions.compact without maxLines runs embedded manual compaction for chec
391391 ws . close ( ) ;
392392} ) ;
393393
394+ test ( "sessions.compact treats Codex native compaction start as pending, not completed" , async ( ) => {
395+ const { dir, storePath } = await createSessionStoreDir ( ) ;
396+ await fs . writeFile (
397+ path . join ( dir , "sess-codex.jsonl" ) ,
398+ `${ JSON . stringify ( { role : "user" , content : "hello codex" } ) } \n` ,
399+ "utf-8" ,
400+ ) ;
401+ await writeSessionStore ( {
402+ entries : {
403+ main : sessionStoreEntry ( "sess-codex" , {
404+ agentHarnessId : "codex" ,
405+ compactionCount : 2 ,
406+ totalTokens : 54_321 ,
407+ totalTokensFresh : true ,
408+ } ) ,
409+ } ,
410+ } ) ;
411+ embeddedRunMock . compactEmbeddedPiSession . mockResolvedValueOnce ( {
412+ ok : true ,
413+ compacted : false ,
414+ result : {
415+ summary : "" ,
416+ firstKeptEntryId : "" ,
417+ tokensBefore : 54_321 ,
418+ details : {
419+ backend : "codex-app-server" ,
420+ threadId : "thread-1" ,
421+ signal : "thread/compact/start" ,
422+ pending : true ,
423+ } ,
424+ } ,
425+ } ) ;
426+
427+ const { ws } = await openClient ( ) ;
428+ await rpcReq ( ws , "sessions.subscribe" , { } ) ;
429+ const endEventPromise = onceMessage (
430+ ws ,
431+ ( message ) =>
432+ message . type === "event" &&
433+ message . event === "session.operation" &&
434+ ( message . payload as { operation ?: unknown ; phase ?: unknown } ) ?. operation === "compact" &&
435+ ( message . payload as { operation ?: unknown ; phase ?: unknown } ) ?. phase === "end" ,
436+ ) ;
437+
438+ const compacted = await rpcReq < {
439+ ok : true ;
440+ key : string ;
441+ compacted : boolean ;
442+ result ?: { details ?: unknown } ;
443+ } > ( ws , "sessions.compact" , {
444+ key : "main" ,
445+ } ) ;
446+
447+ expect ( compacted . ok ) . toBe ( true ) ;
448+ expect ( compacted . payload ?. key ) . toBe ( "agent:main:main" ) ;
449+ expect ( compacted . payload ?. compacted ) . toBe ( false ) ;
450+ expect ( compacted . payload ?. result ?. details ) . toMatchObject ( {
451+ backend : "codex-app-server" ,
452+ threadId : "thread-1" ,
453+ signal : "thread/compact/start" ,
454+ pending : true ,
455+ } ) ;
456+ const endEvent = await endEventPromise ;
457+ expect ( endEvent . payload ) . toMatchObject ( {
458+ operation : "compact" ,
459+ phase : "end" ,
460+ sessionKey : "agent:main:main" ,
461+ completed : false ,
462+ } ) ;
463+
464+ const store = JSON . parse ( await fs . readFile ( storePath , "utf-8" ) ) as Record <
465+ string ,
466+ {
467+ compactionCount ?: number ;
468+ totalTokens ?: number ;
469+ totalTokensFresh ?: boolean ;
470+ }
471+ > ;
472+ expect ( store [ "agent:main:main" ] ?. compactionCount ) . toBe ( 2 ) ;
473+ expect ( store [ "agent:main:main" ] ?. totalTokens ) . toBe ( 54_321 ) ;
474+ expect ( store [ "agent:main:main" ] ?. totalTokensFresh ) . toBe ( true ) ;
475+
476+ ws . close ( ) ;
477+ } ) ;
478+
394479test ( "sessions.patch preserves nested model ids under provider overrides" , async ( ) => {
395480 const dir = await fs . mkdtemp ( path . join ( os . tmpdir ( ) , "openclaw-gw-sessions-nested-" ) ) ;
396481 const storePath = path . join ( dir , "sessions.json" ) ;
0 commit comments