@@ -727,6 +727,85 @@ function createRuntime() {
727727 listArtifacts : vi . fn ( ( ) => [ ] ) ,
728728 ingest : vi . fn ( ( ) => ( { artifacts : [ ] } ) ) ,
729729 } as any ,
730+ macosVmService : {
731+ getStatus : vi . fn ( async ( { laneId } : { laneId ?: string | null } = { } ) => ( {
732+ supported : true ,
733+ activeProvider : { kind : "lume" , available : true } ,
734+ laneVm : laneId ? { laneId, name : "ade-lane-1" , state : "running" } : null ,
735+ vms : [ ] ,
736+ } ) ) ,
737+ start : vi . fn ( async ( { laneId } : { laneId : string } ) => ( { laneId, name : "ade-lane-1" , state : "running" } ) ) ,
738+ getAgentGuide : vi . fn ( async ( { laneId } : { laneId : string } ) => ( {
739+ laneId,
740+ vmName : "ade-lane-1" ,
741+ text : "Use macOS VM" ,
742+ target : { kind : "macos_vm_target" , id : "target-1" , laneId, vmName : "ade-lane-1" } ,
743+ } ) ) ,
744+ focusWindow : vi . fn ( async ( { laneId } : { laneId : string } ) => ( {
745+ laneId,
746+ vmName : "ade-lane-1" ,
747+ windowTitleQuery : "ade-lane-1" ,
748+ processName : "Lume" ,
749+ windowTitle : "ade-lane-1" ,
750+ frame : { x : 10 , y : 20 , width : 800 , height : 600 } ,
751+ focusedAt : new Date ( ) . toISOString ( ) ,
752+ } ) ) ,
753+ captureScreenshot : vi . fn ( async ( { laneId } : { laneId : string } ) => {
754+ const screenshotPath = path . join ( projectRoot , ".ade" , "artifacts" , "macos-vms" , laneId , "shot.png" ) ;
755+ fs . mkdirSync ( path . dirname ( screenshotPath ) , { recursive : true } ) ;
756+ fs . writeFileSync ( screenshotPath , "png" ) ;
757+ return {
758+ ok : true ,
759+ laneId,
760+ vmName : "ade-lane-1" ,
761+ path : screenshotPath ,
762+ capturedAt : new Date ( ) . toISOString ( ) ,
763+ captureMode : "window-region" ,
764+ window : {
765+ laneId,
766+ vmName : "ade-lane-1" ,
767+ windowTitleQuery : "ade-lane-1" ,
768+ processName : "Lume" ,
769+ windowTitle : "ade-lane-1" ,
770+ frame : { x : 10 , y : 20 , width : 800 , height : 600 } ,
771+ focusedAt : new Date ( ) . toISOString ( ) ,
772+ } ,
773+ } ;
774+ } ) ,
775+ click : vi . fn ( async ( { laneId, x, y } : { laneId : string ; x : number ; y : number } ) => ( {
776+ ok : true ,
777+ laneId,
778+ x,
779+ y,
780+ } ) ) ,
781+ selectPoint : vi . fn ( async ( { laneId, x, y } : { laneId : string ; x : number ; y : number } ) => {
782+ const screenshotPath = path . join ( projectRoot , ".ade" , "artifacts" , "macos-vms" , laneId , "selection.png" ) ;
783+ fs . mkdirSync ( path . dirname ( screenshotPath ) , { recursive : true } ) ;
784+ fs . writeFileSync ( screenshotPath , "png" ) ;
785+ const screenshot = {
786+ ok : true ,
787+ laneId,
788+ vmName : "ade-lane-1" ,
789+ path : screenshotPath ,
790+ capturedAt : new Date ( ) . toISOString ( ) ,
791+ captureMode : "window-region" ,
792+ window : { laneId, vmName : "ade-lane-1" } ,
793+ } ;
794+ return {
795+ item : {
796+ kind : "macos_vm_target" ,
797+ id : "target-point-1" ,
798+ laneId,
799+ laneName : "Lane 1" ,
800+ vmName : "ade-lane-1" ,
801+ metadata : { selectedPoint : { x, y } } ,
802+ } ,
803+ source : "coordinate-fallback" ,
804+ screenshot,
805+ } ;
806+ } ) ,
807+ typeText : vi . fn ( async ( { laneId, text } : { laneId : string ; text : string } ) => ( { ok : true , laneId, textLength : text . length } ) ) ,
808+ } as any ,
730809 orchestratorService : {
731810 listRuns : vi . fn ( ( ) => [ ] ) ,
732811 pauseRun : vi . fn ( ( { runId } : any ) => ( { id : runId , status : "paused" } ) ) ,
@@ -1042,6 +1121,8 @@ describe("adeRpcServer", () => {
10421121 expect ( names ) . not . toContain ( "interact_gui" ) ;
10431122 expect ( names ) . not . toContain ( "screenshot_environment" ) ;
10441123 expect ( names ) . not . toContain ( "record_environment" ) ;
1124+ expect ( names ) . not . toContain ( "macos_vm_screenshot" ) ;
1125+ expect ( names ) . not . toContain ( "macos_vm_click" ) ;
10451126
10461127 const denied = await callTool ( handler , "screenshot_environment" , { } ) ;
10471128 expect ( denied . isError ) . toBe ( true ) ;
@@ -1182,6 +1263,149 @@ describe("adeRpcServer", () => {
11821263 ) ;
11831264 } ) ;
11841265
1266+ it ( "exposes lane-tied macOS VM computer-use tools to agent callers" , async ( ) => {
1267+ const fixture = createRuntime ( ) ;
1268+ const handler = createAdeRpcRequestHandler ( { runtime : fixture . runtime , serverVersion : "test" } ) ;
1269+
1270+ await initialize ( handler , {
1271+ callerId : "worker-1" ,
1272+ role : "agent" ,
1273+ missionId : "mission-1" ,
1274+ runId : "run-1" ,
1275+ stepId : "step-1" ,
1276+ attemptId : "attempt-1" ,
1277+ } ) ;
1278+
1279+ const result = ( await handler ( { jsonrpc : "2.0" , id : 3 , method : "ade/actions/list" } ) ) as any ;
1280+ const names = ( result . actions ?? [ ] ) . map ( ( tool : any ) => tool . name ) ;
1281+
1282+ expect ( names ) . toEqual (
1283+ expect . arrayContaining ( [
1284+ "macos_vm_status" ,
1285+ "macos_vm_start" ,
1286+ "macos_vm_guide" ,
1287+ "macos_vm_focus" ,
1288+ "macos_vm_screenshot" ,
1289+ "macos_vm_select" ,
1290+ "macos_vm_click" ,
1291+ "macos_vm_type" ,
1292+ ] ) ,
1293+ ) ;
1294+ } ) ;
1295+
1296+ it ( "routes macOS VM computer-use tools and ingests screenshots as proof artifacts" , async ( ) => {
1297+ const fixture = createRuntime ( ) ;
1298+ const handler = createAdeRpcRequestHandler ( { runtime : fixture . runtime , serverVersion : "test" } ) ;
1299+ await initialize ( handler , { callerId : "agent-1" , role : "agent" , chatSessionId : "chat-session-1" } ) ;
1300+
1301+ const screenshot = await callTool ( handler , "macos_vm_screenshot" , {
1302+ laneId : "lane-1" ,
1303+ name : "VM proof" ,
1304+ } ) ;
1305+ expect ( screenshot ?. isError ) . toBeUndefined ( ) ;
1306+ expect ( fixture . runtime . macosVmService . captureScreenshot ) . toHaveBeenCalledWith ( {
1307+ laneId : "lane-1" ,
1308+ windowTitleQuery : null ,
1309+ } ) ;
1310+ expect ( fixture . runtime . computerUseArtifactBrokerService . ingest ) . toHaveBeenCalledWith (
1311+ expect . objectContaining ( {
1312+ backend : { name : "macos-vm" , toolName : "macos_vm_screenshot" } ,
1313+ owners : expect . arrayContaining ( [
1314+ expect . objectContaining ( { kind : "lane" , id : "lane-1" } ) ,
1315+ expect . objectContaining ( { kind : "chat_session" , id : "chat-session-1" } ) ,
1316+ ] ) ,
1317+ } ) ,
1318+ ) ;
1319+
1320+ const selected = await callTool ( handler , "macos_vm_select" , {
1321+ laneId : "lane-1" ,
1322+ x : 120 ,
1323+ y : 420 ,
1324+ } ) ;
1325+ expect ( selected ?. isError ) . toBeUndefined ( ) ;
1326+ expect ( fixture . runtime . macosVmService . selectPoint ) . toHaveBeenCalledWith ( {
1327+ laneId : "lane-1" ,
1328+ x : 120 ,
1329+ y : 420 ,
1330+ coordinateSpace : undefined ,
1331+ windowTitleQuery : null ,
1332+ } ) ;
1333+ expect ( fixture . runtime . computerUseArtifactBrokerService . ingest ) . toHaveBeenCalledWith (
1334+ expect . objectContaining ( {
1335+ backend : { name : "macos-vm" , toolName : "macos_vm_select" } ,
1336+ } ) ,
1337+ ) ;
1338+
1339+ const clicked = await callTool ( handler , "macos_vm_click" , { laneId : "lane-1" , x : 12 , y : 34 } ) ;
1340+ expect ( clicked ?. isError ) . toBeUndefined ( ) ;
1341+ expect ( fixture . runtime . macosVmService . click ) . toHaveBeenCalledWith ( {
1342+ laneId : "lane-1" ,
1343+ x : 12 ,
1344+ y : 34 ,
1345+ coordinateSpace : undefined ,
1346+ windowTitleQuery : null ,
1347+ } ) ;
1348+
1349+ const typed = await callTool ( handler , "macos_vm_type" , { laneId : "lane-1" , text : "hello" } ) ;
1350+ expect ( typed ?. isError ) . toBeUndefined ( ) ;
1351+ expect ( fixture . runtime . macosVmService . typeText ) . toHaveBeenCalledWith ( {
1352+ laneId : "lane-1" ,
1353+ text : "hello" ,
1354+ windowTitleQuery : null ,
1355+ } ) ;
1356+ } ) ;
1357+
1358+ it ( "routes standard computer-use tools to a lane-tied macOS VM target" , async ( ) => {
1359+ const fixture = createRuntime ( ) ;
1360+ const handler = createAdeRpcRequestHandler ( { runtime : fixture . runtime , serverVersion : "test" } ) ;
1361+ await initialize ( handler , { callerId : "agent-1" , role : "agent" , chatSessionId : "chat-session-1" } ) ;
1362+
1363+ const screenshot = await callTool ( handler , "screenshot_environment" , {
1364+ target : "macos_vm" ,
1365+ laneId : "lane-1" ,
1366+ name : "standard VM proof" ,
1367+ } ) ;
1368+ expect ( screenshot ?. isError ) . toBeUndefined ( ) ;
1369+ expect ( fixture . runtime . macosVmService . captureScreenshot ) . toHaveBeenCalledWith ( {
1370+ laneId : "lane-1" ,
1371+ windowTitleQuery : null ,
1372+ } ) ;
1373+ expect ( fixture . runtime . computerUseArtifactBrokerService . ingest ) . toHaveBeenCalledWith (
1374+ expect . objectContaining ( {
1375+ backend : { name : "macos-vm" , toolName : "screenshot_environment" } ,
1376+ } ) ,
1377+ ) ;
1378+
1379+ const clicked = await callTool ( handler , "interact_gui" , {
1380+ target : "macos_vm" ,
1381+ laneId : "lane-1" ,
1382+ action : "click" ,
1383+ x : 30 ,
1384+ y : 40 ,
1385+ } ) ;
1386+ expect ( clicked ?. isError ) . toBeUndefined ( ) ;
1387+ expect ( fixture . runtime . macosVmService . click ) . toHaveBeenCalledWith ( {
1388+ laneId : "lane-1" ,
1389+ x : 30 ,
1390+ y : 40 ,
1391+ coordinateSpace : undefined ,
1392+ windowTitleQuery : null ,
1393+ } ) ;
1394+
1395+ const typed = await callTool ( handler , "interact_gui" , {
1396+ target : "macos_vm" ,
1397+ laneId : "lane-1" ,
1398+ action : "type" ,
1399+ text : "hello" ,
1400+ } ) ;
1401+ expect ( typed ?. isError ) . toBeUndefined ( ) ;
1402+ expect ( fixture . runtime . macosVmService . typeText ) . toHaveBeenCalledWith ( {
1403+ laneId : "lane-1" ,
1404+ text : "hello" ,
1405+ windowTitleQuery : null ,
1406+ } ) ;
1407+ } ) ;
1408+
11851409 it ( "hides ADE spawn and mission-worker tools from standalone chat callers" , async ( ) => {
11861410 await withEnv ( { ADE_DEFAULT_ROLE : "agent" , ADE_CHAT_SESSION_ID : "chat-1" } , async ( ) => {
11871411 const { runtime } = createRuntime ( ) ;
0 commit comments