The file C:\Users\MikeSwanson\claude-projects\guru-connect\agent\src\capture\dxgi.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
   130→                        Ok(o) => o,
   131→                        Err(_) => break,
   132→                    };
   133→
   134→                    // Check if this is the display we want
   135→                    let desc = output.GetDesc()?;
   136→
   137→                    let name = String::from_utf16_lossy(
   138→                        &desc.DeviceName[..desc.DeviceName.iter().position(|&c| c == 0).unwrap_or(desc.DeviceName.len())]
   139→                    );

                    // Check if this is the display we want
                    let desc = output.GetDesc()?;

//! DXGI Desktop Duplication screen capture
//!
//! Uses the Windows Desktop Duplication API (available on Windows 8+) for
//! high-performance, low-latency screen capture with hardware acceleration.
//!
//! Reference: RustDesk's scrap library implementation

use super::{CapturedFrame, Capturer, DirtyRect, Display};
use anyhow::{Context, Result};
use std::ptr;
use std::time::Instant;

use windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE_UNKNOWN;
use windows::Win32::Graphics::Direct3D11::{
    D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
    D3D11_CPU_ACCESS_READ, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC,
    D3D11_USAGE_STAGING, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ,
};
use windows::Win32::Graphics::Dxgi::{
    CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1, IDXGIOutput, IDXGIOutput1,
    IDXGIOutputDuplication, IDXGIResource, DXGI_ERROR_ACCESS_LOST,
    DXGI_ERROR_WAIT_TIMEOUT, DXGI_OUTDUPL_DESC, DXGI_OUTDUPL_FRAME_INFO,
    DXGI_RESOURCE_PRIORITY_MAXIMUM,
};
use windows::core::Interface;

/// DXGI Desktop Duplication capturer
pub struct DxgiCapturer {
    display: Display,
    device: ID3D11Device,
    context: ID3D11DeviceContext,
    duplication: IDXGIOutputDuplication,
    staging_texture: Option<ID3D11Texture2D>,
    width: u32,
    height: u32,
    last_frame: Option<Vec<u8>>,
}

impl DxgiCapturer {
    /// Create a new DXGI capturer for the specified display
    pub fn new(display: Display) -> Result<Self> {
        let (device, context, duplication, desc) = Self::create_duplication(&display)?;

        Ok(Self {
            display,
            device,
            context,
            duplication,
            staging_texture: None,
            width: desc.ModeDesc.Width,
            height: desc.ModeDesc.Height,
            last_frame: None,
        })
    }

    /// Create D3D device and output duplication
    fn create_duplication(
        display_info: &Display,
    ) -> Result<(ID3D11Device, ID3D11DeviceContext, IDXGIOutputDuplication, DXGI_OUTDUPL_DESC)> {
        unsafe {
            // Create DXGI factory
            let factory: IDXGIFactory1 = CreateDXGIFactory1()
                .context("Failed to create DXGI factory")?;

            // Find the adapter and output for this display
            let (adapter, output) = Self::find_adapter_output(&factory, display_info)?;

            // Create D3D11 device
            let mut device: Option<ID3D11Device> = None;
            let mut context: Option<ID3D11DeviceContext> = None;

            D3D11CreateDevice(
                &adapter,
                D3D_DRIVER_TYPE_UNKNOWN,
                None,
                Default::default(),
                None,
                D3D11_SDK_VERSION,
                Some(&mut device),
                None,
                Some(&mut context),
            )
            .context("Failed to create D3D11 device")?;

            let device = device.context("D3D11 device is None")?;
            let context = context.context("D3D11 context is None")?;

            // Get IDXGIOutput1 interface
            let output1: IDXGIOutput1 = output.cast()
                .context("Failed to get IDXGIOutput1 interface")?;

            // Create output duplication
            let duplication = output1.DuplicateOutput(&device)
                .context("Failed to create output duplication")?;

            // Get duplication description
            let desc = duplication.GetDesc();

            tracing::info!(
                "Created DXGI duplication: {}x{}, display: {}",
                desc.ModeDesc.Width,
                desc.ModeDesc.Height,
                display_info.name
            );

            Ok((device, context, duplication, desc))
        }
    }

    /// Find the adapter and output for the specified display
    fn find_adapter_output(
        factory: &IDXGIFactory1,
        display: &Display,
    ) -> Result<(IDXGIAdapter1, IDXGIOutput)> {
        unsafe {
            let mut adapter_idx = 0u32;

            loop {
                // Enumerate adapters
                let adapter: IDXGIAdapter1 = match factory.EnumAdapters1(adapter_idx) {
                    Ok(a) => a,
                    Err(_) => break,
                };

                let mut output_idx = 0u32;

                loop {
                    // Enumerate outputs for this adapter
                    let output: IDXGIOutput = match adapter.EnumOutputs(output_idx) {
                        Ok(o) => o,
                        Err(_) => break,
                    };

                    // Check if this is the display we want
                    let mut desc = Default::default();
                    output.GetDesc(&mut desc)?;

                    let name = String::from_utf16_lossy(
                        &desc.DeviceName[..desc.DeviceName.iter().position(|&c| c == 0).unwrap_or(desc.DeviceName.len())]
                    );

                    if name == display.name || desc.Monitor.0 as isize == display.handle {
                        return Ok((adapter, output));
                    }

                    output_idx += 1;
                }

                adapter_idx += 1;
            }

            // If we didn't find the specific display, use the first one
            let adapter: IDXGIAdapter1 = factory.EnumAdapters1(0)
                .context("No adapters found")?;
            let output: IDXGIOutput = adapter.EnumOutputs(0)
                .context("No outputs found")?;

            Ok((adapter, output))
        }
    }

    /// Create or get the staging texture for CPU access
    fn get_staging_texture(&mut self, src_texture: &ID3D11Texture2D) -> Result<&ID3D11Texture2D> {
        if self.staging_texture.is_none() {
            unsafe {
                let mut desc = D3D11_TEXTURE2D_DESC::default();
                src_texture.GetDesc(&mut desc);

                desc.Usage = D3D11_USAGE_STAGING;
                desc.BindFlags = Default::default();
                desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
                desc.MiscFlags = Default::default();

                let mut staging: Option<ID3D11Texture2D> = None;
                self.device.CreateTexture2D(&desc, None, Some(&mut staging))
                    .context("Failed to create staging texture")?;
                let staging = staging.context("Staging texture is None")?;

                // Set high priority
                let resource: IDXGIResource = staging.cast()?;
                resource.SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM.0)?;

                self.staging_texture = Some(staging);
            }
        }

        Ok(self.staging_texture.as_ref().unwrap())
    }

    /// Acquire the next frame from the desktop
    fn acquire_frame(&mut self, timeout_ms: u32) -> Result<Option<(ID3D11Texture2D, DXGI_OUTDUPL_FRAME_INFO)>> {
        unsafe {
            let mut frame_info = DXGI_OUTDUPL_FRAME_INFO::default();
            let mut desktop_resource: Option<IDXGIResource> = None;

            let result = self.duplication.AcquireNextFrame(
                timeout_ms,
                &mut frame_info,
                &mut desktop_resource,
            );

            match result {
                Ok(_) => {
                    let resource = desktop_resource.context("Desktop resource is None")?;

                    // Check if there's actually a new frame
                    if frame_info.LastPresentTime == 0 {
                        self.duplication.ReleaseFrame().ok();
                        return Ok(None);
                    }

                    let texture: ID3D11Texture2D = resource.cast()
                        .context("Failed to cast to ID3D11Texture2D")?;

                    Ok(Some((texture, frame_info)))
                }
                Err(e) if e.code() == DXGI_ERROR_WAIT_TIMEOUT => {
                    // No new frame available
                    Ok(None)
                }
                Err(e) if e.code() == DXGI_ERROR_ACCESS_LOST => {
                    // Desktop duplication was invalidated, need to recreate
                    tracing::warn!("Desktop duplication access lost, will need to recreate");
                    Err(anyhow::anyhow!("Access lost"))
                }
                Err(e) => {
                    Err(e).context("Failed to acquire frame")
                }
            }
        }
    }

    /// Copy frame data to CPU-accessible memory
    fn copy_frame_data(&mut self, texture: &ID3D11Texture2D) -> Result<Vec<u8>> {
        unsafe {
            // Get or create staging texture
            let staging = self.get_staging_texture(texture)?.clone();

            // Copy from GPU texture to staging texture
            self.context.CopyResource(&staging, texture);

            // Map the staging texture for CPU read
            let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
            self.context
                .Map(&staging, 0, D3D11_MAP_READ, 0, Some(&mut mapped))
                .context("Failed to map staging texture")?;

            // Copy pixel data
            let src_pitch = mapped.RowPitch as usize;
            let dst_pitch = (self.width * 4) as usize;
            let height = self.height as usize;

            let mut data = vec![0u8; dst_pitch * height];

            let src_ptr = mapped.pData as *const u8;
            for y in 0..height {
                let src_row = src_ptr.add(y * src_pitch);
                let dst_row = data.as_mut_ptr().add(y * dst_pitch);
                ptr::copy_nonoverlapping(src_row, dst_row, dst_pitch);
            }

            // Unmap
            self.context.Unmap(&staging, 0);

            Ok(data)
        }
    }

    /// Extract dirty rectangles from frame info
    fn extract_dirty_rects(&self, _frame_info: &DXGI_OUTDUPL_FRAME_INFO) -> Option<Vec<DirtyRect>> {
        // TODO: Implement dirty rectangle extraction using
        // IDXGIOutputDuplication::GetFrameDirtyRects and GetFrameMoveRects
        // For now, return None to indicate full frame update
        None
    }
}

impl Capturer for DxgiCapturer {
    fn capture(&mut self) -> Result<Option<CapturedFrame>> {
        // Try to acquire a frame with 100ms timeout
        let frame_result = self.acquire_frame(100)?;

        let (texture, frame_info) = match frame_result {
            Some((t, f)) => (t, f),
            None => return Ok(None), // No new frame
        };

        // Copy frame data to CPU memory
        let data = self.copy_frame_data(&texture)?;

        // Release the frame
        unsafe {
            self.duplication.ReleaseFrame().ok();
        }

        // Extract dirty rectangles if available
        let dirty_rects = self.extract_dirty_rects(&frame_info);

        Ok(Some(CapturedFrame {
            width: self.width,
            height: self.height,
            data,
            timestamp: Instant::now(),
            display_id: self.display.id,
            dirty_rects,
        }))
    }

    fn display(&self) -> &Display {
        &self.display
    }

    fn is_valid(&self) -> bool {
        // Could check if duplication is still valid
        true
    }
}

impl Drop for DxgiCapturer {
    fn drop(&mut self) {
        // Release any held frame
        unsafe {
            self.duplication.ReleaseFrame().ok();
        }
    }
}

-                    let mut desc = Default::default();

-                    output.GetDesc(&mut desc)?;

+                    let desc = output.GetDesc()?;


The file C:\Users\MikeSwanson\claude-projects\guru-connect\agent\src\capture\dxgi.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
   166→                src_texture.GetDesc(&mut desc);
   167→
   168→                desc.Usage = D3D11_USAGE_STAGING;
   169→                desc.BindFlags = Default::default();
   170→                desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32;
   171→                desc.MiscFlags = Default::default();
   172→
   173→                let mut staging: Option<ID3D11Texture2D> = None;
   174→                self.device.CreateTexture2D(&desc, None, Some(&mut staging))

//! DXGI Desktop Duplication screen capture
//!
//! Uses the Windows Desktop Duplication API (available on Windows 8+) for
//! high-performance, low-latency screen capture with hardware acceleration.
//!
//! Reference: RustDesk's scrap library implementation

use super::{CapturedFrame, Capturer, DirtyRect, Display};
use anyhow::{Context, Result};
use std::ptr;
use std::time::Instant;

use windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE_UNKNOWN;
use windows::Win32::Graphics::Direct3D11::{
    D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
    D3D11_CPU_ACCESS_READ, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC,
    D3D11_USAGE_STAGING, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ,
};
use windows::Win32::Graphics::Dxgi::{
    CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1, IDXGIOutput, IDXGIOutput1,
    IDXGIOutputDuplication, IDXGIResource, DXGI_ERROR_ACCESS_LOST,
    DXGI_ERROR_WAIT_TIMEOUT, DXGI_OUTDUPL_DESC, DXGI_OUTDUPL_FRAME_INFO,
    DXGI_RESOURCE_PRIORITY_MAXIMUM,
};
use windows::core::Interface;

/// DXGI Desktop Duplication capturer
pub struct DxgiCapturer {
    display: Display,
    device: ID3D11Device,
    context: ID3D11DeviceContext,
    duplication: IDXGIOutputDuplication,
    staging_texture: Option<ID3D11Texture2D>,
    width: u32,
    height: u32,
    last_frame: Option<Vec<u8>>,
}

impl DxgiCapturer {
    /// Create a new DXGI capturer for the specified display
    pub fn new(display: Display) -> Result<Self> {
        let (device, context, duplication, desc) = Self::create_duplication(&display)?;

        Ok(Self {
            display,
            device,
            context,
            duplication,
            staging_texture: None,
            width: desc.ModeDesc.Width,
            height: desc.ModeDesc.Height,
            last_frame: None,
        })
    }

    /// Create D3D device and output duplication
    fn create_duplication(
        display_info: &Display,
    ) -> Result<(ID3D11Device, ID3D11DeviceContext, IDXGIOutputDuplication, DXGI_OUTDUPL_DESC)> {
        unsafe {
            // Create DXGI factory
            let factory: IDXGIFactory1 = CreateDXGIFactory1()
                .context("Failed to create DXGI factory")?;

            // Find the adapter and output for this display
            let (adapter, output) = Self::find_adapter_output(&factory, display_info)?;

            // Create D3D11 device
            let mut device: Option<ID3D11Device> = None;
            let mut context: Option<ID3D11DeviceContext> = None;

            D3D11CreateDevice(
                &adapter,
                D3D_DRIVER_TYPE_UNKNOWN,
                None,
                Default::default(),
                None,
                D3D11_SDK_VERSION,
                Some(&mut device),
                None,
                Some(&mut context),
            )
            .context("Failed to create D3D11 device")?;

            let device = device.context("D3D11 device is None")?;
            let context = context.context("D3D11 context is None")?;

            // Get IDXGIOutput1 interface
            let output1: IDXGIOutput1 = output.cast()
                .context("Failed to get IDXGIOutput1 interface")?;

            // Create output duplication
            let duplication = output1.DuplicateOutput(&device)
                .context("Failed to create output duplication")?;

            // Get duplication description
            let desc = duplication.GetDesc();

            tracing::info!(
                "Created DXGI duplication: {}x{}, display: {}",
                desc.ModeDesc.Width,
                desc.ModeDesc.Height,
                display_info.name
            );

            Ok((device, context, duplication, desc))
        }
    }

    /// Find the adapter and output for the specified display
    fn find_adapter_output(
        factory: &IDXGIFactory1,
        display: &Display,
    ) -> Result<(IDXGIAdapter1, IDXGIOutput)> {
        unsafe {
            let mut adapter_idx = 0u32;

            loop {
                // Enumerate adapters
                let adapter: IDXGIAdapter1 = match factory.EnumAdapters1(adapter_idx) {
                    Ok(a) => a,
                    Err(_) => break,
                };

                let mut output_idx = 0u32;

                loop {
                    // Enumerate outputs for this adapter
                    let output: IDXGIOutput = match adapter.EnumOutputs(output_idx) {
                        Ok(o) => o,
                        Err(_) => break,
                    };

                    // Check if this is the display we want
                    let desc = output.GetDesc()?;

                    let name = String::from_utf16_lossy(
                        &desc.DeviceName[..desc.DeviceName.iter().position(|&c| c == 0).unwrap_or(desc.DeviceName.len())]
                    );

                    if name == display.name || desc.Monitor.0 as isize == display.handle {
                        return Ok((adapter, output));
                    }

                    output_idx += 1;
                }

                adapter_idx += 1;
            }

            // If we didn't find the specific display, use the first one
            let adapter: IDXGIAdapter1 = factory.EnumAdapters1(0)
                .context("No adapters found")?;
            let output: IDXGIOutput = adapter.EnumOutputs(0)
                .context("No outputs found")?;

            Ok((adapter, output))
        }
    }

    /// Create or get the staging texture for CPU access
    fn get_staging_texture(&mut self, src_texture: &ID3D11Texture2D) -> Result<&ID3D11Texture2D> {
        if self.staging_texture.is_none() {
            unsafe {
                let mut desc = D3D11_TEXTURE2D_DESC::default();
                src_texture.GetDesc(&mut desc);

                desc.Usage = D3D11_USAGE_STAGING;
                desc.BindFlags = Default::default();
                desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
                desc.MiscFlags = Default::default();

                let mut staging: Option<ID3D11Texture2D> = None;
                self.device.CreateTexture2D(&desc, None, Some(&mut staging))
                    .context("Failed to create staging texture")?;
                let staging = staging.context("Staging texture is None")?;

                // Set high priority
                let resource: IDXGIResource = staging.cast()?;
                resource.SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM.0)?;

                self.staging_texture = Some(staging);
            }
        }

        Ok(self.staging_texture.as_ref().unwrap())
    }

    /// Acquire the next frame from the desktop
    fn acquire_frame(&mut self, timeout_ms: u32) -> Result<Option<(ID3D11Texture2D, DXGI_OUTDUPL_FRAME_INFO)>> {
        unsafe {
            let mut frame_info = DXGI_OUTDUPL_FRAME_INFO::default();
            let mut desktop_resource: Option<IDXGIResource> = None;

            let result = self.duplication.AcquireNextFrame(
                timeout_ms,
                &mut frame_info,
                &mut desktop_resource,
            );

            match result {
                Ok(_) => {
                    let resource = desktop_resource.context("Desktop resource is None")?;

                    // Check if there's actually a new frame
                    if frame_info.LastPresentTime == 0 {
                        self.duplication.ReleaseFrame().ok();
                        return Ok(None);
                    }

                    let texture: ID3D11Texture2D = resource.cast()
                        .context("Failed to cast to ID3D11Texture2D")?;

                    Ok(Some((texture, frame_info)))
                }
                Err(e) if e.code() == DXGI_ERROR_WAIT_TIMEOUT => {
                    // No new frame available
                    Ok(None)
                }
                Err(e) if e.code() == DXGI_ERROR_ACCESS_LOST => {
                    // Desktop duplication was invalidated, need to recreate
                    tracing::warn!("Desktop duplication access lost, will need to recreate");
                    Err(anyhow::anyhow!("Access lost"))
                }
                Err(e) => {
                    Err(e).context("Failed to acquire frame")
                }
            }
        }
    }

    /// Copy frame data to CPU-accessible memory
    fn copy_frame_data(&mut self, texture: &ID3D11Texture2D) -> Result<Vec<u8>> {
        unsafe {
            // Get or create staging texture
            let staging = self.get_staging_texture(texture)?.clone();

            // Copy from GPU texture to staging texture
            self.context.CopyResource(&staging, texture);

            // Map the staging texture for CPU read
            let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
            self.context
                .Map(&staging, 0, D3D11_MAP_READ, 0, Some(&mut mapped))
                .context("Failed to map staging texture")?;

            // Copy pixel data
            let src_pitch = mapped.RowPitch as usize;
            let dst_pitch = (self.width * 4) as usize;
            let height = self.height as usize;

            let mut data = vec![0u8; dst_pitch * height];

            let src_ptr = mapped.pData as *const u8;
            for y in 0..height {
                let src_row = src_ptr.add(y * src_pitch);
                let dst_row = data.as_mut_ptr().add(y * dst_pitch);
                ptr::copy_nonoverlapping(src_row, dst_row, dst_pitch);
            }

            // Unmap
            self.context.Unmap(&staging, 0);

            Ok(data)
        }
    }

    /// Extract dirty rectangles from frame info
    fn extract_dirty_rects(&self, _frame_info: &DXGI_OUTDUPL_FRAME_INFO) -> Option<Vec<DirtyRect>> {
        // TODO: Implement dirty rectangle extraction using
        // IDXGIOutputDuplication::GetFrameDirtyRects and GetFrameMoveRects
        // For now, return None to indicate full frame update
        None
    }
}

impl Capturer for DxgiCapturer {
    fn capture(&mut self) -> Result<Option<CapturedFrame>> {
        // Try to acquire a frame with 100ms timeout
        let frame_result = self.acquire_frame(100)?;

        let (texture, frame_info) = match frame_result {
            Some((t, f)) => (t, f),
            None => return Ok(None), // No new frame
        };

        // Copy frame data to CPU memory
        let data = self.copy_frame_data(&texture)?;

        // Release the frame
        unsafe {
            self.duplication.ReleaseFrame().ok();
        }

        // Extract dirty rectangles if available
        let dirty_rects = self.extract_dirty_rects(&frame_info);

        Ok(Some(CapturedFrame {
            width: self.width,
            height: self.height,
            data,
            timestamp: Instant::now(),
            display_id: self.display.id,
            dirty_rects,
        }))
    }

    fn display(&self) -> &Display {
        &self.display
    }

    fn is_valid(&self) -> bool {
        // Could check if duplication is still valid
        true
    }
}

impl Drop for DxgiCapturer {
    fn drop(&mut self) {
        // Release any held frame
        unsafe {
            self.duplication.ReleaseFrame().ok();
        }
    }
}

-                desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;

+                desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32;


The file C:\Users\MikeSwanson\claude-projects\guru-connect\agent\src\capture\dxgi.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
   176→                let staging = staging.context("Staging texture is None")?;
   177→
   178→                // Set high priority
   179→                let resource: IDXGIResource = staging.cast()?;
   180→                resource.SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM)?;
   181→
   182→                self.staging_texture = Some(staging);
   183→            }
   184→        }

//! DXGI Desktop Duplication screen capture
//!
//! Uses the Windows Desktop Duplication API (available on Windows 8+) for
//! high-performance, low-latency screen capture with hardware acceleration.
//!
//! Reference: RustDesk's scrap library implementation

use super::{CapturedFrame, Capturer, DirtyRect, Display};
use anyhow::{Context, Result};
use std::ptr;
use std::time::Instant;

use windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE_UNKNOWN;
use windows::Win32::Graphics::Direct3D11::{
    D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
    D3D11_CPU_ACCESS_READ, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC,
    D3D11_USAGE_STAGING, D3D11_MAPPED_SUBRESOURCE, D3D11_MAP_READ,
};
use windows::Win32::Graphics::Dxgi::{
    CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1, IDXGIOutput, IDXGIOutput1,
    IDXGIOutputDuplication, IDXGIResource, DXGI_ERROR_ACCESS_LOST,
    DXGI_ERROR_WAIT_TIMEOUT, DXGI_OUTDUPL_DESC, DXGI_OUTDUPL_FRAME_INFO,
    DXGI_RESOURCE_PRIORITY_MAXIMUM,
};
use windows::core::Interface;

/// DXGI Desktop Duplication capturer
pub struct DxgiCapturer {
    display: Display,
    device: ID3D11Device,
    context: ID3D11DeviceContext,
    duplication: IDXGIOutputDuplication,
    staging_texture: Option<ID3D11Texture2D>,
    width: u32,
    height: u32,
    last_frame: Option<Vec<u8>>,
}

impl DxgiCapturer {
    /// Create a new DXGI capturer for the specified display
    pub fn new(display: Display) -> Result<Self> {
        let (device, context, duplication, desc) = Self::create_duplication(&display)?;

        Ok(Self {
            display,
            device,
            context,
            duplication,
            staging_texture: None,
            width: desc.ModeDesc.Width,
            height: desc.ModeDesc.Height,
            last_frame: None,
        })
    }

    /// Create D3D device and output duplication
    fn create_duplication(
        display_info: &Display,
    ) -> Result<(ID3D11Device, ID3D11DeviceContext, IDXGIOutputDuplication, DXGI_OUTDUPL_DESC)> {
        unsafe {
            // Create DXGI factory
            let factory: IDXGIFactory1 = CreateDXGIFactory1()
                .context("Failed to create DXGI factory")?;

            // Find the adapter and output for this display
            let (adapter, output) = Self::find_adapter_output(&factory, display_info)?;

            // Create D3D11 device
            let mut device: Option<ID3D11Device> = None;
            let mut context: Option<ID3D11DeviceContext> = None;

            D3D11CreateDevice(
                &adapter,
                D3D_DRIVER_TYPE_UNKNOWN,
                None,
                Default::default(),
                None,
                D3D11_SDK_VERSION,
                Some(&mut device),
                None,
                Some(&mut context),
            )
            .context("Failed to create D3D11 device")?;

            let device = device.context("D3D11 device is None")?;
            let context = context.context("D3D11 context is None")?;

            // Get IDXGIOutput1 interface
            let output1: IDXGIOutput1 = output.cast()
                .context("Failed to get IDXGIOutput1 interface")?;

            // Create output duplication
            let duplication = output1.DuplicateOutput(&device)
                .context("Failed to create output duplication")?;

            // Get duplication description
            let desc = duplication.GetDesc();

            tracing::info!(
                "Created DXGI duplication: {}x{}, display: {}",
                desc.ModeDesc.Width,
                desc.ModeDesc.Height,
                display_info.name
            );

            Ok((device, context, duplication, desc))
        }
    }

    /// Find the adapter and output for the specified display
    fn find_adapter_output(
        factory: &IDXGIFactory1,
        display: &Display,
    ) -> Result<(IDXGIAdapter1, IDXGIOutput)> {
        unsafe {
            let mut adapter_idx = 0u32;

            loop {
                // Enumerate adapters
                let adapter: IDXGIAdapter1 = match factory.EnumAdapters1(adapter_idx) {
                    Ok(a) => a,
                    Err(_) => break,
                };

                let mut output_idx = 0u32;

                loop {
                    // Enumerate outputs for this adapter
                    let output: IDXGIOutput = match adapter.EnumOutputs(output_idx) {
                        Ok(o) => o,
                        Err(_) => break,
                    };

                    // Check if this is the display we want
                    let desc = output.GetDesc()?;

                    let name = String::from_utf16_lossy(
                        &desc.DeviceName[..desc.DeviceName.iter().position(|&c| c == 0).unwrap_or(desc.DeviceName.len())]
                    );

                    if name == display.name || desc.Monitor.0 as isize == display.handle {
                        return Ok((adapter, output));
                    }

                    output_idx += 1;
                }

                adapter_idx += 1;
            }

            // If we didn't find the specific display, use the first one
            let adapter: IDXGIAdapter1 = factory.EnumAdapters1(0)
                .context("No adapters found")?;
            let output: IDXGIOutput = adapter.EnumOutputs(0)
                .context("No outputs found")?;

            Ok((adapter, output))
        }
    }

    /// Create or get the staging texture for CPU access
    fn get_staging_texture(&mut self, src_texture: &ID3D11Texture2D) -> Result<&ID3D11Texture2D> {
        if self.staging_texture.is_none() {
            unsafe {
                let mut desc = D3D11_TEXTURE2D_DESC::default();
                src_texture.GetDesc(&mut desc);

                desc.Usage = D3D11_USAGE_STAGING;
                desc.BindFlags = Default::default();
                desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32;
                desc.MiscFlags = Default::default();

                let mut staging: Option<ID3D11Texture2D> = None;
                self.device.CreateTexture2D(&desc, None, Some(&mut staging))
                    .context("Failed to create staging texture")?;
                let staging = staging.context("Staging texture is None")?;

                // Set high priority
                let resource: IDXGIResource = staging.cast()?;
                resource.SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM.0)?;

                self.staging_texture = Some(staging);
            }
        }

        Ok(self.staging_texture.as_ref().unwrap())
    }

    /// Acquire the next frame from the desktop
    fn acquire_frame(&mut self, timeout_ms: u32) -> Result<Option<(ID3D11Texture2D, DXGI_OUTDUPL_FRAME_INFO)>> {
        unsafe {
            let mut frame_info = DXGI_OUTDUPL_FRAME_INFO::default();
            let mut desktop_resource: Option<IDXGIResource> = None;

            let result = self.duplication.AcquireNextFrame(
                timeout_ms,
                &mut frame_info,
                &mut desktop_resource,
            );

            match result {
                Ok(_) => {
                    let resource = desktop_resource.context("Desktop resource is None")?;

                    // Check if there's actually a new frame
                    if frame_info.LastPresentTime == 0 {
                        self.duplication.ReleaseFrame().ok();
                        return Ok(None);
                    }

                    let texture: ID3D11Texture2D = resource.cast()
                        .context("Failed to cast to ID3D11Texture2D")?;

                    Ok(Some((texture, frame_info)))
                }
                Err(e) if e.code() == DXGI_ERROR_WAIT_TIMEOUT => {
                    // No new frame available
                    Ok(None)
                }
                Err(e) if e.code() == DXGI_ERROR_ACCESS_LOST => {
                    // Desktop duplication was invalidated, need to recreate
                    tracing::warn!("Desktop duplication access lost, will need to recreate");
                    Err(anyhow::anyhow!("Access lost"))
                }
                Err(e) => {
                    Err(e).context("Failed to acquire frame")
                }
            }
        }
    }

    /// Copy frame data to CPU-accessible memory
    fn copy_frame_data(&mut self, texture: &ID3D11Texture2D) -> Result<Vec<u8>> {
        unsafe {
            // Get or create staging texture
            let staging = self.get_staging_texture(texture)?.clone();

            // Copy from GPU texture to staging texture
            self.context.CopyResource(&staging, texture);

            // Map the staging texture for CPU read
            let mut mapped = D3D11_MAPPED_SUBRESOURCE::default();
            self.context
                .Map(&staging, 0, D3D11_MAP_READ, 0, Some(&mut mapped))
                .context("Failed to map staging texture")?;

            // Copy pixel data
            let src_pitch = mapped.RowPitch as usize;
            let dst_pitch = (self.width * 4) as usize;
            let height = self.height as usize;

            let mut data = vec![0u8; dst_pitch * height];

            let src_ptr = mapped.pData as *const u8;
            for y in 0..height {
                let src_row = src_ptr.add(y * src_pitch);
                let dst_row = data.as_mut_ptr().add(y * dst_pitch);
                ptr::copy_nonoverlapping(src_row, dst_row, dst_pitch);
            }

            // Unmap
            self.context.Unmap(&staging, 0);

            Ok(data)
        }
    }

    /// Extract dirty rectangles from frame info
    fn extract_dirty_rects(&self, _frame_info: &DXGI_OUTDUPL_FRAME_INFO) -> Option<Vec<DirtyRect>> {
        // TODO: Implement dirty rectangle extraction using
        // IDXGIOutputDuplication::GetFrameDirtyRects and GetFrameMoveRects
        // For now, return None to indicate full frame update
        None
    }
}

impl Capturer for DxgiCapturer {
    fn capture(&mut self) -> Result<Option<CapturedFrame>> {
        // Try to acquire a frame with 100ms timeout
        let frame_result = self.acquire_frame(100)?;

        let (texture, frame_info) = match frame_result {
            Some((t, f)) => (t, f),
            None => return Ok(None), // No new frame
        };

        // Copy frame data to CPU memory
        let data = self.copy_frame_data(&texture)?;

        // Release the frame
        unsafe {
            self.duplication.ReleaseFrame().ok();
        }

        // Extract dirty rectangles if available
        let dirty_rects = self.extract_dirty_rects(&frame_info);

        Ok(Some(CapturedFrame {
            width: self.width,
            height: self.height,
            data,
            timestamp: Instant::now(),
            display_id: self.display.id,
            dirty_rects,
        }))
    }

    fn display(&self) -> &Display {
        &self.display
    }

    fn is_valid(&self) -> bool {
        // Could check if duplication is still valid
        true
    }
}

impl Drop for DxgiCapturer {
    fn drop(&mut self) {
        // Release any held frame
        unsafe {
            self.duplication.ReleaseFrame().ok();
        }
    }
}

-                resource.SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM.0)?;

+                resource.SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM)?;


Now fix the keyboard.rs wScan issue - the code_unit from `encode_utf16` is a reference now, need to dereference it.


Now fix keyboard.rs - dereference code_unit:


Edit

    /// Type a unicode character
    #[cfg(windows)]
    pub fn type_char(&mut self, ch: char) -> Result<()> {
        let mut inputs = Vec::new();

        // For characters that fit in a single u16
        for code_unit in ch.encode_utf16(&mut [0; 2]) {
            // Key down
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });

            // Key up
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });
        }

        self.send_input(&inputs)
    }

    /// Type a unicode character
    #[cfg(windows)]
    pub fn type_char(&mut self, ch: char) -> Result<()> {
        let mut inputs = Vec::new();

        // For characters that fit in a single u16
        let mut buf = [0u16; 2];
        for &code_unit in ch.encode_utf16(&mut buf) {
            // Key down
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });

            // Key up
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });
        }

        self.send_input(&inputs)
    }


The file C:\Users\MikeSwanson\claude-projects\guru-connect\agent\src\input\keyboard.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
    76→
    77→        self.send_input(&[input])
    78→    }
    79→
    80→    /// Type a unicode character
    81→    #[cfg(windows)]
    82→    pub fn type_char(&mut self, ch: char) -> Result<()> {
    83→        let mut inputs = Vec::new();
    84→
    85→        // For characters that fit in a single u16
    86→        let mut buf = [0u16; 2];
    87→        for &code_unit in ch.encode_utf16(&mut buf) {
    88→            // Key down
    89→            inputs.push(INPUT {
    90→                r#type: INPUT_KEYBOARD,
    91→                Anonymous: INPUT_0 {
    92→                    ki: KEYBDINPUT {
    93→                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
    94→                        wScan: code_unit,
    95→                        dwFlags: KEYEVENTF_UNICODE,
    96→                        time: 0,
    97→                        dwExtraInfo: 0,
    98→                    },
    99→                },
   100→            });
   101→
   102→            // Key up
   103→            inputs.push(INPUT {
   104→                r#type: INPUT_KEYBOARD,
   105→                Anonymous: INPUT_0 {
   106→                    ki: KEYBDINPUT {
   107→                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
   108→                        wScan: code_unit,
   109→                        dwFlags: KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
   110→                        time: 0,
   111→                        dwExtraInfo: 0,
   112→                    },
   113→                },
   114→            });
   115→        }
   116→
   117→        self.send_input(&inputs)
   118→    }
   119→
   120→    /// Type a string of text
   121→    #[cfg(windows)]
   122→    pub fn type_string(&mut self, text: &str) -> Result<()> {

    /// Type a unicode character
    #[cfg(windows)]
    pub fn type_char(&mut self, ch: char) -> Result<()> {
        let mut inputs = Vec::new();

        // For characters that fit in a single u16
        let mut buf = [0u16; 2];
        for &code_unit in ch.encode_utf16(&mut buf) {
            // Key down
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });

            // Key up
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });
        }

        self.send_input(&inputs)
    }

//! Keyboard input simulation using Windows SendInput API

use anyhow::Result;

#[cfg(windows)]
use windows::Win32::UI::Input::KeyboardAndMouse::{
    SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBD_EVENT_FLAGS, KEYEVENTF_EXTENDEDKEY,
    KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, KEYEVENTF_UNICODE, KEYBDINPUT,
    MapVirtualKeyW, MAPVK_VK_TO_VSC_EX,
};

/// Keyboard input controller
pub struct KeyboardController {
    // Track modifier states for proper handling
    #[allow(dead_code)]
    modifiers: ModifierState,
}

#[derive(Default)]
struct ModifierState {
    ctrl: bool,
    alt: bool,
    shift: bool,
    meta: bool,
}

impl KeyboardController {
    /// Create a new keyboard controller
    pub fn new() -> Result<Self> {
        Ok(Self {
            modifiers: ModifierState::default(),
        })
    }

    /// Press a key down by virtual key code
    #[cfg(windows)]
    pub fn key_down(&mut self, vk_code: u16) -> Result<()> {
        self.send_key(vk_code, true)
    }

    /// Release a key by virtual key code
    #[cfg(windows)]
    pub fn key_up(&mut self, vk_code: u16) -> Result<()> {
        self.send_key(vk_code, false)
    }

    /// Send a key event
    #[cfg(windows)]
    fn send_key(&mut self, vk_code: u16, down: bool) -> Result<()> {
        // Get scan code from virtual key
        let scan_code = unsafe { MapVirtualKeyW(vk_code as u32, MAPVK_VK_TO_VSC_EX) as u16 };

        let mut flags = KEYBD_EVENT_FLAGS::default();

        // Add extended key flag for certain keys
        if Self::is_extended_key(vk_code) || (scan_code >> 8) == 0xE0 {
            flags |= KEYEVENTF_EXTENDEDKEY;
        }

        if !down {
            flags |= KEYEVENTF_KEYUP;
        }

        let input = INPUT {
            r#type: INPUT_KEYBOARD,
            Anonymous: INPUT_0 {
                ki: KEYBDINPUT {
                    wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(vk_code),
                    wScan: scan_code,
                    dwFlags: flags,
                    time: 0,
                    dwExtraInfo: 0,
                },
            },
        };

        self.send_input(&[input])
    }

    /// Type a unicode character
    #[cfg(windows)]
    pub fn type_char(&mut self, ch: char) -> Result<()> {
        let mut inputs = Vec::new();

        // For characters that fit in a single u16
        for code_unit in ch.encode_utf16(&mut [0; 2]) {
            // Key down
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });

            // Key up
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });
        }

        self.send_input(&inputs)
    }

    /// Type a string of text
    #[cfg(windows)]
    pub fn type_string(&mut self, text: &str) -> Result<()> {
        for ch in text.chars() {
            self.type_char(ch)?;
        }
        Ok(())
    }

    /// Send Secure Attention Sequence (Ctrl+Alt+Delete)
    ///
    /// Note: This requires special privileges on Windows.
    /// The agent typically needs to run as SYSTEM or use SAS API.
    #[cfg(windows)]
    pub fn send_sas(&mut self) -> Result<()> {
        // Try using the SAS library if available
        // For now, we'll attempt to send the key combination
        // This won't work in all contexts due to Windows security

        // Load the sas.dll and call SendSAS if available
        use windows::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryW};
        use windows::core::PCWSTR;

        unsafe {
            let dll_name: Vec<u16> = "sas.dll\0".encode_utf16().collect();
            let lib = LoadLibraryW(PCWSTR(dll_name.as_ptr()));

            if let Ok(lib) = lib {
                let proc_name = b"SendSAS\0";
                if let Some(proc) = GetProcAddress(lib, windows::core::PCSTR(proc_name.as_ptr())) {
                    // SendSAS takes a BOOL parameter: FALSE for Ctrl+Alt+Del
                    let send_sas: extern "system" fn(i32) = std::mem::transmute(proc);
                    send_sas(0); // FALSE = Ctrl+Alt+Del
                    return Ok(());
                }
            }
        }

        // Fallback: Try sending the keys (won't work without proper privileges)
        tracing::warn!("SAS library not available, Ctrl+Alt+Del may not work");

        // VK codes
        const VK_CONTROL: u16 = 0x11;
        const VK_MENU: u16 = 0x12; // Alt
        const VK_DELETE: u16 = 0x2E;

        // Press keys
        self.key_down(VK_CONTROL)?;
        self.key_down(VK_MENU)?;
        self.key_down(VK_DELETE)?;

        // Release keys
        self.key_up(VK_DELETE)?;
        self.key_up(VK_MENU)?;
        self.key_up(VK_CONTROL)?;

        Ok(())
    }

    /// Check if a virtual key code is an extended key
    #[cfg(windows)]
    fn is_extended_key(vk: u16) -> bool {
        matches!(
            vk,
            0x21..=0x28 | // Page Up, Page Down, End, Home, Arrow keys
            0x2D | 0x2E | // Insert, Delete
            0x5B | 0x5C | // Left/Right Windows keys
            0x5D |        // Applications key
            0x6F |        // Numpad Divide
            0x90 |        // Num Lock
            0x91          // Scroll Lock
        )
    }

    /// Send input events
    #[cfg(windows)]
    fn send_input(&self, inputs: &[INPUT]) -> Result<()> {
        let sent = unsafe { SendInput(inputs, std::mem::size_of::<INPUT>() as i32) };

        if sent as usize != inputs.len() {
            anyhow::bail!(
                "SendInput failed: sent {} of {} inputs",
                sent,
                inputs.len()
            );
        }

        Ok(())
    }

    #[cfg(not(windows))]
    pub fn key_down(&mut self, _vk_code: u16) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn key_up(&mut self, _vk_code: u16) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn type_char(&mut self, _ch: char) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn send_sas(&mut self) -> Result<()> {
        anyhow::bail!("SAS only supported on Windows")
    }
}

/// Common Windows virtual key codes
#[allow(dead_code)]
pub mod vk {
    pub const BACK: u16 = 0x08;
    pub const TAB: u16 = 0x09;
    pub const RETURN: u16 = 0x0D;
    pub const SHIFT: u16 = 0x10;
    pub const CONTROL: u16 = 0x11;
    pub const MENU: u16 = 0x12; // Alt
    pub const PAUSE: u16 = 0x13;
    pub const CAPITAL: u16 = 0x14; // Caps Lock
    pub const ESCAPE: u16 = 0x1B;
    pub const SPACE: u16 = 0x20;
    pub const PRIOR: u16 = 0x21; // Page Up
    pub const NEXT: u16 = 0x22;  // Page Down
    pub const END: u16 = 0x23;
    pub const HOME: u16 = 0x24;
    pub const LEFT: u16 = 0x25;
    pub const UP: u16 = 0x26;
    pub const RIGHT: u16 = 0x27;
    pub const DOWN: u16 = 0x28;
    pub const INSERT: u16 = 0x2D;
    pub const DELETE: u16 = 0x2E;

    // 0-9 keys
    pub const KEY_0: u16 = 0x30;
    pub const KEY_9: u16 = 0x39;

    // A-Z keys
    pub const KEY_A: u16 = 0x41;
    pub const KEY_Z: u16 = 0x5A;

    // Windows keys
    pub const LWIN: u16 = 0x5B;
    pub const RWIN: u16 = 0x5C;

    // Function keys
    pub const F1: u16 = 0x70;
    pub const F2: u16 = 0x71;
    pub const F3: u16 = 0x72;
    pub const F4: u16 = 0x73;
    pub const F5: u16 = 0x74;
    pub const F6: u16 = 0x75;
    pub const F7: u16 = 0x76;
    pub const F8: u16 = 0x77;
    pub const F9: u16 = 0x78;
    pub const F10: u16 = 0x79;
    pub const F11: u16 = 0x7A;
    pub const F12: u16 = 0x7B;

    // Modifier keys
    pub const LSHIFT: u16 = 0xA0;
    pub const RSHIFT: u16 = 0xA1;
    pub const LCONTROL: u16 = 0xA2;
    pub const RCONTROL: u16 = 0xA3;
    pub const LMENU: u16 = 0xA4; // Left Alt
    pub const RMENU: u16 = 0xA5; // Right Alt
}

-        for code_unit in ch.encode_utf16(&mut [0; 2]) {

+        for &code_unit in ch.encode_utf16(&mut buf) {


Now I need to fix websocket.rs. The issue is at line 128 where we have a borrow conflict. The code holds a lock on `stream` and then tries to call `self.parse_message`. We need to either drop the guard first, or change the approach. Let me look at the structure more carefully:

```rust
        let mut stream = self.stream.lock().await;

        match stream.next().await {
            Some(Ok(ws_msg)) => self.parse_message(ws_msg),
```

The issue is that `stream` is a `MutexGuard` that holds `&self`, and then we try to call `self.parse_message()` which takes `&mut self`. We need to drop the guard before calling parse_message.

Actually, looking at the error more carefully:
```
error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
   --> agent\src\transport\websocket.rs:128:33
    |
125 |         let mut stream = self.stream.lock().await;
    |                          ----------- immutable borrow occurs here
...
128 |             Some(Ok(ws_msg)) => self.parse_message(ws_msg),
```

The solution is to first consume the message from the stream, drop the guard, then parse. Let me fix this.


Now fix websocket.rs borrow conflict:


    /// Receive a message (blocking)
    pub async fn recv(&mut self) -> Result<Option<Message>> {
        // Return buffered message if available
        if let Some(msg) = self.incoming.pop_front() {
            return Ok(Some(msg));
        }

        let mut stream = self.stream.lock().await;

        match stream.next().await {
            Some(Ok(ws_msg)) => self.parse_message(ws_msg),
            Some(Err(e)) => {
                self.connected = false;
                Err(anyhow::anyhow!("WebSocket error: {}", e))
            }
            None => {
                self.connected = false;
                Ok(None)
            }
        }
    }

    /// Receive a message (blocking)
    pub async fn recv(&mut self) -> Result<Option<Message>> {
        // Return buffered message if available
        if let Some(msg) = self.incoming.pop_front() {
            return Ok(Some(msg));
        }

        let ws_result = {
            let mut stream = self.stream.lock().await;
            stream.next().await
        };

        match ws_result {
            Some(Ok(ws_msg)) => self.parse_message(ws_msg),
            Some(Err(e)) => {
                self.connected = false;
                Err(anyhow::anyhow!("WebSocket error: {}", e))
            }
            None => {
                self.connected = false;
                Ok(None)
            }
        }
    }


The file C:\Users\MikeSwanson\claude-projects\guru-connect\agent\src\transport\websocket.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
   114→            None => Ok(None),
   115→        }
   116→    }
   117→
   118→    /// Receive a message (blocking)
   119→    pub async fn recv(&mut self) -> Result<Option<Message>> {
   120→        // Return buffered message if available
   121→        if let Some(msg) = self.incoming.pop_front() {
   122→            return Ok(Some(msg));
   123→        }
   124→
   125→        let ws_result = {
   126→            let mut stream = self.stream.lock().await;
   127→            stream.next().await
   128→        };
   129→
   130→        match ws_result {
   131→            Some(Ok(ws_msg)) => self.parse_message(ws_msg),
   132→            Some(Err(e)) => {
   133→                self.connected = false;
   134→                Err(anyhow::anyhow!("WebSocket error: {}", e))
   135→            }
   136→            None => {
   137→                self.connected = false;
   138→                Ok(None)
   139→            }
   140→        }
   141→    }
   142→
   143→    /// Parse a WebSocket message into a protobuf message
   144→    fn parse_message(&mut self, ws_msg: WsMessage) -> Result<Option<Message>> {
   145→        match ws_msg {

    /// Receive a message (blocking)
    pub async fn recv(&mut self) -> Result<Option<Message>> {
        // Return buffered message if available
        if let Some(msg) = self.incoming.pop_front() {
            return Ok(Some(msg));
        }

        let ws_result = {
            let mut stream = self.stream.lock().await;
            stream.next().await
        };

        match ws_result {
            Some(Ok(ws_msg)) => self.parse_message(ws_msg),
            Some(Err(e)) => {
                self.connected = false;
                Err(anyhow::anyhow!("WebSocket error: {}", e))
            }
            None => {
                self.connected = false;
                Ok(None)
            }
        }
    }

//! WebSocket client transport
//!
//! Handles WebSocket connection to the GuruConnect server with:
//! - TLS encryption
//! - Automatic reconnection
//! - Protobuf message serialization

use crate::proto::Message;
use anyhow::{Context, Result};
use bytes::Bytes;
use futures_util::{SinkExt, StreamExt};
use prost::Message as ProstMessage;
use std::collections::VecDeque;
use std::sync::Arc;
use tokio::net::TcpStream;
use tokio::sync::Mutex;
use tokio_tungstenite::{
    connect_async, tungstenite::protocol::Message as WsMessage, MaybeTlsStream, WebSocketStream,
};

type WsStream = WebSocketStream<MaybeTlsStream<TcpStream>>;

/// WebSocket transport for server communication
pub struct WebSocketTransport {
    stream: Arc<Mutex<WsStream>>,
    incoming: VecDeque<Message>,
    connected: bool,
}

impl WebSocketTransport {
    /// Connect to the server
    pub async fn connect(url: &str, api_key: &str) -> Result<Self> {
        // Append API key as query parameter
        let url_with_auth = if url.contains('?') {
            format!("{}&api_key={}", url, api_key)
        } else {
            format!("{}?api_key={}", url, api_key)
        };

        tracing::info!("Connecting to {}", url);

        let (ws_stream, response) = connect_async(&url_with_auth)
            .await
            .context("Failed to connect to WebSocket server")?;

        tracing::info!("Connected, status: {}", response.status());

        Ok(Self {
            stream: Arc::new(Mutex::new(ws_stream)),
            incoming: VecDeque::new(),
            connected: true,
        })
    }

    /// Send a protobuf message
    pub async fn send(&mut self, msg: Message) -> Result<()> {
        let mut stream = self.stream.lock().await;

        // Serialize to protobuf binary
        let mut buf = Vec::with_capacity(msg.encoded_len());
        msg.encode(&mut buf)?;

        // Send as binary WebSocket message
        stream
            .send(WsMessage::Binary(buf.into()))
            .await
            .context("Failed to send message")?;

        Ok(())
    }

    /// Try to receive a message (non-blocking)
    pub fn try_recv(&mut self) -> Result<Option<Message>> {
        // Return buffered message if available
        if let Some(msg) = self.incoming.pop_front() {
            return Ok(Some(msg));
        }

        // Try to receive more messages
        let stream = self.stream.clone();
        let result = tokio::task::block_in_place(|| {
            tokio::runtime::Handle::current().block_on(async {
                let mut stream = stream.lock().await;

                // Use try_next for non-blocking receive
                match tokio::time::timeout(
                    std::time::Duration::from_millis(1),
                    stream.next(),
                )
                .await
                {
                    Ok(Some(Ok(ws_msg))) => Ok(Some(ws_msg)),
                    Ok(Some(Err(e))) => Err(anyhow::anyhow!("WebSocket error: {}", e)),
                    Ok(None) => {
                        // Connection closed
                        Ok(None)
                    }
                    Err(_) => {
                        // Timeout - no message available
                        Ok(None)
                    }
                }
            })
        });

        match result? {
            Some(ws_msg) => {
                if let Some(msg) = self.parse_message(ws_msg)? {
                    Ok(Some(msg))
                } else {
                    Ok(None)
                }
            }
            None => Ok(None),
        }
    }

    /// Receive a message (blocking)
    pub async fn recv(&mut self) -> Result<Option<Message>> {
        // Return buffered message if available
        if let Some(msg) = self.incoming.pop_front() {
            return Ok(Some(msg));
        }

        let mut stream = self.stream.lock().await;

        match stream.next().await {
            Some(Ok(ws_msg)) => self.parse_message(ws_msg),
            Some(Err(e)) => {
                self.connected = false;
                Err(anyhow::anyhow!("WebSocket error: {}", e))
            }
            None => {
                self.connected = false;
                Ok(None)
            }
        }
    }

    /// Parse a WebSocket message into a protobuf message
    fn parse_message(&mut self, ws_msg: WsMessage) -> Result<Option<Message>> {
        match ws_msg {
            WsMessage::Binary(data) => {
                let msg = Message::decode(Bytes::from(data))
                    .context("Failed to decode protobuf message")?;
                Ok(Some(msg))
            }
            WsMessage::Ping(data) => {
                // Pong is sent automatically by tungstenite
                tracing::trace!("Received ping");
                Ok(None)
            }
            WsMessage::Pong(_) => {
                tracing::trace!("Received pong");
                Ok(None)
            }
            WsMessage::Close(frame) => {
                tracing::info!("Connection closed: {:?}", frame);
                self.connected = false;
                Ok(None)
            }
            WsMessage::Text(text) => {
                // We expect binary protobuf, but log text messages
                tracing::warn!("Received unexpected text message: {}", text);
                Ok(None)
            }
            _ => Ok(None),
        }
    }

    /// Check if connected
    pub fn is_connected(&self) -> bool {
        self.connected
    }

    /// Close the connection
    pub async fn close(&mut self) -> Result<()> {
        let mut stream = self.stream.lock().await;
        stream.close(None).await?;
        self.connected = false;
        Ok(())
    }
}

-        let mut stream = self.stream.lock().await;

+            let mut stream = self.stream.lock().await;


Now let me try building again to see if there are any remaining errors.


Now let me try building again:


Bash

PROTOC="C:/Users/MikeSwanson/AppData/Local/Microsoft/WinGet/Packages/Google.Protobuf_Microsoft.Winget.Source_8wekyb3d8bbwe/bin/protoc.exe" cargo build -p guruconnect-agent --manifest-path "C:/Users/MikeSwanson/claude-projects/guru-connect/agent/Cargo.toml" 2>&1

Build guruconnect-agent after fixes


Error: Exit code 101
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   C:\Users\MikeSwanson\claude-projects\guru-connect\agent\Cargo.toml
workspace: C:\Users\MikeSwanson\claude-projects\guru-connect\Cargo.toml
warning: profiles for the non root package will be ignored, specify profiles at the workspace root:
package:   C:\Users\MikeSwanson\claude-projects\guru-connect\server\Cargo.toml
workspace: C:\Users\MikeSwanson\claude-projects\guru-connect\Cargo.toml
   Compiling guruconnect-agent v0.1.0 (C:\Users\MikeSwanson\claude-projects\guru-connect\agent)
warning: unused import: `Context`
 --> agent\src\capture\gdi.rs:7:14
  |
7 | use anyhow::{Context, Result};
  |              ^^^^^^^
  |
  = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default

warning: unused import: `DisplayInfo`
  --> agent\src\capture\mod.rs:12:28
   |
12 | pub use display::{Display, DisplayInfo};
   |                            ^^^^^^^^^^^

warning: unused imports: `DirtyRect as ProtoDirtyRect` and `RawFrame`
  --> agent\src\encoder\mod.rs:13:32
   |
13 | use crate::proto::{VideoFrame, RawFrame, DirtyRect as ProtoDirtyRect};
   |                                ^^^^^^^^  ^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: unused import: `KEYEVENTF_SCANCODE`
 --> agent\src\input\keyboard.rs:8:22
  |
8 |     KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, KEYEVENTF_UNICODE, KEYBDINPUT,
  |                      ^^^^^^^^^^^^^^^^^^

warning: unused import: `Display`
 --> agent\src\session\mod.rs:9:38
  |
9 | use crate::capture::{self, Capturer, Display};
  |                                      ^^^^^^^

warning: unused import: `std::sync::Arc`
  --> agent\src\session\mod.rs:16:5
   |
16 | use std::sync::Arc;
   |     ^^^^^^^^^^^^^^

warning: unused import: `tokio::sync::mpsc`
  --> agent\src\session\mod.rs:18:5
   |
18 | use tokio::sync::mpsc;
   |     ^^^^^^^^^^^^^^^^^

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> agent\src\session\mod.rs:95:17
   |
62 |         let transport = self.transport.as_mut()
   |                         -------------- first mutable borrow occurs here
...
94 |             while let Some(msg) = transport.try_recv()? {
   |                                   --------- first borrow later used here
95 |                 self.handle_message(&mut input, msg)?;
   |                 ^^^^ second mutable borrow occurs here

error[E0308]: mismatched types
  --> agent\src\input\keyboard.rs:87:13
   |
87 |         for &code_unit in ch.encode_utf16(&mut buf) {
   |             ^^^^^^^^^^    ------------------------- this is an iterator with items of type `&mut u16`
   |             |
   |             types differ in mutability
   |
   = note: expected mutable reference `&mut u16`
                      found reference `&_`

warning: unused import: `Encoder`
  --> agent\src\session\mod.rs:11:28
   |
11 | use crate::encoder::{self, Encoder};
   |                            ^^^^^^^

warning: unused import: `Capturer`
 --> agent\src\session\mod.rs:9:28
  |
9 | use crate::capture::{self, Capturer, Display};
  |                            ^^^^^^^^

warning: variable does not need to be mutable
  --> agent\src\capture\display.rs:63:9
   |
63 |     let mut display_id = 0u32;
   |         ----^^^^^^^^^^
   |         |
   |         help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default

warning: unused variable: `display_id`
  --> agent\src\capture\display.rs:63:9
   |
63 |     let mut display_id = 0u32;
   |         ^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_display_id`
   |
   = note: `#[warn(unused_variables)]` (part of `#[warn(unused)]`) on by default

warning: unused variable: `data`
   --> agent\src\transport\websocket.rs:151:29
    |
151 |             WsMessage::Ping(data) => {
    |                             ^^^^ help: if this is intentional, prefix it with an underscore: `_data`

warning: unused variable: `subscriber`
  --> agent\src\main.rs:23:9
   |
23 |     let subscriber = FmtSubscriber::builder()
   |         ^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_subscriber`

Some errors have detailed explanations: E0308, E0499.
For more information about an error, try `rustc --explain E0308`.
warning: `guruconnect-agent` (bin "guruconnect-agent") generated 13 warnings
error: could not compile `guruconnect-agent` (bin "guruconnect-agent") due to 2 previous errors; 13 warnings emitted


Two more errors:

1. **session/mod.rs** - Line 95: `cannot borrow *self as mutable more than once` - The issue is that `transport` borrows `self.transport.as_mut()` and then we try to call `self.handle_message()`. Need to refactor this.

2. **keyboard.rs** - Line 87: `encode_utf16` returns an iterator of `&mut u16`, not `&u16`. The fix needs to be different - we should iterate differently.

Let me fix these.


Two more errors. Let me fix them:


Edit

        // For characters that fit in a single u16
        let mut buf = [0u16; 2];
        for &code_unit in ch.encode_utf16(&mut buf) {

        // For characters that fit in a single u16
        let mut buf = [0u16; 2];
        let encoded = ch.encode_utf16(&mut buf);
        for code_unit in encoded.iter().copied() {


The file C:\Users\MikeSwanson\claude-projects\guru-connect\agent\src\input\keyboard.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
    81→    #[cfg(windows)]
    82→    pub fn type_char(&mut self, ch: char) -> Result<()> {
    83→        let mut inputs = Vec::new();
    84→
    85→        // For characters that fit in a single u16
    86→        let mut buf = [0u16; 2];
    87→        let encoded = ch.encode_utf16(&mut buf);
    88→        for code_unit in encoded.iter().copied() {
    89→            // Key down
    90→            inputs.push(INPUT {
    91→                r#type: INPUT_KEYBOARD,
    92→                Anonymous: INPUT_0 {

        // For characters that fit in a single u16
        let mut buf = [0u16; 2];
        let encoded = ch.encode_utf16(&mut buf);
        for code_unit in encoded.iter().copied() {

//! Keyboard input simulation using Windows SendInput API

use anyhow::Result;

#[cfg(windows)]
use windows::Win32::UI::Input::KeyboardAndMouse::{
    SendInput, INPUT, INPUT_0, INPUT_KEYBOARD, KEYBD_EVENT_FLAGS, KEYEVENTF_EXTENDEDKEY,
    KEYEVENTF_KEYUP, KEYEVENTF_SCANCODE, KEYEVENTF_UNICODE, KEYBDINPUT,
    MapVirtualKeyW, MAPVK_VK_TO_VSC_EX,
};

/// Keyboard input controller
pub struct KeyboardController {
    // Track modifier states for proper handling
    #[allow(dead_code)]
    modifiers: ModifierState,
}

#[derive(Default)]
struct ModifierState {
    ctrl: bool,
    alt: bool,
    shift: bool,
    meta: bool,
}

impl KeyboardController {
    /// Create a new keyboard controller
    pub fn new() -> Result<Self> {
        Ok(Self {
            modifiers: ModifierState::default(),
        })
    }

    /// Press a key down by virtual key code
    #[cfg(windows)]
    pub fn key_down(&mut self, vk_code: u16) -> Result<()> {
        self.send_key(vk_code, true)
    }

    /// Release a key by virtual key code
    #[cfg(windows)]
    pub fn key_up(&mut self, vk_code: u16) -> Result<()> {
        self.send_key(vk_code, false)
    }

    /// Send a key event
    #[cfg(windows)]
    fn send_key(&mut self, vk_code: u16, down: bool) -> Result<()> {
        // Get scan code from virtual key
        let scan_code = unsafe { MapVirtualKeyW(vk_code as u32, MAPVK_VK_TO_VSC_EX) as u16 };

        let mut flags = KEYBD_EVENT_FLAGS::default();

        // Add extended key flag for certain keys
        if Self::is_extended_key(vk_code) || (scan_code >> 8) == 0xE0 {
            flags |= KEYEVENTF_EXTENDEDKEY;
        }

        if !down {
            flags |= KEYEVENTF_KEYUP;
        }

        let input = INPUT {
            r#type: INPUT_KEYBOARD,
            Anonymous: INPUT_0 {
                ki: KEYBDINPUT {
                    wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(vk_code),
                    wScan: scan_code,
                    dwFlags: flags,
                    time: 0,
                    dwExtraInfo: 0,
                },
            },
        };

        self.send_input(&[input])
    }

    /// Type a unicode character
    #[cfg(windows)]
    pub fn type_char(&mut self, ch: char) -> Result<()> {
        let mut inputs = Vec::new();

        // For characters that fit in a single u16
        let mut buf = [0u16; 2];
        for &code_unit in ch.encode_utf16(&mut buf) {
            // Key down
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });

            // Key up
            inputs.push(INPUT {
                r#type: INPUT_KEYBOARD,
                Anonymous: INPUT_0 {
                    ki: KEYBDINPUT {
                        wVk: windows::Win32::UI::Input::KeyboardAndMouse::VIRTUAL_KEY(0),
                        wScan: code_unit,
                        dwFlags: KEYEVENTF_UNICODE | KEYEVENTF_KEYUP,
                        time: 0,
                        dwExtraInfo: 0,
                    },
                },
            });
        }

        self.send_input(&inputs)
    }

    /// Type a string of text
    #[cfg(windows)]
    pub fn type_string(&mut self, text: &str) -> Result<()> {
        for ch in text.chars() {
            self.type_char(ch)?;
        }
        Ok(())
    }

    /// Send Secure Attention Sequence (Ctrl+Alt+Delete)
    ///
    /// Note: This requires special privileges on Windows.
    /// The agent typically needs to run as SYSTEM or use SAS API.
    #[cfg(windows)]
    pub fn send_sas(&mut self) -> Result<()> {
        // Try using the SAS library if available
        // For now, we'll attempt to send the key combination
        // This won't work in all contexts due to Windows security

        // Load the sas.dll and call SendSAS if available
        use windows::Win32::System::LibraryLoader::{GetProcAddress, LoadLibraryW};
        use windows::core::PCWSTR;

        unsafe {
            let dll_name: Vec<u16> = "sas.dll\0".encode_utf16().collect();
            let lib = LoadLibraryW(PCWSTR(dll_name.as_ptr()));

            if let Ok(lib) = lib {
                let proc_name = b"SendSAS\0";
                if let Some(proc) = GetProcAddress(lib, windows::core::PCSTR(proc_name.as_ptr())) {
                    // SendSAS takes a BOOL parameter: FALSE for Ctrl+Alt+Del
                    let send_sas: extern "system" fn(i32) = std::mem::transmute(proc);
                    send_sas(0); // FALSE = Ctrl+Alt+Del
                    return Ok(());
                }
            }
        }

        // Fallback: Try sending the keys (won't work without proper privileges)
        tracing::warn!("SAS library not available, Ctrl+Alt+Del may not work");

        // VK codes
        const VK_CONTROL: u16 = 0x11;
        const VK_MENU: u16 = 0x12; // Alt
        const VK_DELETE: u16 = 0x2E;

        // Press keys
        self.key_down(VK_CONTROL)?;
        self.key_down(VK_MENU)?;
        self.key_down(VK_DELETE)?;

        // Release keys
        self.key_up(VK_DELETE)?;
        self.key_up(VK_MENU)?;
        self.key_up(VK_CONTROL)?;

        Ok(())
    }

    /// Check if a virtual key code is an extended key
    #[cfg(windows)]
    fn is_extended_key(vk: u16) -> bool {
        matches!(
            vk,
            0x21..=0x28 | // Page Up, Page Down, End, Home, Arrow keys
            0x2D | 0x2E | // Insert, Delete
            0x5B | 0x5C | // Left/Right Windows keys
            0x5D |        // Applications key
            0x6F |        // Numpad Divide
            0x90 |        // Num Lock
            0x91          // Scroll Lock
        )
    }

    /// Send input events
    #[cfg(windows)]
    fn send_input(&self, inputs: &[INPUT]) -> Result<()> {
        let sent = unsafe { SendInput(inputs, std::mem::size_of::<INPUT>() as i32) };

        if sent as usize != inputs.len() {
            anyhow::bail!(
                "SendInput failed: sent {} of {} inputs",
                sent,
                inputs.len()
            );
        }

        Ok(())
    }

    #[cfg(not(windows))]
    pub fn key_down(&mut self, _vk_code: u16) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn key_up(&mut self, _vk_code: u16) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn type_char(&mut self, _ch: char) -> Result<()> {
        anyhow::bail!("Keyboard input only supported on Windows")
    }

    #[cfg(not(windows))]
    pub fn send_sas(&mut self) -> Result<()> {
        anyhow::bail!("SAS only supported on Windows")
    }
}

/// Common Windows virtual key codes
#[allow(dead_code)]
pub mod vk {
    pub const BACK: u16 = 0x08;
    pub const TAB: u16 = 0x09;
    pub const RETURN: u16 = 0x0D;
    pub const SHIFT: u16 = 0x10;
    pub const CONTROL: u16 = 0x11;
    pub const MENU: u16 = 0x12; // Alt
    pub const PAUSE: u16 = 0x13;
    pub const CAPITAL: u16 = 0x14; // Caps Lock
    pub const ESCAPE: u16 = 0x1B;
    pub const SPACE: u16 = 0x20;
    pub const PRIOR: u16 = 0x21; // Page Up
    pub const NEXT: u16 = 0x22;  // Page Down
    pub const END: u16 = 0x23;
    pub const HOME: u16 = 0x24;
    pub const LEFT: u16 = 0x25;
    pub const UP: u16 = 0x26;
    pub const RIGHT: u16 = 0x27;
    pub const DOWN: u16 = 0x28;
    pub const INSERT: u16 = 0x2D;
    pub const DELETE: u16 = 0x2E;

    // 0-9 keys
    pub const KEY_0: u16 = 0x30;
    pub const KEY_9: u16 = 0x39;

    // A-Z keys
    pub const KEY_A: u16 = 0x41;
    pub const KEY_Z: u16 = 0x5A;

    // Windows keys
    pub const LWIN: u16 = 0x5B;
    pub const RWIN: u16 = 0x5C;

    // Function keys
    pub const F1: u16 = 0x70;
    pub const F2: u16 = 0x71;
    pub const F3: u16 = 0x72;
    pub const F4: u16 = 0x73;
    pub const F5: u16 = 0x74;
    pub const F6: u16 = 0x75;
    pub const F7: u16 = 0x76;
    pub const F8: u16 = 0x77;
    pub const F9: u16 = 0x78;
    pub const F10: u16 = 0x79;
    pub const F11: u16 = 0x7A;
    pub const F12: u16 = 0x7B;

    // Modifier keys
    pub const LSHIFT: u16 = 0xA0;
    pub const RSHIFT: u16 = 0xA1;
    pub const LCONTROL: u16 = 0xA2;
    pub const RCONTROL: u16 = 0xA3;
    pub const LMENU: u16 = 0xA4; // Left Alt
    pub const RMENU: u16 = 0xA5; // Right Alt
}

-        for &code_unit in ch.encode_utf16(&mut buf) {

+        let encoded = ch.encode_utf16(&mut buf);

+        for code_unit in encoded.iter().copied() {


Now fix the session borrow issue. The problem is that transport borrows `self.transport.as_mut()` mutably, and then `self.handle_message()` tries to borrow self again. We need to restructure this.

One solution is to have `handle_message` take only what it needs (the InputController) rather than &mut self. Let me look at the handle_message function and see what it uses.

Looking at the function, it only uses `input`. So I can change it to be a free function or just not take `&mut self`. But the simplest fix is to collect the messages first, then process them.

Actually, the simplest fix is to collect all messages first from the transport, then process them:```rust
let messages: Vec<_> = {
    let mut msgs = Vec::new();
    while let Some(msg) = transport.try_recv()? {
        msgs.push(msg);
    }
    msgs
};
for msg in messages {
    self.handle_message(&mut input, msg)?;
}
```But wait, this is still problematic because `transport` is a mutable borrow of `self.transport`. The issue is that we're calling `self.handle_message()` while holding the mutable borrow on `self.transport` via `transport`.

One clean solution is to make `handle_message` a static function that doesn't take self. Let me check if it uses any self fields...

Looking at the function:```rustfn handle_message(&mut self, input: &mut InputController, msg: Message) -> Result<()> {```

It only uses `input` from the parameters, not self. So we can make it take just the input:

```rust
fn handle_message(input: &mut InputController, msg: Message) -> Result<()> {
```Actually, I need to look more carefully. The function signature takes &mut self but doesn't seem to use any self fields. Let me confirm by making it static.

I'll restructure the loop to release the transport borrow before processing messages. By collecting messages first and then processing them separately, I can avoid the simultaneous mutable borrowing problem. This approach separates the message collection from message handling, which should resolve the borrow checker's concerns and simplify the code's structure.


Now fix the session borrow issue. I need to restructure the loop to not hold the transport borrow while calling handle_message:


    85→        let mut input = InputController::new()?;
    86→
    87→        // Calculate frame interval
    88→        let frame_interval = Duration::from_millis(1000 / self.config.capture.fps as u64);
    89→        let mut last_frame_time = Instant::now();
    90→
    91→        // Main loop
    92→        loop {
    93→            // Check for incoming messages (non-blocking)
    94→            while let Some(msg) = transport.try_recv()? {
    95→                self.handle_message(&mut input, msg)?;
    96→            }
    97→
    98→            // Capture and send frame if interval elapsed
    99→            if last_frame_time.elapsed() >= frame_interval {
   100→                last_frame_time = Instant::now();
   101→
   102→                if let Some(frame) = capturer.capture()? {
   103→                    let encoded = encoder.encode(&frame)?;
   104→
   105→                    // Skip empty frames (no changes)
   106→                    if encoded.size > 0 {
   107→                        let msg = Message {
   108→                            payload: Some(message::Payload::VideoFrame(encoded.frame)),
   109→                        };
   110→                        transport.send(msg).await?;
   111→                    }
   112→                }
   113→            }
   114→
   115→            // Small sleep to prevent busy loop
   116→            tokio::time::sleep(Duration::from_millis(1)).await;
   117→
   118→            // Check if still connected
   119→            if !transport.is_connected() {
   120→                tracing::warn!("Connection lost");
   121→                break;
   122→            }
   123→        }
   124→
   125→        self.state = SessionState::Disconnected;
   126→        Ok(())
   127→    }
   128→
   129→    /// Handle incoming message from server
   130→    fn handle_message(&mut self, input: &mut InputController, msg: Message) -> Result<()> {
   131→        match msg.payload {
   132→            Some(message::Payload::MouseEvent(mouse)) => {
   133→                // Handle mouse event
   134→                use crate::proto::MouseEventType;

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

        let mut input = InputController::new()?;

        // Calculate frame interval
        let frame_interval = Duration::from_millis(1000 / self.config.capture.fps as u64);
        let mut last_frame_time = Instant::now();

        // Main loop
        loop {
            // Check for incoming messages (non-blocking)
            while let Some(msg) = transport.try_recv()? {
                self.handle_message(&mut input, msg)?;
            }

            // Capture and send frame if interval elapsed
            if last_frame_time.elapsed() >= frame_interval {
                last_frame_time = Instant::now();

                if let Some(frame) = capturer.capture()? {
                    let encoded = encoder.encode(&frame)?;

                    // Skip empty frames (no changes)
                    if encoded.size > 0 {
                        let msg = Message {
                            payload: Some(message::Payload::VideoFrame(encoded.frame)),
                        };
                        transport.send(msg).await?;
                    }
                }
            }

            // Small sleep to prevent busy loop
            tokio::time::sleep(Duration::from_millis(1)).await;

            // Check if still connected
            if !transport.is_connected() {
                tracing::warn!("Connection lost");
                break;
            }
        }

        self.state = SessionState::Disconnected;
        Ok(())
    }

    /// Handle incoming message from server
    fn handle_message(&mut self, input: &mut InputController, msg: Message) -> Result<()> {
        match msg.payload {
            Some(message::Payload::MouseEvent(mouse)) => {
                // Handle mouse event
                use crate::proto::MouseEventType;


Looking at `handle_message`, it takes `&mut self` but doesn't actually use any self fields in the match arms. It only uses `input` and `msg`. So I can make it a static function that doesn't take self:```rust
fn handle_message(input: &mut InputController, msg: Message) -> Result<()> {
```Then the call would be:
```rust
Self::handle_message(&mut input, msg)?;
```This should fix the borrow issue.


I see handle_message takes &mut self but doesn't use self. Let me change it to be a static method:


    /// Handle incoming message from server
    fn handle_message(&mut self, input: &mut InputController, msg: Message) -> Result<()> {

    /// Handle incoming message from server
    fn handle_message(input: &mut InputController, msg: Message) -> Result<()> {


            // Check for incoming messages (non-blocking)
            while let Some(msg) = transport.try_recv()? {
                self.handle_message(&mut input, msg)?;
            }

            // Check for incoming messages (non-blocking)
            while let Some(msg) = transport.try_recv()? {
                Self::handle_message(&mut input, msg)?;
            }


The file C:\Users\MikeSwanson\claude-projects\guru-connect\agent\src\session\mod.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
   125→        self.state = SessionState::Disconnected;
   126→        Ok(())
   127→    }
   128→
   129→    /// Handle incoming message from server
   130→    fn handle_message(input: &mut InputController, msg: Message) -> Result<()> {
   131→        match msg.payload {
   132→            Some(message::Payload::MouseEvent(mouse)) => {
   133→                // Handle mouse event
   134→                use crate::proto::MouseEventType;

    /// Handle incoming message from server
    fn handle_message(input: &mut InputController, msg: Message) -> Result<()> {

//! Session management for the agent
//!
//! Handles the lifecycle of a remote session including:
//! - Connection to server
//! - Authentication
//! - Frame capture and encoding loop
//! - Input event handling

use crate::capture::{self, Capturer, Display};
use crate::config::Config;
use crate::encoder::{self, Encoder};
use crate::input::InputController;
use crate::proto::{Message, message};
use crate::transport::WebSocketTransport;
use anyhow::Result;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::mpsc;

/// Session manager handles the remote control session
pub struct SessionManager {
    config: Config,
    transport: Option<WebSocketTransport>,
    state: SessionState,
}

#[derive(Debug, Clone, PartialEq)]
enum SessionState {
    Disconnected,
    Connecting,
    Connected,
    Active,
}

impl SessionManager {
    /// Create a new session manager
    pub fn new(config: Config) -> Self {
        Self {
            config,
            transport: None,
            state: SessionState::Disconnected,
        }
    }

    /// Connect to the server
    pub async fn connect(&mut self) -> Result<()> {
        self.state = SessionState::Connecting;

        let transport = WebSocketTransport::connect(
            &self.config.server_url,
            &self.config.api_key,
        ).await?;

        self.transport = Some(transport);
        self.state = SessionState::Connected;

        Ok(())
    }

    /// Run the session main loop
    pub async fn run(&mut self) -> Result<()> {
        let transport = self.transport.as_mut()
            .ok_or_else(|| anyhow::anyhow!("Not connected"))?;

        self.state = SessionState::Active;

        // Get primary display
        let primary_display = capture::primary_display()?;
        tracing::info!("Using display: {} ({}x{})", primary_display.name, primary_display.width, primary_display.height);

        // Create capturer
        let mut capturer = capture::create_capturer(
            primary_display.clone(),
            self.config.capture.use_dxgi,
            self.config.capture.gdi_fallback,
        )?;

        // Create encoder
        let mut encoder = encoder::create_encoder(
            &self.config.encoding.codec,
            self.config.encoding.quality,
        )?;

        // Create input controller
        let mut input = InputController::new()?;

        // Calculate frame interval
        let frame_interval = Duration::from_millis(1000 / self.config.capture.fps as u64);
        let mut last_frame_time = Instant::now();

        // Main loop
        loop {
            // Check for incoming messages (non-blocking)
            while let Some(msg) = transport.try_recv()? {
                self.handle_message(&mut input, msg)?;
            }

            // Capture and send frame if interval elapsed
            if last_frame_time.elapsed() >= frame_interval {
                last_frame_time = Instant::now();

                if let Some(frame) = capturer.capture()? {
                    let encoded = encoder.encode(&frame)?;

                    // Skip empty frames (no changes)
                    if encoded.size > 0 {
                        let msg = Message {
                            payload: Some(message::Payload::VideoFrame(encoded.frame)),
                        };
                        transport.send(msg).await?;
                    }
                }
            }

            // Small sleep to prevent busy loop
            tokio::time::sleep(Duration::from_millis(1)).await;

            // Check if still connected
            if !transport.is_connected() {
                tracing::warn!("Connection lost");
                break;
            }
        }

        self.state = SessionState::Disconnected;
        Ok(())
    }

    /// Handle incoming message from server
    fn handle_message(&mut self, input: &mut InputController, msg: Message) -> Result<()> {
        match msg.payload {
            Some(message::Payload::MouseEvent(mouse)) => {
                // Handle mouse event
                use crate::proto::MouseEventType;
                use crate::input::MouseButton;

                match MouseEventType::try_from(mouse.event_type).unwrap_or(MouseEventType::MouseMove) {
                    MouseEventType::MouseMove => {
                        input.mouse_move(mouse.x, mouse.y)?;
                    }
                    MouseEventType::MouseDown => {
                        input.mouse_move(mouse.x, mouse.y)?;
                        if let Some(ref buttons) = mouse.buttons {
                            if buttons.left { input.mouse_click(MouseButton::Left, true)?; }
                            if buttons.right { input.mouse_click(MouseButton::Right, true)?; }
                            if buttons.middle { input.mouse_click(MouseButton::Middle, true)?; }
                        }
                    }
                    MouseEventType::MouseUp => {
                        if let Some(ref buttons) = mouse.buttons {
                            if buttons.left { input.mouse_click(MouseButton::Left, false)?; }
                            if buttons.right { input.mouse_click(MouseButton::Right, false)?; }
                            if buttons.middle { input.mouse_click(MouseButton::Middle, false)?; }
                        }
                    }
                    MouseEventType::MouseWheel => {
                        input.mouse_scroll(mouse.wheel_delta_x, mouse.wheel_delta_y)?;
                    }
                }
            }

            Some(message::Payload::KeyEvent(key)) => {
                // Handle keyboard event
                input.key_event(key.vk_code as u16, key.down)?;
            }

            Some(message::Payload::SpecialKey(special)) => {
                use crate::proto::SpecialKey;
                match SpecialKey::try_from(special.key).ok() {
                    Some(SpecialKey::CtrlAltDel) => {
                        input.send_ctrl_alt_del()?;
                    }
                    _ => {}
                }
            }

            Some(message::Payload::Heartbeat(_)) => {
                // Respond to heartbeat
                // TODO: Send heartbeat ack
            }

            Some(message::Payload::Disconnect(disc)) => {
                tracing::info!("Disconnect requested: {}", disc.reason);
                return Err(anyhow::anyhow!("Disconnect: {}", disc.reason));
            }

            _ => {
                // Ignore unknown messages
            }
        }

        Ok(())
    }
}

-    fn handle_message(&mut self, input: &mut InputController, msg: Message) -> Result<()> {

+    fn handle_message(input: &mut InputController, msg: Message) -> Result<()> {


The file C:\Users\MikeSwanson\claude-projects\guru-connect\agent\src\session\mod.rs has been updated. Here's the result of running `cat -n` on a snippet of the edited file:
    89→        let mut last_frame_time = Instant::now();
    90→
    91→        // Main loop
    92→        loop {
    93→            // Check for incoming messages (non-blocking)
    94→            while let Some(msg) = transport.try_recv()? {
    95→                Self::handle_message(&mut input, msg)?;
    96→            }
    97→
    98→            // Capture and send frame if interval elapsed
    99→            if last_frame_time.elapsed() >= frame_interval {
   100→                last_frame_time = Instant::now();

            // Check for incoming messages (non-blocking)
            while let Some(msg) = transport.try_recv()? {
                Self::handle_message(&mut input, msg)?;
            }

//! Session management for the agent
//!
//! Handles the lifecycle of a remote session including:
//! - Connection to server
//! - Authentication
//! - Frame capture and encoding loop
//! - Input event handling

use crate::capture::{self, Capturer, Display};
use crate::config::Config;
use crate::encoder::{self, Encoder};
use crate::input::InputController;
use crate::proto::{Message, message};
use crate::transport::WebSocketTransport;
use anyhow::Result;
use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::sync::mpsc;

/// Session manager handles the remote control session
pub struct SessionManager {
    config: Config,
    transport: Option<WebSocketTransport>,
    state: SessionState,
}

#[derive(Debug, Clone, PartialEq)]
enum SessionState {
    Disconnected,
    Connecting,
    Connected,
    Active,
}

impl SessionManager {
    /// Create a new session manager
    pub fn new(config: Config) -> Self {
        Self {
            config,
            transport: None,
            state: SessionState::Disconnected,
        }
    }

    /// Connect to the server
    pub async fn connect(&mut self) -> Result<()> {
        self.state = SessionState::Connecting;

        let transport = WebSocketTransport::connect(
            &self.config.server_url,
            &self.config.api_key,
        ).await?;

        self.transport = Some(transport);
        self.state = SessionState::Connected;

        Ok(())
    }

    /// Run the session main loop
    pub async fn run(&mut self) -> Result<()> {
        let transport = self.transport.as_mut()
            .ok_or_else(|| anyhow::anyhow!("Not connected"))?;

        self.state = SessionState::Active;

        // Get primary display
        let primary_display = capture::primary_display()?;
        tracing::info!("Using display: {} ({}x{})", primary_display.name, primary_display.width, primary_display.height);

        // Create capturer
        let mut capturer = capture::create_capturer(
            primary_display.clone(),
            self.config.capture.use_dxgi,
            self.config.capture.gdi_fallback,
        )?;

        // Create encoder
        let mut encoder = encoder::create_encoder(
            &self.config.encoding.codec,
            self.config.encoding.quality,
        )?;

        // Create input controller
        let mut input = InputController::new()?;

        // Calculate frame interval
        let frame_interval = Duration::from_millis(1000 / self.config.capture.fps as u64);
        let mut last_frame_time = Instant::now();

        // Main loop
        loop {
            // Check for incoming messages (non-blocking)
            while let Some(msg) = transport.try_recv()? {
                self.handle_message(&mut input, msg)?;
            }

            // Capture and send frame if interval elapsed
            if last_frame_time.elapsed() >= frame_interval {
                last_frame_time = Instant::now();

                if let Some(frame) = capturer.capture()? {
                    let encoded = encoder.encode(&frame)?;

                    // Skip empty frames (no changes)
                    if encoded.size > 0 {
                        let msg = Message {
                            payload: Some(message::Payload::VideoFrame(encoded.frame)),
                        };
                        transport.send(msg).await?;
                    }
                }
            }

            // Small sleep to prevent busy loop
            tokio::time::sleep(Duration::from_millis(1)).await;

            // Check if still connected
            if !transport.is_connected() {
                tracing::warn!("Connection lost");
                break;
            }
        }

        self.state = SessionState::Disconnected;
        Ok(())
    }

    /// Handle incoming message from server
    fn handle_message(input: &mut InputController, msg: Message) -> Result<()> {
        match msg.payload {
            Some(message::Payload::MouseEvent(mouse)) => {
                // Handle mouse event
                use crate::proto::MouseEventType;
                use crate::input::MouseButton;

                match MouseEventType::try_from(mouse.event_type).unwrap_or(MouseEventType::MouseMove) {
                    MouseEventType::MouseMove => {
                        input.mouse_move(mouse.x, mouse.y)?;
                    }
                    MouseEventType::MouseDown => {
                        input.mouse_move(mouse.x, mouse.y)?;
                        if let Some(ref buttons) = mouse.buttons {
                            if buttons.left { input.mouse_click(MouseButton::Left, true)?; }
                            if buttons.right { input.mouse_click(MouseButton::Right, true)?; }
                            if buttons.middle { input.mouse_click(MouseButton::Middle, true)?; }
                        }
                    }
                    MouseEventType::MouseUp => {
                        if let Some(ref buttons) = mouse.buttons {
                            if buttons.left { input.mouse_click(MouseButton::Left, false)?; }
                            if buttons.right { input.mouse_click(MouseButton::Right, false)?; }
                            if buttons.middle { input.mouse_click(MouseButton::Middle, false)?; }
                        }
                    }
                    MouseEventType::MouseWheel => {
                        input.mouse_scroll(mouse.wheel_delta_x, mouse.wheel_delta_y)?;
                    }
                }
            }

            Some(message::Payload::KeyEvent(key)) => {
                // Handle keyboard event
                input.key_event(key.vk_code as u16, key.down)?;
            }

            Some(message::Payload::SpecialKey(special)) => {
                use crate::proto::SpecialKey;
                match SpecialKey::try_from(special.key).ok() {
                    Some(SpecialKey::CtrlAltDel) => {
                        input.send_ctrl_alt_del()?;
                    }
                    _ => {}
                }
            }

            Some(message::Payload::Heartbeat(_)) => {
                // Respond to heartbeat
                // TODO: Send heartbeat ack
            }

            Some(message::Payload::Disconnect(disc)) => {
                tracing::info!("Disconnect requested: {}", disc.reason);
                return Err(anyhow::anyhow!("Disconnect: {}", disc.reason));
            }

            _ => {
                // Ignore unknown messages
            }
        }

        Ok(())
    }
}

-                self.handle_message(&mut input, msg)?;

+                Self::handle_message(&mut input, msg)?;