@@ -24,7 +24,11 @@ import { act, createElement } from 'react';
2424import { createRoot , type Root } from 'react-dom/client' ;
2525import type { AgentGraphClientSnapshot } from '@maka/runtime/stream-graph-read-model' ;
2626import type { AgentGraphEpochSummary } from '@maka/runtime-host/protocol' ;
27- import { AgentGraphPanel } from '../../renderer/agent-graph-panel.js' ;
27+ import {
28+ AgentGraphPanel ,
29+ AgentGraphServicesProvider ,
30+ type AgentGraphServices ,
31+ } from '../../renderer/features/agent-graph/testing.js' ;
2832
2933type GraphListener = ( ) => void ;
3034
@@ -112,6 +116,7 @@ function installGraphRenderer(
112116 renderSession ( sessionId : string ) : Promise < void > ;
113117 holdNextEpochList ( sessionId : string ) : DeferredRead ;
114118 holdNextSnapshot ( graphId : string ) : DeferredRead ;
119+ holdNextInspection ( operatorId : string ) : DeferredRead ;
115120 holdNextStop ( sessionId : string ) : DeferredRead ;
116121 setCurrentWithoutNotification ( next : AgentGraphClientSnapshot ) : void ;
117122 notify ( ) : void ;
@@ -161,6 +166,7 @@ function installGraphRenderer(
161166 const listeners = new Set < GraphListener > ( ) ;
162167 const epochListGates = new Map < string , DeferredReadGate > ( ) ;
163168 const snapshotGates = new Map < string , DeferredReadGate > ( ) ;
169+ const inspectionGates = new Map < string , DeferredReadGate > ( ) ;
164170 const stopGates = new Map < string , DeferredReadGate > ( ) ;
165171 const stopCalls : Array < { sessionId : string ; expectedGraphId : string } > = [ ] ;
166172 let fullEpochReads = 0 ;
@@ -184,7 +190,7 @@ function installGraphRenderer(
184190 truncated : false ,
185191 } ;
186192 } ;
187- ( window as unknown as { maka : unknown } ) . maka = {
193+ const services : AgentGraphServices = {
188194 graphs : {
189195 listEpochs : async ( sessionId : string ) => {
190196 fullEpochReads += 1 ;
@@ -216,6 +222,12 @@ function installGraphRenderer(
216222 } ,
217223 inspectOperator : async ( sessionId : string , operatorId : string , graphId ?: string ) => {
218224 inspectionReads . set ( operatorId , ( inspectionReads . get ( operatorId ) ?? 0 ) + 1 ) ;
225+ const gate = inspectionGates . get ( operatorId ) ;
226+ if ( gate ) {
227+ inspectionGates . delete ( operatorId ) ;
228+ gate . markStarted ( ) ;
229+ await gate . waitForRelease ;
230+ }
219231 const snapshot = graphId ? snapshots . get ( graphId ) : undefined ;
220232 const operator = snapshot ?. operators . find ( ( candidate ) => candidate . operatorId === operatorId ) ;
221233 if ( ! snapshot || snapshot . rootSessionId !== sessionId || ! operator ) {
@@ -275,6 +287,7 @@ function installGraphRenderer(
275287 } ,
276288 } ,
277289 } ;
290+ ( window as unknown as { maka : unknown } ) . maka = services ;
278291
279292 const container = document . querySelector ( '#root' ) ;
280293 assert . ok ( container ) ;
@@ -312,12 +325,16 @@ function installGraphRenderer(
312325 async renderSession ( sessionId ) {
313326 await act ( async ( ) => {
314327 root . render (
315- createElement ( AgentGraphPanel , {
316- rootSessionId : sessionId ,
317- enabled : true ,
318- locale : 'en' ,
319- onOpenSession : ( ) => undefined ,
320- } ) ,
328+ createElement (
329+ AgentGraphServicesProvider ,
330+ { services } ,
331+ createElement ( AgentGraphPanel , {
332+ rootSessionId : sessionId ,
333+ enabled : true ,
334+ locale : 'en' ,
335+ onOpenSession : ( ) => undefined ,
336+ } ) ,
337+ ) ,
321338 ) ;
322339 await Promise . resolve ( ) ;
323340 } ) ;
@@ -332,6 +349,11 @@ function installGraphRenderer(
332349 snapshotGates . set ( graphId , gate ) ;
333350 return gate ;
334351 } ,
352+ holdNextInspection ( operatorId ) {
353+ const gate = deferredReadGate ( ) ;
354+ inspectionGates . set ( operatorId , gate ) ;
355+ return gate ;
356+ } ,
335357 holdNextStop ( sessionId ) {
336358 const gate = deferredReadGate ( ) ;
337359 stopGates . set ( sessionId , gate ) ;
@@ -347,12 +369,16 @@ async function renderPanel(
347369 const harness = installGraphRenderer ( initial ) ;
348370 await act ( async ( ) => {
349371 harness . root . render (
350- createElement ( AgentGraphPanel , {
351- rootSessionId : 'session-1' ,
352- enabled : true ,
353- locale : 'en' ,
354- onOpenSession : ( ) => undefined ,
355- } ) ,
372+ createElement (
373+ AgentGraphServicesProvider ,
374+ { services : ( window as unknown as { maka : AgentGraphServices } ) . maka } ,
375+ createElement ( AgentGraphPanel , {
376+ rootSessionId : 'session-1' ,
377+ enabled : true ,
378+ locale : 'en' ,
379+ onOpenSession : ( ) => undefined ,
380+ } ) ,
381+ ) ,
356382 ) ;
357383 await Promise . resolve ( ) ;
358384 } ) ;
@@ -536,6 +562,47 @@ describe('AgentGraphPanel dismiss', () => {
536562 await act ( async ( ) => harness . root . unmount ( ) ) ;
537563 } ) ;
538564
565+ it ( 'keeps inspected details visible while a live refresh is pending' , async ( ) => {
566+ const graph = snapshot ( {
567+ graphId : 'graph-inspection-refresh' ,
568+ status : 'active' ,
569+ operators : [ graphOperator ( 'a' , [ 'work-a' ] ) ] ,
570+ work : [ graphWork ( 'work-a' , 'a' , 'Initial inspection detail' ) ] ,
571+ } ) ;
572+ const harness = await renderPanel ( graph ) ;
573+ const node = harness . container . querySelector ( '.maka-agent-graph-node' ) ;
574+ assert . ok ( node ) ;
575+ await act ( async ( ) => {
576+ ( node as HTMLElement ) . click ( ) ;
577+ await Promise . resolve ( ) ;
578+ } ) ;
579+ const details = harness . container . querySelector (
580+ '[aria-label="Operator details: agent-a"]' ,
581+ ) ;
582+ assert . ok ( details ) ;
583+ assert . match ( details . textContent ?? '' , / I n i t i a l i n s p e c t i o n d e t a i l / ) ;
584+
585+ const inspection = harness . holdNextInspection ( 'a' ) ;
586+ await harness . setSnapshot ( {
587+ ...graph ,
588+ snapshotVersion : '2' ,
589+ work : [ graphWork ( 'work-a' , 'a' , 'Refreshed inspection detail' ) ] ,
590+ } ) ;
591+ await inspection . started ;
592+
593+ assert . equal ( details . getAttribute ( 'aria-busy' ) , 'true' ) ;
594+ assert . match ( details . textContent ?? '' , / I n i t i a l i n s p e c t i o n d e t a i l / ) ;
595+ assert . doesNotMatch ( details . textContent ?? '' , / R e f r e s h e d i n s p e c t i o n d e t a i l / ) ;
596+
597+ await act ( async ( ) => {
598+ inspection . release ( ) ;
599+ await Promise . resolve ( ) ;
600+ } ) ;
601+ assert . equal ( details . getAttribute ( 'aria-busy' ) , 'false' ) ;
602+ assert . match ( details . textContent ?? '' , / R e f r e s h e d i n s p e c t i o n d e t a i l / ) ;
603+ await act ( async ( ) => harness . root . unmount ( ) ) ;
604+ } ) ;
605+
539606 it ( 'keeps keyboard order visual and reveals a retained selection' , async ( ) => {
540607 const harness = await renderPanel (
541608 snapshot ( {
@@ -670,17 +737,7 @@ describe('AgentGraphPanel dismiss', () => {
670737 const current = snapshot ( { graphId : 'graph-2' , status : 'active' } ) ;
671738 const previous = snapshot ( { graphId : 'graph-1' , status : 'completed' } ) ;
672739 const harness = installGraphRenderer ( current , [ previous ] ) ;
673- await act ( async ( ) => {
674- harness . root . render (
675- createElement ( AgentGraphPanel , {
676- rootSessionId : 'session-1' ,
677- enabled : true ,
678- locale : 'en' ,
679- onOpenSession : ( ) => undefined ,
680- } ) ,
681- ) ;
682- await Promise . resolve ( ) ;
683- } ) ;
740+ await harness . renderSession ( 'session-1' ) ;
684741
685742 const selector = harness . container . querySelector ( '[role="combobox"]' ) ;
686743 assert . ok ( selector ) ;
@@ -869,17 +926,7 @@ describe('AgentGraphPanel dismiss', () => {
869926 const current = snapshot ( { graphId : 'graph-2' , status : 'active' } ) ;
870927 const previous = snapshot ( { graphId : 'graph-1' , status : 'completed' } ) ;
871928 const harness = installGraphRenderer ( current , [ previous ] ) ;
872- await act ( async ( ) => {
873- harness . root . render (
874- createElement ( AgentGraphPanel , {
875- rootSessionId : 'session-1' ,
876- enabled : true ,
877- locale : 'en' ,
878- onOpenSession : ( ) => undefined ,
879- } ) ,
880- ) ;
881- await Promise . resolve ( ) ;
882- } ) ;
929+ await harness . renderSession ( 'session-1' ) ;
883930
884931 const selector = harness . container . querySelector ( '[role="combobox"]' ) ;
885932 assert . ok ( selector ) ;
0 commit comments