Diagnosing and Remediating a Thrashed Windows Laptop via an Outbound HTTP Agent Loop
/ 14 min read
Table of Contents
Overview
An acquaintance recently handed me an older Core i5 laptop running a preview build of Windows 11 that had degraded into an unusable state. The machine suffered from perpetual micro-stuttering, cursor lag, unresponsive applications, and recurring multi-second system freezes at 100% disk and kernel CPU utilization.
I wanted to run an in-depth root cause analysis remotely from my macOS workstation over the local network rather than hunched over a lagging physical keyboard. More importantly, I wanted to drive the investigation through an autonomous AI agent harness running on my Mac—having the agent inspect system telemetry, formulate hypotheses, author diagnostic probes, and apply remediations iteratively.
However, bootstrapping an interactive OpenSSH server on a machine already choking under severe I/O exhaustion triggered a servicing deadlock. To give the AI agent remote execution access without installing heavy third-party management tools or wiping the drive, I set up a lightweight outbound HTTP polling loop between a local Python control plane and PowerShell.
Here are my notes on the agentic triage architecture, the telemetry findings, the root causes, and the automated fixes that restored the machine.
Environment
The hardware and operating system profile of the target machine:
- Hardware Platform: Casper C650 Notebook
- CPU: Intel Core i5-8250U (4 Cores / 8 Threads, 1.60 GHz base, 3.40 GHz boost, 15W TDP)
- Memory: 1x 8 GB Crucial DDR4-2400 SODIMM (7.65 GB usable)
- Storage: Dahua C800A 240 GB SATA SSD (DRAM-less)
- Graphics: Intel UHD Graphics 620 + NVIDIA GeForce 940MX (2 GB dedicated VRAM)
- Operating System: Windows 11 Pro 64-bit (Build 10.0.26200, 24H2 Preview)
- Operator Workstation: macOS workstation on the LAN running an AI coding agent harness and a local HTTP control plane
The Inbound SSH Trap and the Slow-Machine Paradox
The standard sysadmin reflex for remote command execution on modern Windows is enabling the built-in OpenSSH server. I hosted a small setup script on my local HTTP server to fetch and install the Windows capability:
# Elevation checkif (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Warning "Administrator privileges required." Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`"" -Verb RunAs exit}
# 1. Install OpenSSH Server capabilityAdd-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
# 2. Configure and start sshd serviceSet-Service -Name sshd -StartupType 'Automatic'Start-Service -Name sshd
# 3. Configure Windows Firewall rule for TCP 22if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue)) { New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22}This ran straight into what I call the slow-machine paradox.
Between the servicing lockup, firewall considerations, and the friction of having the user type lengthy one-liners on an unresponsive keyboard, inbound SSH proved to be the wrong tool for an emergency triage.
Pivoting to an Ephemeral Outbound HTTP Agent Loop
Instead of fighting inbound access, I flipped the connection architecture into an outbound pull-and-push model. The Windows target periodically polls an HTTP endpoint on my Mac, retrieves queued PowerShell tasks, executes them locally via Invoke-Expression, and posts the output back.
This gave the AI agent on my Mac direct, decoupled execution capability over the remote target:
+------------------------------------+ +------------------------------------+| macOS Operator | | Windows Target || | | || +----------------------------+ | | +----------------------------+ || | AI Agent | | | | runner.ps1 | || | - Authors diag/fix scripts| | | | | || | - Enqueues tasks | | | | - Polls every 2 seconds | || | - Analyzes telemetry JSON | | | | - Invokes queued payloads | || +--------------┬-------------+ | | | - Streams stdout/stderr | || | | | +----------------------------+ || +--------------▼-------------+ | | | || | server.py | | | | || | - task_queue.json | | | | || | - agent_results.json | | | | || +--------------┬-------------+ | | | || | | | | || | <==== POST /agent/poll ======================| || | ===== { action: "run", script: "..." } =====>| || | | (Executes task) || | <==== POST /agent/result ====================| || | ===== { status: "ok" } =====================>| |+------------------------------------+ +------------------------------------+This model solved every operational obstacle:
- Zero External Packages: It relies strictly on built-in PowerShell
Invoke-RestMethodandInvoke-Expression. - NAT & Firewall Transparent: Outbound HTTP requests to the local network are permitted by default by Windows Firewall; no inbound ports or listening services were needed on the laptop.
- Autonomous Agent Feedback Loop: The AI agent running locally on macOS could iteratively enqueue tasks, observe execution output, digest structured JSON reports, formulate diagnostic conclusions, and push remediations without manual human intervention for each command.
The Operator Control Plane
On my Mac, I ran a compact Python server implementing the poll and result endpoints:
import http.serverimport jsonimport osimport socketserverimport time
PORT = 8000DIRECTORY = os.path.dirname(os.path.abspath(__file__))QUEUE_FILE = os.path.join(DIRECTORY, "task_queue.json")RESULTS_FILE = os.path.join(DIRECTORY, "agent_results.json")
def load_queue(): if os.path.exists(QUEUE_FILE): try: with open(QUEUE_FILE, "r") as f: return json.load(f) except Exception: return [] return []
def save_queue(queue): with open(QUEUE_FILE, "w") as f: json.dump(queue, f, indent=2)
class AgentServer(http.server.SimpleHTTPRequestHandler): def __init__(self, *args, **kwargs): super().__init__(*args, directory=DIRECTORY, **kwargs)
def do_POST(self): content_length = int(self.headers.get("Content-Length", 0)) post_data = self.rfile.read(content_length)
if self.path == "/agent/poll": queue = load_queue() if queue: task = queue.pop(0) save_queue(queue) response = { "action": "run", "name": task.get("name", "task"), "script_url": task.get("script_url", ""), "command": task.get("command", "") } else: response = {"action": "idle", "sleep": 2}
self.send_response(200) self.send_header("Content-type", "application/json") self.end_headers() self.wfile.write(json.dumps(response).encode("utf-8"))
elif self.path == "/agent/result": try: data = json.loads(post_data.decode("utf-8", errors="ignore")) except Exception: data = {"raw": post_data.decode("utf-8", errors="ignore")}
print(f"\n[AGENT RESULT] Task: {data.get('name')}\n{data.get('output', '')}\n", flush=True)
with open(RESULTS_FILE, "a") as f: f.write(json.dumps({"timestamp": time.time(), "result": data}) + "\n")
self.send_response(200) self.send_header("Content-type", "application/json") self.end_headers() self.wfile.write(b'{"status":"ok"}')
elif self.path.startswith("/report"): filename = "report_deep.json" if "deep" in self.path else "report.json" report_path = os.path.join(DIRECTORY, filename) with open(report_path, "wb") as f: f.write(post_data)
self.send_response(200) self.send_header("Content-type", "application/json") self.end_headers() self.wfile.write(b'{"status":"received"}') else: self.send_response(404) self.end_headers()
class ReusableTCPServer(socketserver.TCPServer): allow_reuse_address = True
if __name__ == "__main__": with ReusableTCPServer(("0.0.0.0", PORT), AgentServer) as httpd: print(f"Agent Server listening on 0.0.0.0:{PORT}", flush=True) httpd.serve_forever()The Target Client Runner
To bootstrap the agent loop on the laptop, only a single one-liner was executed in an elevated PowerShell window:
# Replace with the operator workstation IP on the LANirm http://192.168.1.50:8000/runner.ps1 | iexThe runner script loops indefinitely, polling the control plane and streaming back standard output and errors:
$ProgressPreference = 'SilentlyContinue'$ServerUrl = "http://192.168.1.50:8000"
Write-Host "[+] Agent runner active." -ForegroundColor GreenWrite-Host "[+] Polling operator server at $ServerUrl" -ForegroundColor Cyan
while ($true) { try { $body = @{ ComputerName = $env:COMPUTERNAME UserName = $env:USERNAME Time = (Get-Date).ToString("s") } | ConvertTo-Json
$resp = Invoke-RestMethod -Uri "$ServerUrl/agent/poll" -Method Post -Body $body -ContentType "application/json; charset=utf-8" -TimeoutSec 10
if ($resp.action -eq "run") { Write-Host "`n[*] Task received: $($resp.name)" -ForegroundColor Cyan $output = ""
try { if ($resp.script_url) { $scriptContent = Invoke-RestMethod -Uri "$ServerUrl/$($resp.script_url)" $output = Invoke-Expression $scriptContent *>&1 | Out-String } elseif ($resp.command) { $output = Invoke-Expression $resp.command *>&1 | Out-String } } catch { $output = "ERROR: $($_.Exception.Message)`n$($_.ScriptStackTrace)" }
$resultPayload = @{ name = $resp.name output = $output } | ConvertTo-Json -Depth 3
Invoke-RestMethod -Uri "$ServerUrl/agent/result" -Method Post -Body $resultPayload -ContentType "application/json; charset=utf-8" -TimeoutSec 10 | Out-Null } elseif ($resp.action -eq "done") { Write-Host "[*] Execution complete. Exiting loop." -ForegroundColor Green break } else { Start-Sleep -Seconds ($resp.sleep) } } catch { Write-Host "." -NoNewline -ForegroundColor DarkGray Start-Sleep -Seconds 3 }}The Autonomous Telemetry and Diagnostic Loop
With the target runner polling for work, the AI agent on macOS took over the diagnostic loop. The agent authored a reconnaissance payload (diag.ps1), placed it in the server directory, and enqueued the task in task_queue.json to dump hardware counters, processes, disk health, and Windows system events:
$ProgressPreference = 'SilentlyContinue'$report = [ordered]@{}
# 1. OS and Memory$cs = Get-CimInstance Win32_ComputerSystem$os = Get-CimInstance Win32_OperatingSystem$report['System'] = [ordered]@{ Manufacturer = $cs.Manufacturer Model = $cs.Model TotalPhysicalRAM_GB = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2) FreePhysicalRAM_GB = [math]::Round($os.FreePhysicalMemory / 1MB, 2) OSName = $os.Caption OSVersion = $os.Version}
# 2. CPU Frequency and Load$cpus = Get-CimInstance Win32_Processor$report['CPU'] = $cpus | ForEach-Object { [ordered]@{ Name = $_.Name Cores = $_.NumberOfCores LogicalProcessors = $_.NumberOfLogicalProcessors MaxClockSpeed_MHz = $_.MaxClockSpeed CurrentClockSpeed_MHz = $_.CurrentClockSpeed LoadPercentage = $_.LoadPercentage }}
# 3. Storage Hardware$report['PhysicalDisks'] = Get-PhysicalDisk | ForEach-Object { [ordered]@{ DeviceId = $_.DeviceId FriendlyName = $_.FriendlyName MediaType = $_.MediaType BusType = $_.BusType Size_GB = [math]::Round($_.Size / 1GB, 2) HealthStatus = $_.HealthStatus OperationalStatus = $_.OperationalStatus }}
# 4. Top Resource Consumers$report['TopCPUProcesses'] = Get-Process | Sort-Object CPU -Descending | Select-Object -First 10 | ForEach-Object { [ordered]@{ Name = $_.ProcessName; Id = $_.Id; CPU = [math]::Round($_.CPU, 2); WorkingSet_MB = [math]::Round($_.WorkingSet64 / 1MB, 2) }}$report['TopMemoryProcesses'] = Get-Process | Sort-Object WorkingSet64 -Descending | Select-Object -First 10 | ForEach-Object { [ordered]@{ Name = $_.ProcessName; Id = $_.Id; WorkingSet_MB = [math]::Round($_.WorkingSet64 / 1MB, 2) }}
# 5. Event Log Analysis (WHEA and storage errors over the last 48 hours)$since = (Get-Date).AddDays(-2)$report['HardwareEvents'] = Get-WinEvent -FilterHashtable @{ LogName = 'System'; Level = 1, 2, 3; StartTime = $since} -MaxEvents 50 -ErrorAction SilentlyContinue | Where-Object { $_.ProviderName -match 'disk|WHEA|Kernel-Power|storahci'} | Select-Object -First 10 | ForEach-Object { [ordered]@{ TimeCreated = $_.TimeCreated.ToString('s'); ProviderName = $_.ProviderName; Id = $_.Id; Message = ($_.Message -split "`r?`n")[0] }}
$json = $report | ConvertTo-Json -Depth 5Invoke-RestMethod -Uri "http://192.168.1.50:8000/report" -Method Post -Body $json -ContentType "application/json; charset=utf-8"Root Cause Analysis
When the target posted report.json back to the control plane, the AI agent digested the structured telemetry and pinpointed three distinct bottlenecks acting concurrently:
1. PCIe Correctable Error Storm (WHEA Event ID 17)
The Windows System event log was flooded with continuous entries from Microsoft-Windows-WHEA-Logger with Event ID 17 (“A corrected hardware error has occurred”).
The laptop houses dual graphics: an integrated Intel UHD 620 and a discrete NVIDIA GeForce 940MX. On Casper OEM motherboards, PCI Express Active State Power Management (ASPM / Link State Power Management) fails to synchronize packets cleanly when the discrete GPU toggles between low-power link states (L0s/L1) and active mode.
While the error is marked “corrected”, each occurrence fires a hardware interrupt. The Windows kernel traps the interrupt, retransmits the dropped packet, and logs the event. This constant loop created an interrupt storm:
- The kernel process
System(PID 4) had accumulated 4,471 CPU seconds of execution time. - The machine suffered from continuous cursor micro-stuttering and lag even when no user applications were open.
2. Physical Memory Exhaustion and Paging Pressure
Out of 7.65 GB usable RAM, the machine had only 1.52 GB free at idle (an 80.1% baseline memory commit).
The culprits were a mix of background bloat and memory leaks:
svchost.exe(PID 3012): 512 MBPhoneExperienceHost.exe(Windows Phone Link): 427 MBMsMpEng.exe(Windows Defender): 298 MBexplorer.exe: 281 MB- Background Autostart Items: Microsoft Teams, OneDrive, Adobe Acrobat Synchronizer (
AdobeCollabSync.exe), Microsoft Edge AutoLaunch, and four separate Realtek audio helper binaries (RAVBg64.exe).
With only 1.5 GB of headroom, opening even two or three browser tabs forced Windows into hard page faults, swapping memory back and forth to disk.
3. DRAM-Less SATA SSD Queue Saturation
The drive installed was a Dahua C800A 240 GB SATA SSD.
The C800A is a budget DRAM-less solid-state drive. Without dedicated DRAM cache to hold its Flash Translation Layer (FTL) mapping table in fast memory, random 4K write operations introduce significant controller latency. When MsMpEng.exe (file scanning), SearchIndexer.exe, TiWorker.exe, and SysMain (Superfetch attempting to prefetch binaries into an already constrained RAM) fired concurrent random write requests alongside virtual memory pagefile swapping, the controller’s I/O queue backed up completely. Disk active time spiked to 100%, freezing the entire UI for seconds at a time.
Remediation
Having isolated all three mechanisms, the AI agent synthesized a unified remediation payload (fix.ps1) targeting each root cause in a single queue execution:
Write-Host "[1/5] Disabling PCIe Link State Power Management (WHEA Event 17)..." -ForegroundColor Cyantry { # GUID 0ee12f90-76e7-4b4d-9e91-b3a2b56e0893 = Link State Power Management # Setting index 0 = Off powercfg /setacvalueindex SCHEME_CURRENT SUB_PCIEXPRESS 0ee12f90-76e7-4b4d-9e91-b3a2b56e0893 0 powercfg /setdcvalueindex SCHEME_CURRENT SUB_PCIEXPRESS 0ee12f90-76e7-4b4d-9e91-b3a2b56e0893 0 powercfg /setactive SCHEME_CURRENT Write-Host "[OK] PCIe ASPM disabled." -ForegroundColor Green} catch { Write-Warning "Failed to set PCIe power profile: $_"}
Write-Host "`n[2/5] Disabling disk-thrashing background services..." -ForegroundColor Cyan$servicesToDisable = @('SysMain', 'DiagTrack')foreach ($svc in $servicesToDisable) { if (Get-Service -Name $svc -ErrorAction SilentlyContinue) { Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue Set-Service -Name $svc -StartupType Disabled Write-Host "[OK] Service disabled: $svc" -ForegroundColor Green }}
Write-Host "`n[3/5] Terminating memory leaks and cleaning startup items..." -ForegroundColor CyanGet-Process -Name "PhoneExperienceHost", "CrossDeviceService" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinueSet-Service -Name "CrossDeviceService" -StartupType Manual -ErrorAction SilentlyContinue
$runKeyPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"$bloatEntries = @( 'Teams', 'OneDrive', 'Adobe Acrobat Synchronizer', 'MicrosoftEdgeAutoLaunch_9EDF312A5B50E1CCDC30082229C6FF51')foreach ($entry in $bloatEntries) { if (Get-ItemProperty -Path $runKeyPath -Name $entry -ErrorAction SilentlyContinue) { Remove-ItemProperty -Path $runKeyPath -Name $entry -ErrorAction SilentlyContinue Write-Host "[OK] Removed autostart entry: $entry" -ForegroundColor Green }}
Write-Host "`n[4/5] Executing SSD TRIM optimization..." -ForegroundColor Cyantry { Optimize-Volume -DriveLetter C -ReTrim -Verbose -ErrorAction SilentlyContinue Write-Host "[OK] TRIM issued to C: drive." -ForegroundColor Green} catch { Write-Warning "TRIM error: $_"}Verification and Telemetry Comparison
After the payload executed, the AI agent enqueued follow-up verification scripts (check_whea.ps1, check_cpu.ps1) to capture the delta against the baseline:
| Metric | Baseline (Pre-Fix) | Post-Remediation | Impact |
|---|---|---|---|
| Free Physical RAM | 1.52 GB | 2.67 GB | +1.15 GB recovered (+75% free memory) |
| Idle CPU Load | 73% | 5% | -68% CPU load reduction |
| WHEA Event 17 Errors | Flooding continuously | 0 errors | Hardware interrupt storm eliminated |
System (PID 4) Thrashing |
4,471 CPU seconds | Normalized to near 0% | Kernel interrupt overhead resolved |
| SSD I/O Congestion | SysMain, TiWorker, DiagTrack active |
Services disabled + TRIM issued | Random 4K queue saturation cleared |
PhoneExperienceHost |
427 MB memory footprint | Process killed, service manual | Memory leak cleared |
Disabling PCIe Link State Power Management immediately silenced the WHEA Event 17 interrupt storm. The cursor lag and UI stutters stopped instantly.
Hardware Audit and Thermal Benchmark
To verify whether the laptop had underlying hardware degradation or dried thermal paste, the AI agent enqueued an audit payload (audit.ps1) querying memory layout, SMART attributes, and thermal headroom under load.
Memory and Storage Topology
- Memory Topology:
ChannelA-DIMM0: 1x 8 GB Crucial DDR4-2400 SODIMM (PartNumber: CT8G4SFS824A.C8FHD1).ChannelB-DIMM0: Empty.- Bus Mode: Single-Channel (64-bit).
- SSD Health:
- Dahua C800A 240 GB SSD.
- Wear indicator: 0% wear (100% health remaining).
- Power-On Hours: 1,027 hours (~42 operational days).
- Temperature: 48°C (healthy operating range).
Thermal Saturation Test
The agent subjected the CPU to a 60-second multi-threaded mathematical calculation using background PowerShell jobs (Start-Job) while sampling clock speeds and temperatures every 10 seconds:
[Baseline] Temp: 71°C | Clock: 1600 MHz[T + 10s] Temp: 71°C | Clock: 1600 MHz | Load: 79%[T + 20s] Temp: 71°C | Clock: 1600 MHz | Load: 100%[T + 30s] Temp: 71°C | Clock: 1600 MHz | Load: 100%[T + 40s] Temp: 71°C | Clock: 1600 MHz | Load: 100%[T + 50s] Temp: 71°C | Clock: 1600 MHz | Load: 100%[T + 60s] Temp: 71°C | Clock: 1600 MHz | Load: 84%Peak Temperature: 71°CTakeaways
- Decoupled HTTP Loops Enable Autonomous Agentic Remediation: Pairing an AI coding agent with a decoupled HTTP pull-and-push mechanism turns the agent into a remote incident responder. The agent can inspect raw diagnostics, write tailored remediation scripts, and verify metrics across heterogeneous platforms without requiring complex SSH or remote management agents.
- Avoid Inbound Administrative Ports for Ad-Hoc Triage: Setting up OpenSSH, opening firewalls, and managing authentication on a crippled machine invites servicing deadlocks. A lightweight outbound HTTP polling loop over PowerShell
Invoke-RestMethodbypasses inbound network blocks and requires zero target-side installation. - Beware of DISM on Resource-Starved Hosts: Commands like
Add-WindowsCapabilityspin up Windows Update servicing workers (TiWorker.exe) that exacerbate disk and RAM thrashing on already compromised systems. - Hardware Interrupt Storms Mimic OS Corruption: Frequent PCIe ASPM packet errors (
WHEA Event 17) silently pin the Windows kernel (SystemPID 4) in an interrupt-handling loop. Setting PCIe Link State Power Management to “Off” in the active power profile cuts off the storm at the source. - Protect DRAM-Less SATA SSDs from Random 4K Floods: Turning off Superfetch (
SysMain) and diagnostic telemetry (DiagTrack) prevents FTL queue saturation and eliminates multi-second UI freezing on budget SATA drives. - Single-Channel Memory Is a Severe Constraint: Populating the second SODIMM slot to enable Dual-Channel 128-bit memory doubles available memory bandwidth and feeds both the CPU and shared integrated graphics.