@@ -1253,6 +1253,174 @@ describe("CodexAppServerEventProjector", () => {
12531253 expect ( toolResultContentItem . content ) . toBe ( "ok" ) ;
12541254 } ) ;
12551255
1256+ it ( "synthesizes native tool progress from turn completion snapshots" , async ( ) => {
1257+ const onAgentEvent = vi . fn ( ) ;
1258+ const onToolResult = vi . fn ( ) ;
1259+ const projector = await createProjector ( {
1260+ ...( await createParams ( ) ) ,
1261+ verboseLevel : "on" ,
1262+ onAgentEvent,
1263+ onToolResult,
1264+ } ) ;
1265+
1266+ await projector . handleNotification (
1267+ turnCompleted ( [
1268+ {
1269+ type : "commandExecution" ,
1270+ id : "cmd-snapshot" ,
1271+ command : "pnpm test extensions/codex" ,
1272+ cwd : "/workspace" ,
1273+ processId : null ,
1274+ source : "agent" ,
1275+ status : "completed" ,
1276+ commandActions : [ ] ,
1277+ aggregatedOutput : "ok" ,
1278+ exitCode : 0 ,
1279+ durationMs : 42 ,
1280+ } ,
1281+ ] ) ,
1282+ ) ;
1283+
1284+ const itemStart = findAgentEvent ( onAgentEvent , {
1285+ stream : "item" ,
1286+ phase : "start" ,
1287+ itemId : "cmd-snapshot" ,
1288+ } ) . data ;
1289+ expect ( itemStart . kind ) . toBe ( "command" ) ;
1290+ expect ( itemStart . name ) . toBe ( "bash" ) ;
1291+ expect ( itemStart . suppressChannelProgress ) . toBe ( true ) ;
1292+ const toolStart = findAgentEvent ( onAgentEvent , {
1293+ stream : "tool" ,
1294+ phase : "start" ,
1295+ itemId : "cmd-snapshot" ,
1296+ name : "bash" ,
1297+ } ) . data ;
1298+ expect ( toolStart . args ) . toEqual ( { command : "pnpm test extensions/codex" , cwd : "/workspace" } ) ;
1299+ const toolResult = findAgentEvent ( onAgentEvent , {
1300+ stream : "tool" ,
1301+ phase : "result" ,
1302+ itemId : "cmd-snapshot" ,
1303+ name : "bash" ,
1304+ } ) . data ;
1305+ expect ( toolResult . status ) . toBe ( "completed" ) ;
1306+ expect ( toolResult . isError ) . toBe ( false ) ;
1307+ expect ( onToolResult ) . toHaveBeenCalledWith ( {
1308+ text : "🛠️ `run tests (workspace)`" ,
1309+ } ) ;
1310+ } ) ;
1311+
1312+ it ( "does not duplicate native tool starts when the snapshot completes a started item" , async ( ) => {
1313+ const onAgentEvent = vi . fn ( ) ;
1314+ const projector = await createProjector ( { ...( await createParams ( ) ) , onAgentEvent } ) ;
1315+ const commandItem = {
1316+ type : "commandExecution" ,
1317+ id : "cmd-started" ,
1318+ command : "pnpm test extensions/codex" ,
1319+ cwd : "/workspace" ,
1320+ processId : null ,
1321+ source : "agent" ,
1322+ status : "completed" ,
1323+ commandActions : [ ] ,
1324+ aggregatedOutput : "ok" ,
1325+ exitCode : 0 ,
1326+ durationMs : 42 ,
1327+ } ;
1328+
1329+ await projector . handleNotification (
1330+ forCurrentTurn ( "item/started" , {
1331+ item : { ...commandItem , status : "inProgress" , aggregatedOutput : null , exitCode : null } ,
1332+ } ) ,
1333+ ) ;
1334+ await projector . handleNotification ( turnCompleted ( [ commandItem ] ) ) ;
1335+
1336+ const toolEvents = onAgentEvent . mock . calls
1337+ . map ( ( call ) => requireRecord ( call [ 0 ] , "agent event" ) )
1338+ . filter ( ( event ) => event . stream === "tool" )
1339+ . map ( ( event ) => requireRecord ( event . data , "agent event data" ) ) ;
1340+ expect (
1341+ toolEvents . filter ( ( event ) => event . phase === "start" && event . itemId === "cmd-started" ) ,
1342+ ) . toHaveLength ( 1 ) ;
1343+ expect (
1344+ toolEvents . filter ( ( event ) => event . phase === "result" && event . itemId === "cmd-started" ) ,
1345+ ) . toHaveLength ( 1 ) ;
1346+ } ) ;
1347+
1348+ it ( "does not synthesize completed progress for running turn completion snapshots" , async ( ) => {
1349+ const onAgentEvent = vi . fn ( ) ;
1350+ const projector = await createProjector ( { ...( await createParams ( ) ) , onAgentEvent } ) ;
1351+
1352+ await projector . handleNotification (
1353+ turnCompleted ( [
1354+ {
1355+ type : "commandExecution" ,
1356+ id : "cmd-running-snapshot" ,
1357+ command : "pnpm test extensions/codex" ,
1358+ cwd : "/workspace" ,
1359+ processId : null ,
1360+ source : "agent" ,
1361+ status : "inProgress" ,
1362+ commandActions : [ ] ,
1363+ aggregatedOutput : null ,
1364+ exitCode : null ,
1365+ durationMs : null ,
1366+ } ,
1367+ ] ) ,
1368+ ) ;
1369+
1370+ const toolEvents = onAgentEvent . mock . calls
1371+ . map ( ( call ) => requireRecord ( call [ 0 ] , "agent event" ) )
1372+ . filter ( ( event ) => event . stream === "tool" )
1373+ . map ( ( event ) => requireRecord ( event . data , "agent event data" ) ) ;
1374+ expect ( toolEvents ) . toEqual ( [ ] ) ;
1375+ } ) ;
1376+
1377+ it ( "does not synthesize progress for stale prior-turn snapshot items" , async ( ) => {
1378+ const onAgentEvent = vi . fn ( ) ;
1379+ const projector = await createProjector ( { ...( await createParams ( ) ) , onAgentEvent } ) ;
1380+
1381+ await projector . handleNotification (
1382+ turnCompleted ( [
1383+ {
1384+ type : "commandExecution" ,
1385+ id : "cmd-prior-turn" ,
1386+ turnId : "turn-old" ,
1387+ command : "pnpm test extensions/codex" ,
1388+ cwd : "/workspace" ,
1389+ processId : null ,
1390+ source : "agent" ,
1391+ status : "completed" ,
1392+ commandActions : [ ] ,
1393+ aggregatedOutput : "ok" ,
1394+ exitCode : 0 ,
1395+ durationMs : 42 ,
1396+ } ,
1397+ {
1398+ type : "commandExecution" ,
1399+ id : "cmd-current-turn" ,
1400+ turnId : TURN_ID ,
1401+ command : "pnpm test extensions/codex" ,
1402+ cwd : "/workspace" ,
1403+ processId : null ,
1404+ source : "agent" ,
1405+ status : "completed" ,
1406+ commandActions : [ ] ,
1407+ aggregatedOutput : "ok" ,
1408+ exitCode : 0 ,
1409+ durationMs : 42 ,
1410+ } ,
1411+ ] ) ,
1412+ ) ;
1413+
1414+ const toolEvents = onAgentEvent . mock . calls
1415+ . map ( ( call ) => requireRecord ( call [ 0 ] , "agent event" ) )
1416+ . filter ( ( event ) => event . stream === "tool" )
1417+ . map ( ( event ) => requireRecord ( event . data , "agent event data" ) ) ;
1418+ expect ( toolEvents . map ( ( event ) => event . itemId ) ) . toEqual ( [
1419+ "cmd-current-turn" ,
1420+ "cmd-current-turn" ,
1421+ ] ) ;
1422+ } ) ;
1423+
12561424 it ( "orders declined native tool diagnostics after their start event" , async ( ) => {
12571425 const projector = await createProjector ( ) ;
12581426 const diagnosticEvents : DiagnosticEventPayload [ ] = [ ] ;
0 commit comments