@@ -693,6 +693,89 @@ describe("runCodexAppServerAttempt", () => {
693693 expect ( heartbeat ?. deferLoading ) . toBe ( true ) ;
694694 } ) ;
695695
696+ it ( "keeps searchable Codex dynamic tools canonical in mirrored transcript snapshots" , async ( ) => {
697+ __testing . setOpenClawCodingToolsFactoryForTests ( ( ) => [
698+ createRuntimeDynamicTool ( "wiki_status" ) ,
699+ ] ) ;
700+ const harness = createStartedThreadHarness ( ) ;
701+ const params = createParams (
702+ path . join ( tempDir , "session.jsonl" ) ,
703+ path . join ( tempDir , "workspace" ) ,
704+ ) ;
705+ params . disableTools = false ;
706+ params . runtimePlan = createCodexRuntimePlanFixture ( ) ;
707+ params . toolsAllow = [ "wiki_status" ] ;
708+
709+ const run = runCodexAppServerAttempt ( params , {
710+ pluginConfig : {
711+ codexDynamicToolsLoading : "searchable" ,
712+ appServer : { mode : "yolo" } ,
713+ } ,
714+ } ) ;
715+ await harness . waitForMethod ( "turn/start" , 120_000 ) ;
716+
717+ const toolResult = ( await harness . handleServerRequest ( {
718+ id : "request-tool-wiki-status" ,
719+ method : "item/tool/call" ,
720+ params : {
721+ threadId : "thread-1" ,
722+ turnId : "turn-1" ,
723+ callId : "call-wiki-status-1" ,
724+ namespace : CODEX_OPENCLAW_DYNAMIC_TOOL_NAMESPACE ,
725+ tool : "wiki_status" ,
726+ arguments : { topic : "README.md" } ,
727+ } ,
728+ } ) ) as {
729+ contentItems ?: Array < { text ?: string ; type ?: string } > ;
730+ success ?: boolean ;
731+ } ;
732+ expect ( toolResult ) . toEqual ( {
733+ success : true ,
734+ contentItems : [ { type : "inputText" , text : "wiki_status done" } ] ,
735+ } ) ;
736+
737+ await harness . completeTurn ( { threadId : "thread-1" , turnId : "turn-1" } ) ;
738+ const result = await run ;
739+
740+ expect ( result . messagesSnapshot . map ( ( message ) => message . role ) ) . toEqual ( [
741+ "user" ,
742+ "assistant" ,
743+ "toolResult" ,
744+ ] ) ;
745+ expect ( result . messagesSnapshot [ 1 ] ) . toMatchObject ( {
746+ role : "assistant" ,
747+ content : [
748+ {
749+ type : "toolCall" ,
750+ id : "call-wiki-status-1" ,
751+ name : "wiki_status" ,
752+ arguments : { topic : "README.md" } ,
753+ input : { topic : "README.md" } ,
754+ } ,
755+ ] ,
756+ } ) ;
757+ expect ( result . messagesSnapshot [ 2 ] ) . toMatchObject ( {
758+ role : "toolResult" ,
759+ toolCallId : "call-wiki-status-1" ,
760+ toolName : "wiki_status" ,
761+ isError : false ,
762+ content : [
763+ expect . objectContaining ( {
764+ type : "toolResult" ,
765+ id : "call-wiki-status-1" ,
766+ name : "wiki_status" ,
767+ toolName : "wiki_status" ,
768+ toolCallId : "call-wiki-status-1" ,
769+ toolUseId : "call-wiki-status-1" ,
770+ tool_use_id : "call-wiki-status-1" ,
771+ content : "wiki_status done" ,
772+ } ) ,
773+ ] ,
774+ } ) ;
775+ expect ( JSON . stringify ( result . messagesSnapshot ) ) . not . toContain ( "tool_search" ) ;
776+ expect ( JSON . stringify ( result . messagesSnapshot ) ) . not . toContain ( "function_call_output" ) ;
777+ } ) ;
778+
696779 it ( "passes the live run session key to Codex dynamic tools when sandbox policy uses another key" , ( ) => {
697780 const workspaceDir = path . join ( tempDir , "workspace" ) ;
698781 const params = createParams ( path . join ( tempDir , "session.jsonl" ) , workspaceDir ) ;
0 commit comments