@@ -238,6 +238,35 @@ func TestParseCodexSession_FunctionCalls(t *testing.T) {
238238 assert .Equal (t , "continuing" , msgs [3 ].Content )
239239 })
240240
241+ t .Run ("duplicate pending notification preserves earliest chronology" , func (t * testing.T ) {
242+ childID := "019c9c96-6ee7-77c0-ba4c-380f844289d5"
243+ summary := "Exit code: `1`\n \n Full output:\n ```text\n Traceback...\n ```"
244+ notification := "<subagent_notification>\n " +
245+ "{\" agent_id\" :\" " + childID + "\" ,\" status\" :{\" completed\" :\" Exit code: `1`\\ n\\ nFull output:\\ n```text\\ nTraceback...\\ n```\" }}\n " +
246+ "</subagent_notification>"
247+ content := testjsonl .JoinJSONL (
248+ testjsonl .CodexSessionMetaJSON ("fc-subagent-notify-dupe-order" , "/tmp" , "user" , tsEarly ),
249+ testjsonl .CodexMsgJSON ("user" , "run a child agent" , tsEarlyS1 ),
250+ testjsonl .CodexFunctionCallWithCallIDJSON ("spawn_agent" , "call_spawn" , map [string ]any {
251+ "agent_type" : "awaiter" ,
252+ "message" : "Run the compile smoke test" ,
253+ }, tsEarlyS5 ),
254+ testjsonl .CodexFunctionCallOutputJSON ("call_spawn" , `{"agent_id":"` + childID + `","nickname":"Fennel"}` , tsLate ),
255+ testjsonl .CodexMsgJSON ("user" , notification , tsLateS5 ),
256+ testjsonl .CodexMsgJSON ("assistant" , "continuing" , "2024-01-01T10:01:06Z" ),
257+ testjsonl .CodexMsgJSON ("user" , notification , "2024-01-01T10:01:07Z" ),
258+ )
259+ sess , msgs := runCodexParserTest (t , "test.jsonl" , content , false )
260+
261+ require .NotNil (t , sess )
262+ assert .Equal (t , 4 , len (msgs ))
263+ require .Len (t , msgs [2 ].ToolResults , 1 )
264+ assert .Equal (t , "call_spawn" , msgs [2 ].ToolResults [0 ].ToolUseID )
265+ assert .Equal (t , summary , DecodeContent (msgs [2 ].ToolResults [0 ].ContentRaw ))
266+ assert .Equal (t , RoleAssistant , msgs [3 ].Role )
267+ assert .Equal (t , "continuing" , msgs [3 ].Content )
268+ })
269+
241270 t .Run ("running subagent notification does not suppress later completion" , func (t * testing.T ) {
242271 childID := "019c9c96-6ee7-77c0-ba4c-380f844289d5"
243272 running := "<subagent_notification>\n " +
@@ -824,6 +853,44 @@ func TestParseCodexSessionFrom_SubagentOutputRequiresFullParse(t *testing.T) {
824853 assert .Contains (t , err .Error (), "full parse" )
825854}
826855
856+ func TestParseCodexSessionFrom_WaitCallRequiresFullParse (t * testing.T ) {
857+ t .Parallel ()
858+
859+ childID := "019c9c96-6ee7-77c0-ba4c-380f844289d5"
860+ notification := "<subagent_notification>\n " +
861+ "{\" agent_id\" :\" " + childID + "\" ,\" status\" :{\" completed\" :\" Finished successfully\" }}\n " +
862+ "</subagent_notification>"
863+ initial := testjsonl .JoinJSONL (
864+ testjsonl .CodexSessionMetaJSON ("inc-wait" , "/tmp" , "codex_cli_rs" , tsEarly ),
865+ testjsonl .CodexMsgJSON ("user" , "run child" , tsEarlyS1 ),
866+ testjsonl .CodexFunctionCallWithCallIDJSON ("spawn_agent" , "call_spawn" , map [string ]any {
867+ "agent_type" : "awaiter" ,
868+ "message" : "run it" ,
869+ }, tsEarlyS5 ),
870+ testjsonl .CodexFunctionCallOutputJSON ("call_spawn" , `{"agent_id":"` + childID + `","nickname":"Fennel"}` , tsLate ),
871+ testjsonl .CodexMsgJSON ("user" , notification , tsLateS5 ),
872+ )
873+ path := createTestFile (t , "codex-wait-inc.jsonl" , initial )
874+
875+ info , err := os .Stat (path )
876+ require .NoError (t , err )
877+ offset := info .Size ()
878+
879+ f , err := os .OpenFile (path , os .O_APPEND | os .O_WRONLY , 0o644 )
880+ require .NoError (t , err )
881+ _ , err = f .WriteString (testjsonl .JoinJSONL (
882+ testjsonl .CodexFunctionCallWithCallIDJSON ("wait" , "call_wait" , map [string ]any {
883+ "ids" : []string {childID },
884+ }, "2024-01-01T10:01:06Z" ),
885+ ))
886+ require .NoError (t , err )
887+ require .NoError (t , f .Close ())
888+
889+ _ , _ , _ , err = ParseCodexSessionFrom (path , offset , 4 , false )
890+ require .Error (t , err )
891+ assert .Contains (t , err .Error (), "full parse" )
892+ }
893+
827894func TestParseCodexSessionFrom_SystemMessageDoesNotRequireFullParse (t * testing.T ) {
828895 t .Parallel ()
829896
0 commit comments