Skill: danielfoojunwei/agentic-wireless-manager (version 1.0.1)

--- name: net-intel description: > UHCI Network Intelligence — AI-powered wireless network manager with SAC-LTC. Diagnoses, optimizes, and manages Wi-Fi and 3G/4G/5G hotspot switching. Provides deep RF environment awareness: signal quality, interference sources, channel congestion, presence detection, and navigation guidance. Use when: wifi slow, network issues, internet problems, signal weak, hotspot management, check connection, speed test, fix network, optimize wifi, why is my internet slow, network switch, net intel, sentinel mode, is someone nearby, interference, congestion, which direction, channel map, what is fighting for my network, which AP is best, walk towards signal. --- # UHCI Net-Intel: Universal Heterogeneous Connectivity Intelligence Derived from [PreceptualAI UHCI](https://github.com/Danielfoojunwei/PreceptualAI-Universal-Heterogeneous-Connectivity-Intelligence-UHCI-). SAC-LTC (Soft Actor-Critic with Liquid Time-Constant cells) for intelligent network management. ## Hardware Requirements ``` REQUIRED: Wi-Fi adapter (built-in or USB) — for reading signal data Internet connection (Wi-Fi or hotspot) — for speed/latency tests Terminal: Bash (macOS/Linux) or PowerShell (Windows) Python 3.8+ with PyTorch and NumPy — for SAC-LTC inference OPTIONAL: Admin/sudo access — for DNS optimization, DHCP renewal, adapter restart RESOURCE USAGE: CPU: < 2% (tiny model, ~15K parameters) RAM: < 30 MB (PyTorch + model) GPU: None needed Disk: < 2 MB (model weights + history) Network: < 2 MB per full scan cycle ``` ## Permissions Required ### From the User / Hardware Owner Before running, confirm the user grants: 1. **Wi-Fi adapter read access** — to read RSSI, noise, channel from the adapter 2. **Network scan permission** — OS may prompt for location/Wi-Fi access 3. **Terminal execution** — to run OS network commands 4. **Python execution** — for SAC-LTC inference engine 5. **Admin/sudo** (optional) — only for auto-optimization actions 6. **File write to ~/.net-intel/** — for history, weights, spatial calibration 7. **Background execution** — for monitor and sentinel modes ### From Access Points **NOTHING.** We are a client device only. We passively receive AP beacon signals that are publicly broadcast per 802.11 spec. We never authenticate to, communicate with, or modify any AP we are not connected to. ## Setup Run these checks on first use: ```bash # Check OS uname -s 2>/dev/null || echo "Windows" # Check Python + dependencies python3 -c "import torch; import numpy; print('Dependencies OK')" 2>&1 # If missing, install: # pip3 install torch numpy # Create data directory mkdir -p ~/.net-intel # Initialize or check model weights WEIGHTS="$HOME/.net-intel/weights.json" if [ ! -f "$WEIGHTS" ]; then python3 "$(dirname "$0")/sac_ltc_agent.py" --init-weights fi ``` If Python/PyTorch is not available, the skill falls back to heuristic-only mode (no SAC-LTC AI decisions, but all diagnostics and monitoring still work). ## Step 1: Detect OS and Wi-Fi Interface Detect the platform and find the Wi-Fi interface name. **macOS:** ```bash WIFI_IF=$(networksetup -listallhardwareports | grep -A 1 "Wi-Fi" | grep Device | awk '{print $2}') echo "Interface: $WIFI_IF" ``` **Linux:** ```bash WIFI_IF=$(iw dev 2>/dev/null | grep Interface | head -1 | awk '{print $2}') [ -z "$WIFI_IF" ] && WIFI_IF=$(nmcli -t -f DEVICE,TYPE dev | grep wifi | cut -d: -f1 | head -1) echo "Interface: $WIFI_IF" ``` **Windows (PowerShell):** ```powershell $wifiAdapter = Get-NetAdapter | Where-Object {$_.MediaType -eq "802.11" -or $_.InterfaceDescription -like "*Wi-Fi*"} | Select-Object -First 1 $WIFI_IF = $wifiAdapter.Name Write-Output "Interface: $WIFI_IF" ``` ## Step 2: Scan All Visible Networks Gather RSSI, noise, channel, SSID, BSSID for ALL visible networks (not just connected). **macOS:** ```bash # Connected network details + ALL nearby networks system_profiler SPAirPortDataType 2>/dev/null ``` Parse this output to extract for EACH visible network: - SSID, BSSID - RSSI (dBm), Noise (dBm) → compute SNR = RSSI - Noise - Channel number, band (2.4/5/6 GHz), bandwidth (20/40/80/160 MHz) - PHY mode (802.11a/b/g/n/ac/ax) - Security type - Whether it's a hotspot (detect by SSID patterns: "iPhone", "Android", "Galaxy", "Pixel", "Hotspot", "Mobile", "Moto", "OnePlus", "Samsung", "Xiaomi") **Linux:** ```bash # All visible networks with signal, channel, frequency, rate nmcli -f SSID,BSSID,SIGNAL,FREQ,CHAN,RATE,SECURITY,MODE dev wifi list 2>/dev/null # For noise floor (if available) iwconfig $WIFI_IF 2>/dev/null | grep -i noise ``` **Windows (PowerShell):** ```powershell # All visible networks with BSSID, signal, channel netsh wlan show networks mode=bssid # Connected interface details netsh wlan show interfaces ``` **Note on Windows:** Windows reports signal as percentage (0-100%), not dBm. Convert approximately: `dBm = (signal_pct / 2) - 100`. Windows does not expose noise floor; estimate noise at -90dBm for typical indoor environments. ## Step 3: Performance Tests (Automatic) Run these automatically every monitoring cycle. All tests are lightweight. **Latency + Packet Loss:** ```bash # macOS/Linux ping -c 10 -q 8.8.8.8 2>&1 | tail -2 # Parse: avg latency (ms), packet loss (%) ``` ```powershell # Windows ping -n 10 8.8.8.8 | Select-String "Average|Lost" ``` **DNS Speed:** ```bash # Test current DNS dig google.com +noall +stats 2>&1 | grep "Query time" # Compare with fast public DNS dig google.com @1.1.1.1 +noall +stats 2>&1 | grep "Query time" dig google.com @8.8.8.8 +noall +stats 2>&1 | grep "Query time" ``` ```powershell # Windows Measure-Command { Resolve-DnsName google.com -DnsOnly } | Select TotalMilliseconds Measure-Command { Resolve-DnsName google.com -Server 1.1.1.1 -DnsOnly } | Select TotalMilliseconds ``` **Throughput:** ```bash # Download 1MB test file, measure speed curl -o /dev/null -w '{"speed_bytes_sec": %{speed_download}, "time_sec": %{time_total}}' \ -s --max-time 15 http://speedtest.tele2.net/1MB.zip 2>/dev/null ``` ## Step 4: Score All Networks (0-100) For each visible network, compute a quality score using these weights: | Component | Weight | Excellent | Good | Fair | Poor | |-----------|--------|-----------|------|------|------| | Signal (RSSI) | 30 pts | >-50dBm: 30 | -50 to -60: 22 | -60 to -70: 15 | <-70: 7 | | SNR | 15 pts | >40dB: 15 | 25-40: 11 | 15-25: 7 | <15: 3 | | Latency | 25 pts | <20ms: 25 | 20-50: 19 | 50-100: 12 | >100: 5 | | Throughput | 20 pts | >50Mbps: 20 | 10-50: 15 | 1-10: 10 | <1: 3 | | Stability | 10 pts | 0% loss: 10 | <1%: 7 | 1-5%: 4 | >5%: 0 | For networks we're not connected to (no latency/throughput data), estimate from signal strength: - Estimated throughput: use Shannon capacity from SNR and bandwidth - Estimated latency: 10ms base + 5ms per wall (inferred from RSSI vs expected FSPL) ## Step 5: SAC-LTC Decision + Explanation Run the SAC-LTC inference engine: ```bash # Collect all network data as JSON array NETWORKS='[{"ssid":"MyWiFi","rssi_dbm":-47,"noise_dbm":-94,"latency_ms":12,...}, ...]' # Get AI decision with full explanation python3 ~/.net-intel/sac_ltc_agent.py --explain "$NETWORKS" ``` The agent returns: - **action**: which network index to use - **confidence**: 0-1 probability - **feature_weights**: which RF factors drove the decision - **temporal_trend**: stable / improving / degrading / reacting to change - **reason**: technical explanation - **layman_summary**: plain English explanation ## Step 6: Channel Congestion & Interference Map Build a complete picture of the RF environment from the scan data. ### Channel Congestion Map Group all visible networks by channel. For each channel: - Count networks (co-channel congestion) - Calculate total interference power: sum of all other networks' RSSI on that channel - Check adjacent channels (within +/-2 for 2.4GHz, overlapping bands for 5GHz 80/160MHz) - Identify the LEAST congested channel in each band ### Interference Identification From the monitoring history, classify interference sources: | Pattern | Likely Source | How to Identify | |---------|-------------|-----------------| | Periodic noise spikes (2.4GHz, 1-3min cycles) | Microwave oven | Correlates with meal times, 2.4GHz only, periodic with ~15s on/off | | Rapid SNR fluctuations (2.4GHz) | Bluetooth devices | Frequency hopping signature, constant low-level interference | | Persistent elevated noise (one channel) | Baby monitor / cordless phone | Narrowband, doesn't move, constant | | Sudden channel change (5GHz DFS channels) | Radar detected | Adapter forced to vacate Ch52-144, brief disconnection | | Broadband noise all channels | Power line / LED driver | Noise floor elevated equally across all channels | | Intermittent strong signal on your channel | Neighbor's AP / competing device | Identifiable by BSSID, consistent presence | ### Competing Devices on Your Connection List every device/network that shares your channel or adjacent channels: ``` WHAT'S COMPETING FOR YOUR AIRSPACE: Your channel: 149 (5GHz, 80MHz width) SAME CHANNEL (direct competition): "Neighbor_5G" -52dBm Ch149 [STRONG competitor] "Office_Main" -71dBm Ch149 [Moderate] "IoT_Network" -78dBm Ch149 [Weak but adds noise] OVERLAPPING CHANNELS (partial interference): "Guest_WiFi" -65dBm Ch153 [80MHz overlaps yours] TOTAL INTERFERENCE LOAD: High Your signal has to compete with 3 strong transmitters. WHAT YOU CAN DO: - Ask the "IoT_Network" owner to move to Ch44 (empty) - Switch your own router to Ch36 (only 1 weak network) - Or: the system can switch you to your 5G hotspot which currently scores higher (78 vs 71) ``` ## Step 7: Navigation Guidance **This is a paradigm shift:** The system tells the user exactly WHERE to move for better connectivity and WHICH device to get closer to. ### Signal Direction & Distance Estimation From RSSI readings + calibrated spatial map, provide walking directions: ``` NAVIGATION GUIDANCE =================== Your current connection: MyWiFi (score 65/100) FOR BETTER WI-FI: Walk towards your RIGHT (that's where MyRouter is) Moving ~2 meters closer should improve signal by about 6dBm, boosting your score to ~80/100. Your signal: -62dBm (fair) Estimated at 2m closer: -56dBm (good) At the router: -42dBm (excellent) FOR BEST HOTSPOT: Your "iPhone-Dan" hotspot is to your LEFT and BEHIND you. Signal is currently -68dBm (weak). Moving 3m towards it (left and back) would give you -55dBm and much better 5G speeds. BEST OPTION RIGHT NOW: Stay where you are and I'll switch you to "Office_5G" which has the strongest signal from your current position (-49dBm, score 82/100). It's in FRONT of you and slightly to the LEFT. AVOID: Don't move towards the back-right corner — that's where the interference source is (probably a microwave or Bluetooth speaker causing noise on your channel). ``` ### How Navigation Works 1. **RSSI-to-distance estimation**: Using the indoor path loss model: `distance_m = 10^((tx_power - rssi - constant) / (10 * n))` where n=2.8 for indoor, constant accounts for frequency 2. **Direction from spatial calibration**: Each AP is mapped to a direction relative to the user (from calibration in Step 8) 3. **Signal improvement prediction**: For each potential position: - Closer to AP: RSSI improves ~6dB per halving of distance - Through walls: each wall costs ~5-10dB - The LTC temporal data predicts whether the signal at a location is stable or fluctuating 4. **Interference source localization**: If noise is directional (affects some APs more than others), infer the interference source's approximate direction ## Step 8: Spatial Calibration (One-Time Setup) Map where each AP's signal is coming from relative to the user. ### Quick Calibration (tell the system) User says: "My router is to my right, my phone is in my pocket, the office AP is behind me" → Map SSIDs to directions: {MyRouter: right, iPhone: pocket/near, Office_AP: behind} ### Precise Calibration (body-blocking method) The human body absorbs 10-15dBm of 2.4GHz signal. By turning in the chair: 1. Face normally → record baseline RSSI for all APs 2. Turn LEFT (body blocks RIGHT) → APs that drop are to the RIGHT 3. Turn RIGHT (body blocks LEFT) → APs that drop are to the LEFT 4. Lean forward (body blocks BEHIND) → APs that drop are BEHIND ```bash # Run 4 scans during calibration, save to spatial map # macOS system_profiler SPAirPortDataType > /tmp/cal_baseline.txt echo "Turn LEFT now, wait 5 seconds..." sleep 5 system_profiler SPAirPortDataType > /tmp/cal_left.txt # ... repeat for each direction ``` Save spatial map to `~/.net-intel/spatial_map.json`: ```json { "calibrated": "2026-04-05T11:30:00", "access_points": [ {"bssid": "AA:BB:CC:DD:EE:01", "ssid": "MyRouter", "direction": "right", "baseline_rssi": -47}, {"bssid": "AA:BB:CC:DD:EE:02", "ssid": "iPhone-Dan", "direction": "left", "baseline_rssi": -61} ] } ``` ## Step 9: Auto-Optimization Actions These run automatically when conditions are met. The system logs what it did. | Condition | Action | Command (macOS) | Command (Linux) | Command (Windows) | |-----------|--------|-----------------|-----------------|-------------------| | DNS >2x slower than 1.1.1.1 | Switch DNS | `networksetup -setdnsservers Wi-Fi 1.1.1.1 1.0.0.1` | `resolvectl dns $IF 1.1.1.1 1.0.0.1` | `Set-DnsClientServerAddress -InterfaceAlias Wi-Fi -ServerAddresses 1.1.1.1,1.0.0.1` | | Stale DNS responses | Flush cache | `sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder` | `sudo systemd-resolve --flush-caches` | `Clear-DnsClientCache` | | DHCP lease expiring | Renew | `sudo ipconfig set $IF DHCP` | `sudo dhclient -r $IF && sudo dhclient $IF` | `ipconfig /renew` | | SAC-LTC confidence >0.7 for different network | Switch network | `networksetup -setairportnetwork $IF SSID [password]` | `nmcli dev wifi connect SSID` | `netsh wlan connect name=SSID` | | Packet loss >10% for 3+ cycles | Restart adapter | `networksetup -setairportpower $IF off && sleep 2 && networksetup -setairportpower $IF on` | `sudo nmcli radio wifi off && sleep 2 && sudo nmcli radio wifi on` | `Disable-NetAdapter -Name Wi-Fi -Confirm:$false; Start-Sleep 2; Enable-NetAdapter -Name Wi-Fi -Confirm:$false` | After each auto-action, log to `~/.net-intel/actions.log` and verify the action improved the connection by re-scanning after 10 seconds. ## Step 10: Log to History Append every scan cycle to `~/.net-intel/history.json` (one JSON object per line, JSONL format): ```json {"ts":"2026-04-05T11:32:00","nets":[{"ssid":"MyWiFi","rssi":-47,"noise":-94,"snr":47,"ch":149,"bw":80,"ping":12,"loss":0,"mbps":45,"score":85,"connected":true},{"ssid":"iPhone-Dan","rssi":-61,"noise":-90,"snr":29,"ch":6,"bw":40,"score":62,"connected":false,"is_hotspot":true,"cell_gen":"5g"}],"action":"stay","conf":0.92,"explain":"Strong signal + lowest latency","congestion":{"ch149":5,"ch44":0,"ch36":1},"interference":"none"} ``` Auto-prune entries older than 24 hours to keep file under 500KB. ## Modes of Operation ### Mode 1: Quick Scan (default, on-demand) Run Steps 1-7 once, display results, done. Output format (phone-friendly, plain English): ``` YOUR CONNECTION: Good (85/100) ================================ You're on MyWiFi — strong signal, fast speed. SPEED: Downloads 45 Mbps, response 12ms CHANNEL: 149 (5GHz) — a bit crowded (5 networks) BEST OPTIONS: 1. MyWiFi 85/100 [YOU ARE HERE] 2. Office_5G 78/100 [in front of you] 3. iPhone-Dan 62/100 [5G hotspot, to your left] WHAT'S COMPETING WITH YOU: 3 other networks on your channel. Strongest competitor: "Neighbor_5G" at -52dBm. TO IMPROVE: Move ~2m to your right (closer to router) → estimated score improvement: 85 → 92 WHAT I DID: Switched DNS (saves 20ms per page) Scanned: 11:32 AM ``` ### Mode 2: Monitor (autonomous background) Start with: "start monitoring" or "monitor my network" Uses CronCreate with 2-minute interval. Each cycle: 1. Run Steps 2-6 (scan, test, score, decide) 2. Auto-execute optimization actions (Step 9) 3. Log to history (Step 10) 4. Alert on significant changes The monitoring prompt for CronCreate: ``` Run /net-intel in monitor mode: scan networks, test speed, run SAC-LTC decision, auto-optimize, log to ~/.net-intel/history.json. Only report if something changed significantly (score dropped >15 points, switched networks, detected new interference, or detected presence). ``` ### Mode 3: Sentinel (live spatial awareness) Start with: "sentinel mode" or "watch around me" High-frequency passive RSSI monitoring (every 10 seconds) for presence detection. Uses only passive beacon RSSI reads — zero bandwidth consumption. ``` SENTINEL [LIVE] 11:32:45 ======================== FRONT clear | LEFT ---- YOU ---- RIGHT movement | clear (72%) | BEHIND clear Someone is moving to your LEFT. The signal from "Neighbor_5G" (to your left) is wobbling — consistent with a person walking through the signal path. NETWORK: MyWiFi 85/100 | stable ``` Proactive alerts when movement detected, interference changes, or signal anomalies occur. ### Mode 4: Query (conversational, anytime) User asks natural questions. The system answers from monitoring history + current scan + SAC-LTC reasoning: - "Why is my wifi slow?" → explains from data - "Should I switch to my hotspot?" → compares with reasoning - "What happened at 3pm?" → reviews history log - "Is someone behind me?" → runs sensing on calibrated APs - "Where should I sit for best signal?" → navigation guidance - "What's fighting for my channel?" → lists competing networks - "Which direction is the interference coming from?" → spatial analysis ## SAC-LTC: Why It's Better Than Normal Tools The Liquid Time-Constant cell has adaptive time constants τ(x) that automatically adjust: - Fast RSSI changes → τ shrinks → catches fast-moving person or sudden interference - Slow signal drift → τ grows → detects gradual degradation over hours - Periodic patterns → τ locks to the cycle → distinguishes microwave from human movement Normal tools use fixed thresholds. Our LTC adapts to whatever pattern is actually there. The KAN (Kolmogorov-Arnold Network) actor is inherently interpretable — we can read which features drove every decision, making the system explainable rather than a black box. ## Privacy & Ethics - All processing is local — no data leaves the device - We only passively receive public AP beacon signals (broadcast by design per 802.11) - Presence detection works by sensing signal disruptions, not identifying individuals - History stored only in ~/.net-intel/, user-controlled - Sentinel mode requires explicit user activation - No beacon frames or payload data is captured — only signal strength values ## Embedded Python Agent The SAC-LTC agent is in `sac_ltc_agent.py` alongside this skill file. It provides: - `--test` — self-test to verify installation - `--init-weights` — generate initial heuristic weights - `--decide '<json>'` — get switching decision - `--sense '<json>'` — presence detection from RSSI samples - `--explain '<json>'` — full RF environment explanation To train a better model with real RF physics: ```bash python3 train_sac_ltc.py --episodes 2000 python3 train_sac_ltc.py --eval ``` The training uses real ITU-R P.1238 indoor propagation, 3GPP path loss models, and Shannon capacity — not mocks or stubs.

path: plugin.yaml
content: # Agentic Liquid Wireless Manager — Plugin Manifest
# This file defines the plugin type, format, targets, and metadata.

plugin:
  name: "agentic-liquid-wireless-manager"
  version: "1.0.0"
  type: "code"  # "code" = source files executed at runtime, NOT pre-bundled binary
  description: >
    AI-powered wireless network intelligence using SAC-LTC for agentic AI systems.
    Autonomous Wi-Fi/hotspot management with RF explainability, presence detection,
    and navigation guidance.

# ---------------------------------------------------------------------------
# Plugin Type Classification
# ---------------------------------------------------------------------------
# BUNDLE PLUGIN: Pre-packaged artifact (compiled binary, container image, WASM).
#   - Distributed as a single opaque file.
#   - Host loads and executes without inspecting internals.
#   - Example: .vsix, .whl, Docker image, .zip with manifest.
#
# CODE PLUGIN: Source files interpreted/executed at runtime.
#   - Distributed as readable source (Python, Markdown, YAML).
#   - Host reads the skill file and executes commands directly.
#   - Fully inspectable, modifiable, forkable.
#   - Example: Claude Code SKILL.md, Manus tool YAML, OpenClaw skill.
#
# THIS PROJECT IS A CODE PLUGIN.
#   - Entry point: SKILL.md (instructions for the agentic host)
#   - Runtime: sac_ltc_agent.py (Python, executed via shell)
#   - Training: train_sac_ltc.py (optional, generates weights)
#   - No compilation, no containers, no binary artifacts.
#   - The host (Claude/Manus/OpenClaw) reads SKILL.md and follows instructions.
# ---------------------------------------------------------------------------

classification:
  type: "code"
  format: "skill-md"           # Primary interface is a Markdown skill file
  runtime: "python3"           # Agent logic runs as Python subprocess
  packaging: "source"          # Distributed as raw source files
  bundled: false               # Not pre-compiled or containerized

# ---------------------------------------------------------------------------
# Source Repository
# ---------------------------------------------------------------------------
source:
  repo: "https://github.com/Danielfoojunwei/Agentic-Liquid-Wireless-Manager"
  branch: "main"
  commit: "a323de326099a8830240a8700027d9b05887f22d"
  ref: "v1.0.0"
  license: "MIT"
  author: "Daniel Foo Jun Wei"

# ---------------------------------------------------------------------------
# Bundle Format (if this were bundled — currently N/A)
# ---------------------------------------------------------------------------
# This is a CODE plugin, not a BUNDLE plugin. If bundled in the future:
bundle:
  format: "none"               # No bundle format currently
  potential_formats:
    - name: "zip"
      description: "ZIP archive with SKILL.md + sac_ltc_agent.py + weights.json"
      use_case: "Manual distribution, offline install"
    - name: "pip"
      description: "Python package via PyPI (pip install agentic-lwm)"
      use_case: "Python ecosystem distribution"
    - name: "docker"
      description: "Container with all deps pre-installed"
      use_case: "Isolated deployment, CI/CD"
    - name: "skill-bundle"
      description: "Single SKILL.md with embedded Python (base64 or heredoc)"
      use_case: "Single-file distribution for Claude Code"

# ---------------------------------------------------------------------------
# Host Targets
# ---------------------------------------------------------------------------
# Which agentic AI platforms can load and use this plugin.
host_targets:
  - name: "Claude Code"
    type: "skill"
    integration_method: "SKILL.md file in project or ~/.claude/skills/"
    invocation: "/net-intel or natural language triggers"
    requirements:
      - "Claude Code CLI, desktop app, or web (claude.ai/code)"
      - "Bash shell access"
      - "Python 3.8+ with torch and numpy"
    entry_point: "SKILL.md"
    install: |
      cp SKILL.md /path/to/project/SKILL.md
      cp sac_ltc_agent.py /path/to/project/sac_ltc_agent.py
      python3 sac_ltc_agent.py --init-weights

  - name: "Manus"
    type: "tool"
    integration_method: "Tool definition in agent YAML config"
    invocation: "Agent calls tool when network topics arise"
    requirements:
      - "Manus agent with shell tool execution"
      - "Python 3.8+ with torch and numpy"
    entry_point: "sac_ltc_agent.py (CLI)"
    install: |
      # Add to Manus agent tools config
      # See README.md → Usage on Agentic AI Platforms → Manus

  - name: "OpenClaw"
    type: "skill"
    integration_method: "Skill registration via YAML"
    invocation: "Trigger phrases or direct skill call"
    requirements:
      - "OpenClaw workspace with skill support"
      - "Python 3.8+ with torch and numpy"
    entry_point: "sac_ltc_agent.py (CLI)"
    install: |
      # Register in openclaw_skills.yaml
      # See README.md → Usage on Agentic AI Platforms → OpenClaw

  - name: "Any LLM Agent"
    type: "cli-tool"
    integration_method: "Shell command execution"
    invocation: "python3 sac_ltc_agent.py --explain/--sense/--decide"
    requirements:
      - "Agent can execute shell commands"
      - "Python 3.8+ with torch and numpy"
    entry_point: "sac_ltc_agent.py (CLI)"
    install: |
      pip install torch numpy
      python3 sac_ltc_agent.py --init-weights

# ---------------------------------------------------------------------------
# Operating System Targets
# ---------------------------------------------------------------------------
os_targets:
  - name: "macOS"
    versions: "12+ (Monterey, Ventura, Sonoma, Sequoia)"
    shell: "zsh/bash"
    scan_command: "system_profiler SPAirPortDataType"
    notes: "Location Services required for nearby network scanning"

  - name: "Linux"
    distros: "Ubuntu 20.04+, Debian 11+, Fedora 36+, Arch"
    shell: "bash"
    scan_command: "nmcli dev wifi list"
    notes: "NetworkManager required. User must be in netdev group."

  - name: "Windows"
    versions: "10 (1903+), 11"
    shell: "PowerShell 5.1+"
    scan_command: "netsh wlan show interfaces"
    notes: "curl.exe built-in on Windows 10+. UAC for admin commands."

# ---------------------------------------------------------------------------
# Dependencies
# ---------------------------------------------------------------------------
dependencies:
  required:
    - name: "Python"
      version: ">=3.8"
      purpose: "Runtime for SAC-LTC agent"

  optional:
    - name: "PyTorch"
      version: ">=2.0.0"
      pip: "torch"
      purpose: "SAC-LTC neural network inference and training"
      fallback: "Heuristic-only mode (no AI decisions)"

    - name: "NumPy"
      version: ">=1.24.0"
      pip: "numpy"
      purpose: "Numerical operations for training"
      fallback: "Pure Python math (slower but functional)"

# ---------------------------------------------------------------------------
# File Manifest
# ---------------------------------------------------------------------------
files:
  entry_points:
    - path: "SKILL.md"
      type: "skill-definition"
      description: "Claude Code skill with all modes and instructions"
    - path: "sac_ltc_agent.py"
      type: "python-cli"
      description: "Lightweight inference agent (LTC + KAN + sensing)"
    - path: "train_sac_ltc.py"
      type: "python-cli"
      description: "Training pipeline with ITU-R/3GPP physics"

  documentation:
    - path: "docs/SAC_LTC_ARCHITECTURE.md"
    - path: "docs/TRAINING_PIPELINE.md"
    - path: "docs/PERMISSIONS_AND_CONTROLS.md"
    - path: "docs/RF_ENVIRONMENT_INTELLIGENCE.md"
    - path: "docs/CROSS_PLATFORM_COMMANDS.md"

  data:
    - path: "data/sample_environment.json"
    - path: "data/sample_episode.json"
    - path: "data/training_log.json"

  metadata:
    - path: "plugin.yaml"
    - path: "package.json"
    - path: "CHANGELOG.md"
    - path: "LICENSE"
    - path: "CONTRIBUTING.md"


path: README.md
content: # Agentic Liquid Wireless Manager

**The first wireless network intelligence layer built for agentic AI systems running on laptops and PCs.**

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-lightgrey.svg)]()

---

## The Problem

Laptops running local agentic AI (Claude, Manus, OpenClaw, local LLMs) depend entirely on their wireless connection. But:

1. **No visibility.** Your OS shows signal bars. It doesn't tell you that 5 neighbor networks are fighting for your channel, that your hotspot's cellular backhaul is throttled, or that someone walking past just caused a 10dBm signal drop.

2. **No intelligence.** Built-in tools offer "reset your adapter" as troubleshooting. They can't tell you WHY your connection degraded, or predict that it will degrade at 6pm when your neighbors come home.

3. **No autonomy.** You manually switch networks, manually check speed, manually diagnose. An agentic AI running on your laptop should be able to manage its own connectivity.

4. **No explainability.** When your video call drops, nothing tells you it was because a microwave oven on the floor below just fired up on your 2.4GHz channel.

## Our Solution

A lightweight AI agent that runs alongside your agentic AI, continuously monitoring and optimizing the wireless environment using **SAC-LTC (Soft Actor-Critic with Liquid Time-Constant cells)** — a neural architecture that adapts its temporal reasoning speed to match real-world wireless dynamics.

```
Your Agentic AI (Claude, Manus, OpenClaw, etc.)
      |
      | "Why is my internet slow?"
      v
[Agentic Liquid Wireless Manager]
      |
      |--- Reads Wi-Fi adapter (passive, read-only)
      |--- Runs SAC-LTC inference (~0.5ms, CPU, 17K params)
      |--- Returns: explanation + action + confidence
      |
      v
"Ch149 has 5 competing networks. Your iPhone hotspot's
 cellular backhaul spiked to 138ms. Switching DNS to
 save 20ms. Move your phone closer — signal dropped 8dBm."
```

## What Makes This Different

### vs. Built-In OS Tools

| Capability | macOS/Windows/Linux Built-In | This System |
|-----------|---------------------------|-------------|
| Signal info | Bars or single dBm number | 0-100 score with component breakdown |
| Troubleshooting | "Reset adapter" / generic wizard | "Ch149 has 5 competing networks, your SNR dropped 12dB from adjacent-channel interference" |
| Temporal awareness | Current snapshot only | LTC tracks 24h trends, predicts degradation, remembers patterns |
| Network switching | Manual | Autonomous (SAC-LTC decides, confidence-gated) |
| Hotspot awareness | None | Detects iPhone/Android hotspot, fingerprints 3G/4G/5G, measures backhaul vs Wi-Fi hop |
| Interference detection | None | Classifies microwave, Bluetooth, radar, neighboring APs by temporal signature |
| Presence detection | None | Senses human movement through walls via RSSI variance |
| Navigation guidance | None | "Walk 2m to your right for 6dBm improvement" |
| Channel intelligence | None | Full congestion map with competitor identification |

### vs. Commercial Wi-Fi Analyzers (NetSpot, WiFi Explorer, inSSIDer)

| Capability | Commercial Analyzers | This System |
|-----------|---------------------|-------------|
| Scanning | Manual scan, visual heatmap | Continuous autonomous monitoring |
| Actions | Report only — you decide what to do | Auto-optimizes: switches networks, DNS, cache, adapter |
| AI intelligence | None — just displays data | SAC-LTC makes decisions, explains reasoning |
| Temporal memory | None — snapshot per scan | LTC cell carries 24h of temporal context |
| Agentic integration | None — standalone GUI app | Designed for AI agents (Claude, Manus, OpenClaw) |
| Presence sensing | None | Wi-Fi sensing via RSSI variance analysis |
| Price | $10-50 | Free, open source |

### vs. Research Wi-Fi Sensing Systems

| Capability | Research Systems (CSI-based) | This System |
|-----------|---------------------------|-------------|
| Hardware | Modified drivers, specific NICs, CSI extraction tools | Any laptop with standard Wi-Fi adapter |
| Data | Channel State Information (amplitude + phase) | RSSI only (universally available) |
| Accuracy | Sub-meter localization possible | Quadrant-level (front/back/left/right) |
| Setup | Complex driver patching, specialized firmware | `pip install torch numpy` |
| Deployment | Lab environment | Any laptop, any OS |
| Temporal intelligence | Fixed-window statistics | LTC adaptive time constants |

## Benchmark Results

### SAC-LTC vs Random Network Selection

Tested on 50 unseen physics-generated environments (ITU-R/3GPP models):

```
SAC-LTC Agent:
  Mean reward:   -27.05  (higher is better)
  Std:            21.86
  Best episode:   +10.10
  Worst episode: -106.55

Random Baseline:
  Mean reward:   -42.43

IMPROVEMENT: 36.2% over random
```

### Presence Detection Accuracy

Tested with synthetic RSSI traces (quiet room vs simulated human movement):

```
Quiet room (no movement):
  Classification: "none"
  Confidence: 82.8%
  False positive rate: <5%

Active movement (simulated person walking):
  Classification: "active_movement"
  Confidence: 85.2%
  Detection rate: >90% for movement >3dBm variance

Periodic interference (microwave signature):
  Classification: "possible_interference"
  Correctly identified as non-human: 60% confidence reduction applied
```

### Resource Usage (Measured)

```
Model inference:  ~0.5ms per decision (CPU, Apple M1)
Training:         536 seconds for 1000 episodes (CPU only)
Memory:           ~30MB (PyTorch + model)
Disk:             ~400KB (weights JSON)
Network overhead: <2MB per scan cycle (1MB speed test + pings + DNS)
CPU utilization:  <2% during monitoring (1 scan every 2 minutes)
```

## Architecture

```
Raw Network Data (RSSI, noise, latency, throughput, loss, congestion, band, type)
    |
    v
[Normalize to 0-1] — 8 features per network
    |
    v
[LTC Cell] — Liquid Time-Constant neuron (continuous-time ODE)
  tau(x) * dh/dt = -h + tanh(W_h*h + W_x*x + b)
  tau(x) = tau_base + softplus(W_tau*x + b_tau)
  input: 8 → hidden: 32
  Sequence: last 8 observations (sliding window)
    |
    v
[KAN Actor] — Kolmogorov-Arnold Network (interpretable)
  KANLinear(32→16) → LayerNorm → KANLinear(16→N) → Softmax
  Each KAN layer: base_weight(SiLU) + spline_weight(RBF B-spline)
    |
    v
Action probabilities + Feature attributions + Plain English explanation
```

**Total: 17,274 parameters. CPU-only. No GPU needed.**

Full architecture disclosure: [docs/SAC_LTC_ARCHITECTURE.md](docs/SAC_LTC_ARCHITECTURE.md)

## Quick Start

### 1. Install

```bash
git clone https://github.com/Danielfoojunwei/Agentic-Liquid-Wireless-Manager.git
cd Agentic-Liquid-Wireless-Manager

pip install torch numpy
```

### 2. Initialize and Test

```bash
python3 sac_ltc_agent.py --init-weights
python3 sac_ltc_agent.py --test
```

### 3. Train (Optional — heuristic weights work out of the box)

```bash
python3 train_sac_ltc.py --episodes 2000
python3 train_sac_ltc.py --eval
```

### 4. Use with Your Agentic AI

See platform-specific instructions below.

## Usage on Agentic AI Platforms

### Claude Code

**Step 1:** Copy the skill file:
```bash
# Option A: Copy to your project
cp SKILL.md /path/to/your/project/SKILL.md

# Option B: Copy to global skills directory
mkdir -p ~/.claude/skills/net-intel
cp SKILL.md ~/.claude/skills/net-intel/SKILL.md
cp sac_ltc_agent.py ~/.claude/skills/net-intel/sac_ltc_agent.py
```

**Step 2:** Ensure the Python agent is accessible:
```bash
# Initialize weights (one-time)
python3 sac_ltc_agent.py --init-weights
```

**Step 3:** Use naturally in conversation:
```
You: "Check my network"
You: "Why is my WiFi slow?"
You: "Start sentinel mode"
You: "Is someone behind that wall?"
You: "What's competing for my channel?"
You: "Where should I move for better signal?"
```

Or invoke directly: `/net-intel`

### Manus

**Step 1:** Add as a tool in your Manus agent configuration:
```yaml
tools:
  - name: net-intel
    description: "Wireless network intelligence — diagnose, optimize, and monitor Wi-Fi/hotspot connectivity"
    type: shell
    commands:
      scan: |
        python3 /path/to/sac_ltc_agent.py --explain "$(python3 -c "
        import subprocess, re, json
        out = subprocess.run(['system_profiler', 'SPAirPortDataType'], capture_output=True, text=True).stdout
        # ... parse and output JSON array of network observations
        ")"
      sense: |
        python3 /path/to/sac_ltc_agent.py --sense "$(python3 -c "
        import subprocess, re, json, time
        # ... collect 30 seconds of RSSI samples and output JSON
        ")"
```

**Step 2:** Reference in your Manus agent's system prompt:
```
You have access to a wireless network intelligence tool called net-intel.
Use it when the user asks about their network, WiFi, internet speed,
or connectivity issues. It can also detect human presence through
WiFi signal analysis.
```

**Step 3:** The agent can call it like any other tool:
```
User: "My internet seems slow"
Agent: [calls net-intel scan] → gets JSON with scores, explanations, recommendations
Agent: "Your WiFi score is 65/100. The issue is channel congestion — 5 networks
        are sharing Channel 149. I recommend..."
```

### OpenClaw

**Step 1:** Register as a skill in your OpenClaw workspace:
```yaml
# openclaw_skills.yaml
skills:
  net-intel:
    name: "Agentic Liquid Wireless Manager"
    description: "AI-powered wireless network diagnostics, optimization, and sensing"
    trigger_phrases:
      - "check network"
      - "wifi slow"
      - "internet issues"
      - "someone nearby"
      - "signal strength"
    entry_point: "python3 sac_ltc_agent.py"
    args:
      scan: "--explain"
      sense: "--sense"
      test: "--test"
    working_directory: "/path/to/Agentic-Liquid-Wireless-Manager"
    requirements:
      - torch
      - numpy
```

**Step 2:** Add to your OpenClaw agent's capabilities:
```python
# In your OpenClaw agent setup
from openclaw import Agent, Skill

agent = Agent(
    skills=[
        Skill.from_yaml("openclaw_skills.yaml", "net-intel")
    ]
)
```

**Step 3:** The agent automatically invokes the skill when relevant topics arise.

### Any LLM Agent (Generic Integration)

The system is a CLI tool. Any agent that can execute shell commands can use it:

```bash
# Quick scan with AI decision
python3 sac_ltc_agent.py --explain '[{"ssid":"MyWiFi","rssi_dbm":-47,...}]'

# Presence detection
python3 sac_ltc_agent.py --sense '[{"ssid":"AP1","rssi":-50},{"ssid":"AP1","rssi":-53},...]'

# Self-test
python3 sac_ltc_agent.py --test
```

Output is structured (JSON for `--decide`/`--sense`, formatted text for `--explain`/`--test`), parseable by any agent framework.

## Modes of Operation

| Mode | Trigger | What It Does | Resource Usage |
|------|---------|-------------|----------------|
| **Quick Scan** | "Check my network" | One-shot diagnosis, scoring, recommendations | ~15 seconds, 2MB network |
| **Monitor** | "Start monitoring" | Autonomous background optimization every 2 min | <1% CPU, 2MB/cycle |
| **Sentinel** | "Sentinel mode" | Live presence detection + RF awareness every 10s | <2% CPU, 0 network (passive) |
| **Query** | "Why was my internet slow at 3pm?" | Answers from monitoring history + SAC-LTC reasoning | Read-only, instant |

## Training Data and Physics Models

The agent is NOT trained on a static dataset. Each training episode generates a **physics-grounded wireless environment** using real propagation models:

| Model | Standard | What It Computes |
|-------|----------|-----------------|
| Indoor path loss | ITU-R P.1238 | RSSI from distance, frequency, wall count |
| Rain attenuation | ITU-R P.838-3 | Signal loss from precipitation |
| Urban path loss | 3GPP TR 38.901 | Cellular path loss (7-24 GHz) |
| Channel capacity | Shannon-Hartley | Theoretical throughput from SNR + bandwidth |
| Thermal noise | Boltzmann (kTB) | Noise floor from bandwidth and temperature |

Full training details, data samples, and reproducibility instructions: [docs/TRAINING_PIPELINE.md](docs/TRAINING_PIPELINE.md)

Sample training data exported to: [`data/`](data/)

## Permissions and Controls

**Full disclosure** — every permission, every command, every data flow is documented:
[docs/PERMISSIONS_AND_CONTROLS.md](docs/PERMISSIONS_AND_CONTROLS.md)

Key principles:
- **Client-only.** Zero access to or control over any access point.
- **Passive-first.** Network scanning reads public beacon signals. No probing.
- **Local-only.** No data leaves the device. No cloud, no telemetry, no phone-home.
- **Reversible.** Every optimization action can be undone.
- **Transparent.** Every admin command is documented with exact syntax.
- **Consent-based.** Sentinel mode requires explicit user activation.

## Documentation

| Document | Contents |
|----------|----------|
| [SAC_LTC_ARCHITECTURE.md](docs/SAC_LTC_ARCHITECTURE.md) | Complete neural architecture — LTC cell ODE, KAN layers, twin critics, SAC algorithm, every parameter |
| [TRAINING_PIPELINE.md](docs/TRAINING_PIPELINE.md) | Physics models, environment generation, reward function, training results, reproducibility |
| [PERMISSIONS_AND_CONTROLS.md](docs/PERMISSIONS_AND_CONTROLS.md) | Every permission needed, every command used, data storage, privacy policy |
| [RF_ENVIRONMENT_INTELLIGENCE.md](docs/RF_ENVIRONMENT_INTELLIGENCE.md) | Congestion analysis, interference classification, hotspot intelligence, presence detection, distance estimation |
| [CROSS_PLATFORM_COMMANDS.md](docs/CROSS_PLATFORM_COMMANDS.md) | Every OS-specific command with exact syntax for macOS, Linux, Windows |
| [SKILL.md](SKILL.md) | The complete Claude Code skill file |

## Repository Structure

```
Agentic-Liquid-Wireless-Manager/
  README.md                          This file
  LICENSE                            MIT License
  CONTRIBUTING.md                    Contribution guidelines
  SKILL.md                           Claude Code skill (all modes)
  sac_ltc_agent.py                   Lightweight inference agent (~500 lines)
  train_sac_ltc.py                   Training pipeline (~600 lines)
  data/
    sample_environment.json           One training environment (5 networks)
    sample_episode.json               One complete training episode (20 steps)
    training_log.json                 Full training results and hyperparameters
  docs/
    SAC_LTC_ARCHITECTURE.md           Complete architecture disclosure
    TRAINING_PIPELINE.md              Training data and physics models
    PERMISSIONS_AND_CONTROLS.md       Full permissions disclosure
    RF_ENVIRONMENT_INTELLIGENCE.md    RF analysis techniques
    CROSS_PLATFORM_COMMANDS.md        OS-specific command reference
```

## Hardware Requirements

```yaml
required:
  wifi_adapter: "Built-in or USB Wi-Fi adapter"
  internet: "Wi-Fi or mobile hotspot connection"
  terminal: "Bash (macOS/Linux) or PowerShell (Windows)"
  python: "3.8+"

optional:
  pytorch: "For SAC-LTC AI decisions (falls back to heuristics without)"
  numpy: "For numerical operations"
  admin_access: "For DNS optimization, DHCP renewal, adapter restart"

resource_budget:
  cpu: "< 2% (inference ~0.5ms per decision)"
  ram: "< 30 MB (PyTorch + 17K parameter model)"
  gpu: "None required"
  disk: "< 2 MB (weights + history)"
  network: "< 2 MB per scan cycle"
```

## Contributing

We welcome contributions. Please see [CONTRIBUTING.md](CONTRIBUTING.md).

Key areas:
- Cross-platform testing (especially Windows and various Linux distros)
- Training improvements (more episodes, curriculum learning, real-world validation)
- New interference signatures (add detection patterns for more device types)
- Spatial calibration improvements (better directional accuracy)
- Integration guides for more agentic AI platforms

## License

MIT License. See [LICENSE](LICENSE).

## Credits

- Derived from [PreceptualAI UHCI](https://github.com/Danielfoojunwei/PreceptualAI-Universal-Heterogeneous-Connectivity-Intelligence-UHCI-) by Daniel Foo Jun Wei
- SAC-LTC architecture inspired by Hasani et al. "Liquid Time-constant Networks" (2021) and the UHCI universal spectrum agent
- KAN layers based on Liu et al. "KAN: Kolmogorov-Arnold Networks" (2024)
- RF physics from ITU-R P.1238, ITU-R P.838-3, 3GPP TR 38.901


path: train_sac_ltc.py
content: #!/usr/bin/env python3
"""
Real SAC-LTC Training Pipeline for Network Switching
=====================================================
Trains the Soft Actor-Critic with Liquid Time-Constant cells
for intelligent Wi-Fi / cellular hotspot switching decisions.

Uses real RF physics models (ITU-R propagation, 3GPP path loss)
to generate physically grounded training environments — not mocks.

Usage:
    python3 train_sac_ltc.py                    # Train with defaults
    python3 train_sac_ltc.py --episodes 5000    # Custom episode count
    python3 train_sac_ltc.py --eval             # Evaluate trained model
"""

import json, math, os, random, sys, time
from pathlib import Path
from collections import deque

import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

DATA_DIR = Path.home() / ".net-intel"
DEVICE = torch.device("cpu")  # Intentionally CPU — model is tiny, GPU overhead not worth it

# ============================================================================
# RF PHYSICS — Real propagation models from ITU-R and 3GPP standards
# ============================================================================

class RFPhysics:
    """ITU-R and 3GPP propagation models for realistic training environments."""

    @staticmethod
    def free_space_path_loss(freq_ghz, dist_m):
        """ITU-R free-space path loss (dB). FSPL = 20log10(d) + 20log10(f) + 32.44"""
        if dist_m < 1:
            dist_m = 1
        return 20 * math.log10(dist_m) + 20 * math.log10(freq_ghz * 1000) + 32.44

    @staticmethod
    def indoor_path_loss_itu(freq_ghz, dist_m, n_walls=0, wall_loss_db=5.0):
        """ITU-R P.1238 indoor propagation. Loss = FSPL + wall_penetration + floor_factor."""
        fspl = RFPhysics.free_space_path_loss(freq_ghz, dist_m)
        # Distance power loss coefficient for residential
        n = 28 if freq_ghz < 3 else 30  # Path loss exponent
        excess = max(0, 10 * (n / 20 - 1) * math.log10(max(1, dist_m)))
        wall = n_walls * wall_loss_db
        return fspl + excess + wall

    @staticmethod
    def uma_nlos_3gpp(freq_ghz, dist_m):
        """3GPP TR 38.901 UMa NLOS path loss (7-24 GHz range)."""
        if dist_m < 10:
            dist_m = 10
        return 13.54 + 39.08 * math.log10(dist_m) + 20 * math.log10(freq_ghz)

    @staticmethod
    def rain_attenuation_itu(freq_ghz, rain_rate_mmh, path_km):
        """ITU-R P.838-3 rain attenuation. gamma_R = k * R^alpha."""
        # Coefficients for horizontal polarization (simplified)
        if freq_ghz < 5:
            k, alpha = 0.000387, 0.912
        elif freq_ghz < 10:
            k, alpha = 0.00887, 1.264
        elif freq_ghz < 30:
            k, alpha = 0.167, 1.032
        else:
            k, alpha = 0.751, 0.843
        gamma = k * (rain_rate_mmh ** alpha)
        return gamma * path_km

    @staticmethod
    def thermal_noise_dbm(bandwidth_mhz, temp_k=290):
        """Thermal noise floor = kTB."""
        k_boltz = 1.38e-23
        noise_w = k_boltz * temp_k * bandwidth_mhz * 1e6
        return 10 * math.log10(noise_w * 1000)  # Convert to dBm

    @staticmethod
    def shannon_capacity_mbps(bandwidth_mhz, snr_db):
        """Shannon capacity: C = BW * log2(1 + SNR)."""
        snr_linear = 10 ** (snr_db / 10)
        return bandwidth_mhz * math.log2(1 + snr_linear)


# ============================================================================
# ENVIRONMENT — Realistic RF Environment with multiple networks
# ============================================================================

class RFEnvironment:
    """Simulates a realistic wireless environment with physics-grounded propagation.

    NOT a mock — uses real ITU-R and 3GPP models for path loss, noise, and capacity.
    Models real-world phenomena: congestion patterns, interference, movement, hotspot behavior.
    """

    def __init__(self, n_networks=5, seed=None):
        if seed is not None:
            random.seed(seed)
            np.random.seed(seed)
        self.n_networks = n_networks
        self.step_count = 0
        self.time_of_day = random.uniform(0, 24)  # hours
        self._generate_environment()

    def _generate_environment(self):
        """Create a physically realistic set of networks."""
        self.networks = []
        # User position (origin)
        self.user_x, self.user_y = 0.0, 0.0
        for i in range(self.n_networks):
            is_hotspot = random.random() < 0.3  # 30% chance of hotspot
            if is_hotspot:
                # Hotspot: closer, mobile, variable backhaul
                dist = random.uniform(1, 8)  # 1-8 meters
                freq = 2.4 if random.random() < 0.3 else 5.0  # Most hotspots on 5GHz
                bandwidth = random.choice([20, 40, 80])
                # Cellular backhaul characteristics
                cell_gen = random.choice(["3g", "4g", "4g", "5g", "5g"])
                if cell_gen == "3g":
                    backhaul_mbps = random.uniform(1, 5)
                    backhaul_latency = random.uniform(80, 200)
                elif cell_gen == "4g":
                    backhaul_mbps = random.uniform(10, 50)
                    backhaul_latency = random.uniform(20, 60)
                else:  # 5g
                    backhaul_mbps = random.uniform(50, 300)
                    backhaul_latency = random.uniform(8, 25)
            else:
                # Infrastructure AP
                dist = random.uniform(3, 30)  # 3-30 meters
                freq = random.choice([2.4, 5.0, 5.0])  # Bias toward 5GHz
                bandwidth = random.choice([20, 40, 80, 160])
                backhaul_mbps = random.uniform(100, 1000)  # Wired backhaul
                backhaul_latency = random.uniform(2, 10)
                cell_gen = None
            # Channel assignment (realistic — clustered on popular channels)
            if freq == 2.4:
                channel = random.choice([1, 1, 6, 6, 11, 11, 1, 6, 3, 9])
            else:
                channel = random.choice([36, 44, 149, 149, 149, 153, 157, 161])
            # AP position (angle and distance from user)
            angle = random.uniform(0, 2 * math.pi)
            tx_power_dbm = random.uniform(15, 23) if not is_hotspot else random.uniform(10, 18)
            # Number of other networks on same channel (will be computed dynamically)
            n_walls = random.randint(0, 3) if dist > 5 else 0
            self.networks.append({
                "id": i,
                "ssid": f"{'Hotspot' if is_hotspot else 'AP'}_{i}",
                "is_hotspot": is_hotspot,
                "cell_gen": cell_gen,
                "freq_ghz": freq,
                "channel": channel,
                "bandwidth_mhz": bandwidth,
                "distance_m": dist,
                "angle_rad": angle,
                "tx_power_dbm": tx_power_dbm,
                "n_walls": n_walls,
                "backhaul_mbps": backhaul_mbps,
                "backhaul_latency_ms": backhaul_latency,
                "movement_speed": random.uniform(0.5, 2.0) if is_hotspot else 0,
                "noise_figure_db": random.uniform(3, 7),
            })

    def _compute_rssi(self, net, extra_loss_db=0):
        """Compute RSSI using real propagation model."""
        path_loss = RFPhysics.indoor_path_loss_itu(
            net["freq_ghz"], net["distance_m"],
            n_walls=net["n_walls"]
        )
        rssi = net["tx_power_dbm"] - path_loss - extra_loss_db
        return max(-100, min(-20, rssi))

    def _compute_noise(self, net):
        """Compute noise floor from thermal + interference."""
        thermal = RFPhysics.thermal_noise_dbm(net["bandwidth_mhz"])
        # Co-channel interference from other networks on same channel
        co_channel = sum(1 for n in self.networks
                        if n["channel"] == net["channel"] and n["id"] != net["id"])
        interference = co_channel * 3  # ~3dB per co-channel interferer
        return thermal + net["noise_figure_db"] + interference

    def _congestion_factor(self, net):
        """Count co-channel and adjacent-channel networks."""
        co = sum(1 for n in self.networks
                 if n["channel"] == net["channel"] and n["id"] != net["id"])
        adj = sum(1 for n in self.networks
                  if abs(n["channel"] - net["channel"]) <= 2
                  and n["channel"] != net["channel"]
                  and n["freq_ghz"] == net["freq_ghz"])
        return co + adj * 0.5

    def step(self):
        """Advance environment by one timestep (~30 seconds).

        Returns observations for all networks as list of dicts.
        """
        self.step_count += 1
        self.time_of_day = (self.time_of_day + 30 / 3600) % 24  # +30s

        # Time-of-day congestion pattern (real: peaks at 6-10pm)
        hour = self.time_of_day
        congestion_multiplier = 1.0 + 0.5 * math.exp(-((hour - 20) ** 2) / 8)

        # Random events
        has_movement = random.random() < 0.15  # 15% chance someone walks by
        has_interference = random.random() < 0.05  # 5% chance of interference event
        interference_band = 2.4 if random.random() < 0.7 else 5.0

        observations = []
        for net in self.networks:
            # Hotspot movement — distance changes over time
            if net["is_hotspot"] and net["movement_speed"] > 0:
                net["distance_m"] += random.gauss(0, 0.3)
                net["distance_m"] = max(1, min(15, net["distance_m"]))

            # Human movement causes temporary RSSI fluctuation
            body_loss = 0
            if has_movement:
                # Body absorption: 3-12 dB depending on proximity to signal path
                body_loss = random.uniform(3, 12) * math.exp(-net["distance_m"] / 10)

            # Compute RF metrics
            rssi = self._compute_rssi(net, extra_loss_db=body_loss)
            noise = self._compute_noise(net)
            # Add interference event
            if has_interference and abs(net["freq_ghz"] - interference_band) < 0.5:
                noise += random.uniform(5, 15)
            snr = rssi - noise

            # Throughput from Shannon capacity, limited by backhaul
            capacity = RFPhysics.shannon_capacity_mbps(net["bandwidth_mhz"], max(0, snr))
            # Congestion reduces effective throughput
            cong = self._congestion_factor(net) * congestion_multiplier
            effective_throughput = capacity / max(1, 1 + cong * 0.3)
            # Bottleneck is min of wireless capacity and backhaul
            throughput = min(effective_throughput, net["backhaul_mbps"])
            throughput = max(0.1, throughput + random.gauss(0, throughput * 0.05))

            # Latency: propagation + processing + congestion + backhaul
            base_latency = 2 + net["distance_m"] * 0.01  # ~2ms base
            congestion_latency = cong * random.uniform(2, 8)
            backhaul_lat = net["backhaul_latency_ms"]
            latency = base_latency + congestion_latency + backhaul_lat
            latency = max(1, latency + random.gauss(0, latency * 0.1))

            # Packet loss from congestion and poor SNR
            loss = 0
            if snr < 10:
                loss += random.uniform(1, 5)
            if cong > 5:
                loss += random.uniform(0.5, 3)
            loss = max(0, min(50, loss + random.gauss(0, 0.3)))

            observations.append({
                "id": net["id"],
                "ssid": net["ssid"],
                "is_hotspot": net["is_hotspot"],
                "cell_gen": net["cell_gen"],
                "rssi_dbm": round(rssi, 1),
                "noise_dbm": round(noise, 1),
                "snr_db": round(snr, 1),
                "latency_ms": round(latency, 1),
                "throughput_mbps": round(throughput, 2),
                "packet_loss_pct": round(loss, 2),
                "channel": net["channel"],
                "freq_ghz": net["freq_ghz"],
                "bandwidth_mhz": net["bandwidth_mhz"],
                "n_networks_on_channel": int(self._congestion_factor(net)),
                "is_5ghz": net["freq_ghz"] >= 5.0,
                "has_movement": has_movement,
                "has_interference": has_interference,
            })

        return observations

    def get_reward(self, action, observations):
        """Compute reward for choosing a network.

        Reward function designed to encourage:
          - High throughput
          - Low latency
          - Low packet loss
          - Stability (not switching too often)
        """
        if action >= len(observations):
            return -10.0  # Invalid action
        obs = observations[action]
        # Throughput reward (log scale to prevent domination by very fast networks)
        throughput_r = math.log2(max(1, obs["throughput_mbps"])) / 10
        # Latency penalty
        latency_r = -obs["latency_ms"] / 200
        # Loss penalty
        loss_r = -obs["packet_loss_pct"] / 5
        # Signal quality bonus
        snr_r = max(0, obs["snr_db"]) / 60
        return throughput_r + latency_r + loss_r + snr_r


# ============================================================================
# PyTorch SAC-LTC Model
# ============================================================================

class TorchLTCCell(nn.Module):
    """Liquid Time-Constant cell in PyTorch."""
    def __init__(self, input_dim, hidden_dim):
        super().__init__()
        self.hidden_dim = hidden_dim
        self.W_x = nn.Linear(input_dim, hidden_dim)
        self.W_h = nn.Linear(hidden_dim, hidden_dim, bias=False)
        self.W_tau = nn.Linear(input_dim, hidden_dim)
        self.tau_base = nn.Parameter(torch.ones(hidden_dim))
        self.dt = 1.0
        self._init_weights()

    def _init_weights(self):
        nn.init.xavier_uniform_(self.W_x.weight)
        nn.init.xavier_uniform_(self.W_h.weight)
        nn.init.xavier_uniform_(self.W_tau.weight)
        nn.init.zeros_(self.W_x.bias)
        nn.init.zeros_(self.W_tau.bias)

    def forward(self, x, h):
        """x: (B, input_dim), h: (B, hidden_dim) → h_new: (B, hidden_dim), tau: (B, hidden_dim)"""
        f = torch.tanh(self.W_x(x) + self.W_h(h))
        tau = self.tau_base + F.softplus(self.W_tau(x))
        h_new = h + (self.dt / tau) * (-h + f)
        return h_new, tau


class TorchKANLinear(nn.Module):
    """KAN layer: base(SiLU) + spline(RBF)."""
    def __init__(self, in_dim, out_dim, grid_size=5):
        super().__init__()
        self.in_dim = in_dim
        self.out_dim = out_dim
        self.grid_size = grid_size
        n_basis = grid_size + 3
        self.base_weight = nn.Linear(in_dim, out_dim)
        self.spline_weight = nn.Parameter(torch.randn(in_dim, n_basis, out_dim) * 0.1)
        self.register_buffer("grid", torch.linspace(-1, 1, n_basis))

    def forward(self, x):
        # Base: SiLU + linear
        base = self.base_weight(F.silu(x))
        # Spline: RBF basis
        # x: (B, in_dim) → expand for grid: (B, in_dim, 1)
        x_expand = x.unsqueeze(-1)  # (B, in_dim, 1)
        basis = torch.exp(-0.5 * ((x_expand - self.grid) / 0.3) ** 2)  # (B, in_dim, n_basis)
        spline = torch.einsum("bin,ino->bo", basis, self.spline_weight)
        return base + spline


class SACLTCAgent(nn.Module):
    """Full SAC-LTC agent with actor and twin critics."""
    def __init__(self, n_features=8, hidden_dim=32, kan_hidden=16, n_actions=5):
        super().__init__()
        self.n_actions = n_actions
        self.hidden_dim = hidden_dim
        # Encoder
        self.ltc = TorchLTCCell(n_features, hidden_dim)
        # Actor (KAN)
        self.kan1 = TorchKANLinear(hidden_dim, kan_hidden)
        self.ln1 = nn.LayerNorm(kan_hidden)
        self.kan2 = TorchKANLinear(kan_hidden, n_actions)
        # Twin critics
        self.q1 = nn.Sequential(
            nn.Linear(hidden_dim, 64), nn.LayerNorm(64), nn.SiLU(),
            nn.Linear(64, n_actions)
        )
        self.q2 = nn.Sequential(
            nn.Linear(hidden_dim, 64), nn.LayerNorm(64), nn.SiLU(),
            nn.Linear(64, n_actions)
        )
        # Target critics
        self.q1_target = nn.Sequential(
            nn.Linear(hidden_dim, 64), nn.LayerNorm(64), nn.SiLU(),
            nn.Linear(64, n_actions)
        )
        self.q2_target = nn.Sequential(
            nn.Linear(hidden_dim, 64), nn.LayerNorm(64), nn.SiLU(),
            nn.Linear(64, n_actions)
        )
        self.q1_target.load_state_dict(self.q1.state_dict())
        self.q2_target.load_state_dict(self.q2.state_dict())
        # Entropy temperature
        self.log_alpha = nn.Parameter(torch.tensor(0.0))
        self.target_entropy = -0.98 * math.log(1.0 / n_actions)

    def actor(self, h):
        """h: (B, hidden_dim) → probs: (B, n_actions)"""
        x = self.kan1(h)
        x = self.ln1(x)
        logits = self.kan2(x)
        return F.softmax(logits, dim=-1)

    def forward(self, x_seq, h=None):
        """Process sequence through LTC then actor.

        x_seq: (B, T, n_features)
        Returns: probs, h_final, all_tau
        """
        B, T, _ = x_seq.shape
        if h is None:
            h = torch.zeros(B, self.hidden_dim, device=x_seq.device)
        all_tau = []
        for t in range(T):
            h, tau = self.ltc(x_seq[:, t, :], h)
            all_tau.append(tau)
        probs = self.actor(h)
        return probs, h, all_tau


# ============================================================================
# REPLAY BUFFER
# ============================================================================

class ReplayBuffer:
    def __init__(self, capacity=50000):
        self.buffer = deque(maxlen=capacity)

    def push(self, state_seq, action, reward, next_state_seq, done):
        self.buffer.append((state_seq, action, reward, next_state_seq, done))

    def sample(self, batch_size):
        batch = random.sample(self.buffer, min(batch_size, len(self.buffer)))
        states, actions, rewards, next_states, dones = zip(*batch)
        return (
            torch.tensor(np.array(states), dtype=torch.float32),
            torch.tensor(actions, dtype=torch.long),
            torch.tensor(rewards, dtype=torch.float32),
            torch.tensor(np.array(next_states), dtype=torch.float32),
            torch.tensor(dones, dtype=torch.float32),
        )

    def __len__(self):
        return len(self.buffer)


# ============================================================================
# TRAINING LOOP
# ============================================================================

def normalize_features(obs):
    """Convert raw observation dict to normalized feature vector."""
    rssi = obs["rssi_dbm"]
    noise = obs["noise_dbm"]
    return [
        max(0, min(1, (rssi + 100) / 60)),
        max(0, min(1, (rssi - noise) / 60)),
        max(0, min(1, 1 - (obs["latency_ms"] / 200))),
        max(0, min(1, obs["throughput_mbps"] / 100)),
        max(0, min(1, 1 - (obs["packet_loss_pct"] / 10))),
        max(0, min(1, 1 - (obs["n_networks_on_channel"] / 10))),
        1.0 if obs["is_5ghz"] else 0.0,
        1.0 if obs["is_hotspot"] else 0.0,
    ]


def train(episodes=2000, seq_len=8, batch_size=128, gamma=0.99, tau_polyak=0.005, lr=3e-4):
    n_networks = 5
    agent = SACLTCAgent(n_features=8, hidden_dim=32, kan_hidden=16, n_actions=n_networks).to(DEVICE)

    actor_params = list(agent.ltc.parameters()) + list(agent.kan1.parameters()) + \
                   list(agent.ln1.parameters()) + list(agent.kan2.parameters())
    critic_params = list(agent.q1.parameters()) + list(agent.q2.parameters())

    actor_opt = optim.Adam(actor_params, lr=lr)
    critic_opt = optim.Adam(critic_params, lr=lr)
    alpha_opt = optim.Adam([agent.log_alpha], lr=lr)

    buffer = ReplayBuffer(capacity=50000)
    best_reward = -float("inf")
    reward_history = []

    print(f"Training SAC-LTC: {episodes} episodes, {n_networks} networks")
    print(f"Model parameters: {sum(p.numel() for p in agent.parameters()):,}")
    start_time = time.time()

    for ep in range(episodes):
        env = RFEnvironment(n_networks=n_networks, seed=ep)
        state_history = deque(maxlen=seq_len)
        ep_reward = 0
        current_action = 0

        # Warm up state history
        for _ in range(seq_len):
            obs_list = env.step()
            features = [normalize_features(o) for o in obs_list]
            # Use best-scoring network's features as state
            state_history.append(features[current_action])

        for step in range(100):  # 100 steps per episode (~50 minutes simulated)
            obs_list = env.step()
            state_seq = np.array(list(state_history))  # (seq_len, n_features)

            # Get action from policy
            with torch.no_grad():
                s = torch.tensor(state_seq, dtype=torch.float32).unsqueeze(0)
                probs, _, _ = agent(s)
                dist = torch.distributions.Categorical(probs)
                action = dist.sample().item()

            # Get reward
            reward = env.get_reward(action, obs_list)
            # Switching penalty (hysteresis)
            if action != current_action:
                reward -= 0.3  # Small penalty for switching
            current_action = action

            # Next state
            features = [normalize_features(o) for o in obs_list]
            state_history.append(features[action])
            next_state_seq = np.array(list(state_history))

            done = step == 99
            buffer.push(state_seq, action, reward, next_state_seq, float(done))
            ep_reward += reward

            # Train if enough data
            if len(buffer) >= batch_size:
                states, actions, rewards_b, next_states, dones = buffer.sample(batch_size)

                # Critic update
                with torch.no_grad():
                    next_probs, _, _ = agent(next_states)
                    next_q1 = agent.q1_target(agent.ltc(next_states[:, -1, :],
                               torch.zeros(batch_size, agent.hidden_dim))[0])
                    next_q2 = agent.q2_target(agent.ltc(next_states[:, -1, :],
                               torch.zeros(batch_size, agent.hidden_dim))[0])
                    next_q_min = torch.min(next_q1, next_q2)
                    alpha = agent.log_alpha.exp()
                    next_v = (next_probs * (next_q_min - alpha * torch.log(next_probs + 1e-8))).sum(-1)
                    target_q = rewards_b + gamma * (1 - dones) * next_v

                # Process states through LTC
                _, h_final, _ = agent(states)
                q1_vals = agent.q1(h_final).gather(1, actions.unsqueeze(1)).squeeze()
                q2_vals = agent.q2(h_final).gather(1, actions.unsqueeze(1)).squeeze()
                critic_loss = F.mse_loss(q1_vals, target_q) + F.mse_loss(q2_vals, target_q)

                critic_opt.zero_grad()
                critic_loss.backward()
                torch.nn.utils.clip_grad_norm_(critic_params, 1.0)
                critic_opt.step()

                # Actor update
                probs_train, h_train, _ = agent(states)
                q1_train = agent.q1(h_train.detach())
                q2_train = agent.q2(h_train.detach())
                q_min = torch.min(q1_train, q2_train)
                alpha = agent.log_alpha.exp().detach()
                log_probs = torch.log(probs_train + 1e-8)
                actor_loss = (probs_train * (alpha * log_probs - q_min)).sum(-1).mean()

                actor_opt.zero_grad()
                actor_loss.backward()
                torch.nn.utils.clip_grad_norm_(actor_params, 1.0)
                actor_opt.step()

                # Alpha (entropy temperature) update
                entropy = -(probs_train.detach() * log_probs.detach()).sum(-1).mean()
                alpha_loss = agent.log_alpha * (entropy - agent.target_entropy)
                alpha_opt.zero_grad()
                alpha_loss.backward()
                alpha_opt.step()

                # Soft update target networks
                with torch.no_grad():
                    for p, p_targ in zip(agent.q1.parameters(), agent.q1_target.parameters()):
                        p_targ.data.mul_(1 - tau_polyak).add_(p.data, alpha=tau_polyak)
                    for p, p_targ in zip(agent.q2.parameters(), agent.q2_target.parameters()):
                        p_targ.data.mul_(1 - tau_polyak).add_(p.data, alpha=tau_polyak)

        reward_history.append(ep_reward)

        if ep_reward > best_reward:
            best_reward = ep_reward

        if (ep + 1) % 100 == 0:
            avg_r = np.mean(reward_history[-100:])
            elapsed = time.time() - start_time
            print(f"  Episode {ep+1}/{episodes} | Avg reward: {avg_r:.2f} | "
                  f"Best: {best_reward:.2f} | Alpha: {agent.log_alpha.exp().item():.3f} | "
                  f"Time: {elapsed:.0f}s")

    # Save trained weights
    save_path = DATA_DIR / "trained_model.pt"
    torch.save(agent.state_dict(), str(save_path))
    print(f"\nModel saved to {save_path}")

    # Export to numpy-compatible JSON for the lightweight agent
    export_to_numpy_weights(agent, str(DATA_DIR / "weights.json"))
    print(f"Numpy weights exported to {DATA_DIR / 'weights.json'}")

    return agent, reward_history


def export_to_numpy_weights(agent, path):
    """Export PyTorch model weights to JSON format for the numpy agent."""
    with torch.no_grad():
        data = {
            "ltc": {
                "W_x": agent.ltc.W_x.weight.t().tolist(),  # Transpose for our convention
                "W_h": agent.ltc.W_h.weight.t().tolist(),
                "b": agent.ltc.W_x.bias.tolist(),
                "W_tau": agent.ltc.W_tau.weight.t().tolist(),
                "b_tau": agent.ltc.W_tau.bias.tolist(),
                "tau_base": agent.ltc.tau_base.tolist(),
            },
            "kan1": {
                "base_weight": agent.kan1.base_weight.weight.t().tolist(),
                "spline_weight": agent.kan1.spline_weight.tolist(),
            },
            "kan2": {
                "base_weight": agent.kan2.base_weight.weight.t().tolist(),
                "spline_weight": agent.kan2.spline_weight.tolist(),
            },
            "hidden": [0.0] * agent.hidden_dim,
        }
    os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
    with open(path, "w") as f:
        json.dump(data, f)


def evaluate(n_episodes=50):
    """Evaluate trained model."""
    model_path = DATA_DIR / "trained_model.pt"
    if not model_path.exists():
        print("No trained model found. Run training first.")
        sys.exit(1)

    agent = SACLTCAgent()
    agent.load_state_dict(torch.load(str(model_path), weights_only=True))
    agent.eval()

    total_rewards = []
    for ep in range(n_episodes):
        env = RFEnvironment(n_networks=5, seed=10000 + ep)
        state_history = deque(maxlen=8)
        ep_reward = 0
        current_action = 0

        for _ in range(8):
            obs_list = env.step()
            features = [normalize_features(o) for o in obs_list]
            state_history.append(features[current_action])

        for step in range(100):
            obs_list = env.step()
            state_seq = np.array(list(state_history))
            with torch.no_grad():
                s = torch.tensor(state_seq, dtype=torch.float32).unsqueeze(0)
                probs, _, _ = agent(s)
                action = probs.argmax(dim=-1).item()
            reward = env.get_reward(action, obs_list)
            if action != current_action:
                reward -= 0.3
            current_action = action
            features = [normalize_features(o) for o in obs_list]
            state_history.append(features[action])
            ep_reward += reward

        total_rewards.append(ep_reward)

    print(f"\nEvaluation Results ({n_episodes} episodes):")
    print(f"  Mean reward:   {np.mean(total_rewards):.2f}")
    print(f"  Std reward:    {np.std(total_rewards):.2f}")
    print(f"  Min reward:    {np.min(total_rewards):.2f}")
    print(f"  Max reward:    {np.max(total_rewards):.2f}")

    # Random baseline
    random_rewards = []
    for ep in range(n_episodes):
        env = RFEnvironment(n_networks=5, seed=10000 + ep)
        ep_reward = 0
        current_action = 0
        for _ in range(8):
            env.step()
        for step in range(100):
            obs_list = env.step()
            action = random.randint(0, 4)
            reward = env.get_reward(action, obs_list)
            if action != current_action:
                reward -= 0.3
            current_action = action
            ep_reward += reward
        random_rewards.append(ep_reward)

    print(f"\nRandom Baseline ({n_episodes} episodes):")
    print(f"  Mean reward:   {np.mean(random_rewards):.2f}")
    improvement = ((np.mean(total_rewards) - np.mean(random_rewards)) /
                   abs(np.mean(random_rewards))) * 100
    print(f"\nSAC-LTC improvement over random: {improvement:.1f}%")


if __name__ == "__main__":
    DATA_DIR.mkdir(parents=True, exist_ok=True)

    if "--eval" in sys.argv:
        evaluate()
    else:
        episodes = 2000
        for i, arg in enumerate(sys.argv):
            if arg == "--episodes" and i + 1 < len(sys.argv):
                episodes = int(sys.argv[i + 1])
        train(episodes=episodes)


path: data/sample_environment.json
content: {
  "description": "One training environment configuration with 5 networks generated from physics models",
  "networks": [
    {
      "id": 0,
      "ssid": "Hotspot_0",
      "is_hotspot": true,
      "cell_gen": "3g",
      "freq_ghz": 2.4,
      "channel": 1,
      "bandwidth_mhz": 80,
      "distance_m": 2.925,
      "angle_rad": 3.71,
      "tx_power_dbm": 10.254,
      "n_walls": 0,
      "backhaul_mbps": 3.707,
      "backhaul_latency_ms": 187.062,
      "movement_speed": 0.641,
      "noise_figure_db": 3.931
    },
    {
      "id": 1,
      "ssid": "AP_1",
      "is_hotspot": false,
      "cell_gen": null,
      "freq_ghz": 5.0,
      "channel": 36,
      "bandwidth_mhz": 160,
      "distance_m": 18.154,
      "angle_rad": 4.768,
      "tx_power_dbm": 16.277,
      "n_walls": 3,
      "backhaul_mbps": 298.397,
      "backhaul_latency_ms": 6.714,
      "movement_speed": 0,
      "noise_figure_db": 4.361
    },
    {
      "id": 2,
      "ssid": "Hotspot_2",
      "is_hotspot": true,
      "cell_gen": "5g",
      "freq_ghz": 5.0,
      "channel": 149,
      "bandwidth_mhz": 20,
      "distance_m": 7.7,
      "angle_rad": 5.071,
      "tx_power_dbm": 15.838,
      "n_walls": 0,
      "backhaul_mbps": 74.179,
      "backhaul_latency_ms": 22.407,
      "movement_speed": 1.96,
      "noise_figure_db": 4.514
    },
    {
      "id": 3,
      "ssid": "AP_3",
      "is_hotspot": false,
      "cell_gen": null,
      "freq_ghz": 5.0,
      "channel": 36,
      "bandwidth_mhz": 80,
      "distance_m": 25.394,
      "angle_rad": 4.155,
      "tx_power_dbm": 21.185,
      "n_walls": 0,
      "backhaul_mbps": 619.617,
      "backhaul_latency_ms": 7.637,
      "movement_speed": 0,
      "noise_figure_db": 6.421
    },
    {
      "id": 4,
      "ssid": "AP_4",
      "is_hotspot": false,
      "cell_gen": null,
      "freq_ghz": 5.0,
      "channel": 149,
      "bandwidth_mhz": 80,
      "distance_m": 13.263,
      "angle_rad": 4.41,
      "tx_power_dbm": 20.468,
      "n_walls": 0,
      "backhaul_mbps": 246.389,
      "backhaul_latency_ms": 4.842,
      "movement_speed": 0,
      "noise_figure_db": 5.437
    }
  ]
}

path: data/training_log.json
content: {
  "description": "Training results from 1000-episode SAC-LTC run",
  "hyperparameters": {
    "episodes": 1000,
    "steps_per_episode": 100,
    "batch_size": 128,
    "gamma": 0.99,
    "tau_polyak": 0.005,
    "learning_rate": 0.0003,
    "replay_buffer_size": 50000,
    "gradient_clip": 1.0,
    "sequence_length": 8,
    "hidden_dim": 32,
    "kan_hidden": 16,
    "n_actions": 5,
    "n_features": 8,
    "switch_penalty": 0.3,
    "target_entropy_ratio": 0.98
  },
  "results": {
    "total_parameters": 17274,
    "training_time_seconds": 536,
    "hardware": "Apple M1, CPU only",
    "pytorch_version": "2.8.0",
    "checkpoints": [
      {
        "episode": 100,
        "avg_reward": -43.8,
        "best": -21.56,
        "alpha": 0.38
      },
      {
        "episode": 200,
        "avg_reward": -43.11,
        "best": -21.56,
        "alpha": 0.231
      },
      {
        "episode": 300,
        "avg_reward": -45.5,
        "best": -21.56,
        "alpha": 0.233
      },
      {
        "episode": 400,
        "avg_reward": -45.89,
        "best": -17.35,
        "alpha": 0.211
      },
      {
        "episode": 500,
        "avg_reward": -43.14,
        "best": -17.35,
        "alpha": 0.204
      },
      {
        "episode": 600,
        "avg_reward": -43.62,
        "best": -17.35,
        "alpha": 0.217
      },
      {
        "episode": 700,
        "avg_reward": -43.55,
        "best": -17.35,
        "alpha": 0.194
      },
      {
        "episode": 800,
        "avg_reward": -46.61,
        "best": -17.35,
        "alpha": 0.219
      },
      {
        "episode": 900,
        "avg_reward": -43.49,
        "best": -17.35,
        "alpha": 0.185
      },
      {
        "episode": 1000,
        "avg_reward": -43.58,
        "best": -17.35,
        "alpha": 0.172
      }
    ],
    "evaluation_50_episodes": {
      "sac_ltc_mean": -27.05,
      "sac_ltc_std": 21.86,
      "sac_ltc_min": -106.55,
      "sac_ltc_max": 10.1,
      "random_mean": -42.43,
      "improvement_over_random": "36.2%"
    }
  }
}

path: package.json
content: {
  "name": "agentic-liquid-wireless-manager",
  "version": "1.0.0",
  "description": "AI-powered wireless network intelligence using SAC-LTC for agentic AI systems. Autonomous Wi-Fi/hotspot management with RF explainability, presence detection, and navigation guidance.",
  "main": "sac_ltc_agent.py",
  "scripts": {
    "test": "python3 sac_ltc_agent.py --test",
    "init": "python3 sac_ltc_agent.py --init-weights",
    "train": "python3 train_sac_ltc.py --episodes 2000",
    "train:fast": "python3 train_sac_ltc.py --episodes 500",
    "eval": "python3 train_sac_ltc.py --eval",
    "setup": "pip install torch numpy && python3 sac_ltc_agent.py --init-weights && python3 sac_ltc_agent.py --test"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/Danielfoojunwei/Agentic-Liquid-Wireless-Manager.git"
  },
  "keywords": [
    "wireless",
    "wifi",
    "network-optimization",
    "sac",
    "liquid-time-constant",
    "ltc",
    "kan",
    "reinforcement-learning",
    "agentic-ai",
    "rf-sensing",
    "presence-detection",
    "hotspot",
    "5g",
    "4g",
    "channel-congestion",
    "interference-detection",
    "cross-platform",
    "explainable-ai",
    "itu-r",
    "3gpp"
  ],
  "author": "Daniel Foo Jun Wei",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/Danielfoojunwei/Agentic-Liquid-Wireless-Manager/issues"
  },
  "homepage": "https://github.com/Danielfoojunwei/Agentic-Liquid-Wireless-Manager#readme",
  "engines": {
    "python": ">=3.8"
  },
  "dependencies": {},
  "pythonDependencies": {
    "torch": ">=2.0.0",
    "numpy": ">=1.24.0"
  }
}


path: CHANGELOG.md
content: # Changelog

All notable changes to Agentic Liquid Wireless Manager are documented here.

Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2026-04-05

### Added

**Core Agent**
- SAC-LTC (Soft Actor-Critic with Liquid Time-Constant) inference engine
- LTC cell with continuous-time ODE and input-dependent time constants
- KAN (Kolmogorov-Arnold Network) actor with interpretable spline activations
- Pure Python fallback (runs without PyTorch via numpy-only implementation)
- Feature normalization pipeline (8 RF features normalized to [0,1])
- Network scoring heuristic (0-100 composite score)

**Training Pipeline**
- Physics-grounded RF environment generation (not mocked/simulated)
- ITU-R P.1238 indoor propagation model
- ITU-R P.838-3 rain attenuation model
- 3GPP TR 38.901 UMa NLOS path loss model
- Shannon-Hartley channel capacity computation
- Thermal noise floor calculation (kTB)
- Soft Actor-Critic with twin critics, Polyak target networks, entropy tuning
- 17,274 parameter model trained in ~9 minutes on CPU
- 36.2% improvement over random baseline on 50 evaluation episodes

**Wireless Intelligence**
- Wi-Fi signal diagnostics (RSSI, noise, SNR, channel, PHY mode, MCS)
- Channel congestion mapping (co-channel and adjacent-channel analysis)
- Interference classification (microwave, Bluetooth, radar, neighboring APs)
- Hotspot detection via IP subnet analysis (iPhone 172.20.10.x, Android 192.168.43.x)
- Cellular generation fingerprinting (3G/4G/5G from latency/throughput signatures)
- Backhaul vs Wi-Fi hop quality separation
- RSSI-to-distance estimation using ITU-R indoor path loss model
- Competitor identification (which networks share your channel)
- Navigation guidance (direction and distance to better signal)

**Presence Detection**
- RSSI variance analysis for human movement detection
- Autocorrelation-based pattern classification (human vs appliance)
- Directional sensing via spatial calibration (body-blocking method)
- Confidence scoring with false-positive mitigation for periodic interference

**Explainability**
- Finite-difference feature attribution (which RF factors drove each decision)
- LTC time constant analysis (what the model is tracking: fast changes vs slow trends)
- Plain English output generation (layman-friendly explanations)
- Full technical deep-dive mode available on request

**Operating Modes**
- Quick Scan: one-shot diagnosis with scoring and recommendations
- Monitor Mode: autonomous background optimization (CronCreate, 2-min interval)
- Sentinel Mode: live spatial awareness with presence detection (10-sec interval)
- Query Mode: conversational answers from monitoring history + SAC-LTC reasoning

**Cross-Platform Support**
- macOS: system_profiler, networksetup, ifconfig
- Linux/Ubuntu: nmcli, iw, iwconfig, ip, resolvectl
- Windows: netsh wlan, Get-NetAdapter, PowerShell cmdlets

**Auto-Optimization Actions**
- DNS server switching (to fastest available)
- DNS cache flushing
- DHCP lease renewal
- Network switching (SAC-LTC confidence-gated)
- Adapter restart (on sustained packet loss)

**Documentation**
- SAC_LTC_ARCHITECTURE.md: Complete neural architecture disclosure
- TRAINING_PIPELINE.md: All physics models, data generation, training results
- PERMISSIONS_AND_CONTROLS.md: Every permission and command disclosed
- RF_ENVIRONMENT_INTELLIGENCE.md: All analysis techniques documented
- CROSS_PLATFORM_COMMANDS.md: Every OS command with exact syntax

**Data**
- sample_environment.json: One training environment (5 networks)
- sample_episode.json: One complete episode (20 steps with full observations)
- training_log.json: Hyperparameters and training results

**Plugin Metadata**
- plugin.yaml: Plugin manifest with type classification, host targets, OS targets
- package.json: npm-compatible project metadata with scripts
- CHANGELOG.md: This file
- LICENSE: MIT
- CONTRIBUTING.md: Contribution guidelines

**Host Target Integrations**
- Claude Code: SKILL.md with /net-intel invocation
- Manus: Tool YAML configuration
- OpenClaw: Skill registration YAML
- Generic LLM agents: CLI interface (--explain, --sense, --decide)

### Source

- Repository: https://github.com/Danielfoojunwei/Agentic-Liquid-Wireless-Manager
- Initial commit: `3d740ff`
- This release commit: `a323de3`
- Ref: `v1.0.0`
- Branch: `main`
- Derived from: [PreceptualAI UHCI](https://github.com/Danielfoojunwei/PreceptualAI-Universal-Heterogeneous-Connectivity-Intelligence-UHCI-)

[1.0.0]: https://github.com/Danielfoojunwei/Agentic-Liquid-Wireless-Manager/releases/tag/v1.0.0


path: docs/RF_ENVIRONMENT_INTELLIGENCE.md
content: # RF Environment Intelligence: How the System Understands Your Wireless World

The system turns raw signal data into a complete picture of the electromagnetic landscape around you. This document explains every analysis technique.

## What We Measure

From your laptop's Wi-Fi adapter (passive, read-only):

| Measurement | Source | What It Tells Us |
|------------|--------|-----------------|
| RSSI (dBm) | Beacon frames from each AP | How strong each signal is at your location |
| Noise floor (dBm) | Adapter's noise measurement | Background electromagnetic noise level |
| SNR (dB) | RSSI minus noise | How clearly your laptop can hear the AP |
| Channel number | Beacon frame | Which frequency each AP uses |
| Bandwidth (MHz) | Beacon HT/VHT/HE IEs | How wide each channel is (20/40/80/160) |
| PHY mode | Beacon capabilities | Wi-Fi generation (a/b/g/n/ac/ax) |
| BSSID | Beacon header | Unique AP identifier |
| Security | Beacon RSN IE | Encryption type |

From active tests (using existing connection only):

| Test | Method | What It Tells Us |
|------|--------|-----------------|
| Latency | ICMP ping (10 packets to 8.8.8.8) | Round-trip time to internet |
| Packet loss | Same ping test | Reliability of connection |
| DNS speed | dig queries to current and alternative DNS | Name resolution performance |
| Throughput | HTTP download of 1MB file | Actual download speed |

---

## Congestion Analysis

### Co-Channel Congestion (CCI)

When multiple APs share the same channel, they must take turns transmitting (CSMA/CA protocol). More APs = more waiting = slower speeds.

```
Detection:
  Count all visible BSSIDs on the same channel as your connection.

Impact estimation:
  1 network (you alone): 100% airtime available
  2 networks: ~50% airtime (moderate)
  3-4 networks: ~25-33% airtime (crowded)
  5+ networks: <20% airtime (very congested)

  Note: actual impact depends on traffic load of each network.
  Idle APs still send beacons but don't consume much airtime.
```

### Adjacent-Channel Interference (ACI)

Especially important on 2.4 GHz where only channels 1, 6, and 11 are non-overlapping.

```
2.4 GHz:
  Each channel is 22 MHz wide, spaced 5 MHz apart.
  Channel 1 (2412 MHz) overlaps with channels 2, 3, 4, 5.
  Only channels 1, 6, 11 are completely non-overlapping.
  A network on channel 3 interferes with BOTH channel 1 and channel 6.

5 GHz:
  Channels are wider but non-overlapping at 20 MHz.
  However, 80 MHz channels span 4x 20 MHz channels:
    Ch149 (80MHz) occupies channels 149, 153, 157, 161.
    A 20 MHz network on Ch153 sits inside your 80 MHz band.
```

### Channel Heat Map

The system builds a complete picture of every channel's congestion:

```
For each channel in the band:
  Count: number of networks
  Power: sum of all RSSI values (total interference power)
  Overlap: number of networks with overlapping bandwidth
  Score: lower is better (fewer networks, less power)

Recommendation: the channel with the lowest score is the best choice.
```

---

## Interference Classification

The system identifies interference sources by their temporal signature in the RSSI/noise data.

### Microwave Oven (2.4 GHz)
```
Signature:
  - 2.4 GHz only (does not affect 5 GHz)
  - Periodic noise spikes: ~15 seconds on, ~5 seconds off
  - Noise increase: 10-20 dB during active periods
  - Correlates with meal times (12:00, 18:00-19:00)
  - Duration: 1-5 minutes per event

Detection:
  If noise spikes are periodic (autocorrelation > 0.5)
  AND only on 2.4 GHz
  AND duration matches cooking cycle
  → classify as "microwave"
```

### Bluetooth Devices (2.4 GHz)
```
Signature:
  - 2.4 GHz only
  - Rapid, random SNR fluctuations
  - Frequency hopping causes intermittent interference
  - Constant low-level, not periodic
  - Variance > 5 dBm in SNR over 30 seconds

Detection:
  If SNR variance is high AND not periodic AND 2.4 GHz only
  → classify as "Bluetooth/frequency hopping"
```

### Radar (5 GHz DFS Channels)
```
Signature:
  - 5 GHz channels 52-144 only (DFS channels)
  - Adapter suddenly changes channel
  - Brief disconnection (1-2 seconds)
  - Required by regulation — adapter MUST vacate if radar detected

Detection:
  If monitoring history shows sudden channel change on DFS channel
  AND brief connectivity gap
  → classify as "DFS radar event"
```

### Neighboring AP (Co-Channel)
```
Signature:
  - Visible as a BSSID in scan results
  - Consistent signal level (not intermittent)
  - Same channel as your connection
  - Competing for airtime via CSMA/CA

Detection:
  Direct observation from network scan.
  Quantified by RSSI (stronger = more impact).
```

---

## Hotspot Intelligence

### Cellular Generation Fingerprinting

When connected to a mobile hotspot, the system infers the cellular technology from measured characteristics:

| Generation | Latency | Throughput | Jitter | Confidence |
|-----------|---------|------------|--------|------------|
| 3G | 80-200+ ms | 1-5 Mbps | >30 ms | High |
| 4G/LTE | 20-60 ms | 5-50 Mbps | 10-20 ms | High |
| 5G Sub-6 | 10-30 ms | 50-300 Mbps | <10 ms | Medium |
| 5G mmWave | <10 ms | 300-1000+ Mbps | <5 ms | Low (rare) |

### iPhone Hotspot Detection

```
Method: IP address subnet analysis
  172.20.10.x/28 (subnet mask 255.255.255.240) = iPhone hotspot
  192.168.43.x or 192.168.49.x = Android hotspot
  Other 192.168.x.x = likely infrastructure router

This is 100% reliable — Apple and Google use fixed subnets for tethering.
```

### Backhaul Quality Assessment

```
The "backhaul" is the phone's cellular connection to the tower.
The "Wi-Fi hop" is the wireless link from your laptop to the phone.

If RSSI to phone is strong (-40 to -50 dBm)
BUT latency is high (>80ms) or throughput is low (<5 Mbps):
  → Bottleneck is the cellular backhaul, not the Wi-Fi hop.
  → Nothing the system can do except advise:
    "Move your phone near a window for better cell reception"

If RSSI to phone is weak (-65+ dBm)
AND latency is high:
  → Both Wi-Fi hop AND backhaul may be problems.
  → Advise: "Move your phone closer to your laptop"
```

---

## RSSI-to-Distance Estimation

Using the ITU-R P.1238 indoor propagation model in reverse:

```
Given: RSSI (measured), TxPower (estimated), frequency
Find: distance

Path Loss = TxPower - RSSI
FSPL = 20*log10(d) + 20*log10(f_MHz) + 32.44
Indoor excess = 10 * (N/20 - 1) * log10(d)

For 5 GHz (N=30):
  d = 10^((PathLoss - 46.2) / 30) meters

For 2.4 GHz (N=28):
  d = 10^((PathLoss - 40.2) / 28) meters

Assumed TxPower:
  Infrastructure AP: 20 dBm
  Phone hotspot: 18 dBm
```

### Accuracy Limitations

- Walls reduce signal by 5-10 dB each → makes distance appear farther
- Multipath reflections can increase or decrease signal unpredictably
- Antenna orientation affects RSSI by 3-5 dB
- Accuracy: ±50% (useful for "same room" vs "next room", not precise meters)

---

## Presence Detection: Deep Dive

### The Physics

A human body is approximately 60% water. Water absorbs 2.4 GHz radiation very effectively (this is how microwave ovens work). When a person moves through a Wi-Fi signal path:

1. **Absorption**: body absorbs 3-10 dBm of signal energy
2. **Reflection**: body reflects signal, creating multipath changes
3. **Diffraction**: signal bends around the body, changing phase

These effects cause the RSSI measured by your laptop to fluctuate.

### Why LTC is Better for Sensing

Normal sensing: fixed 10-second window, compute variance, apply threshold.

LTC sensing: adaptive time constant adjusts to match the movement pattern.

```
Person walking fast:
  RSSI changes every 0.5-1 second
  Normal: 10-sec window catches it but smears the peaks
  LTC: tau shrinks to ~1 second, tracks individual footsteps

Person walking slowly:
  RSSI changes over 3-5 seconds
  Normal: 10-sec window may attribute it to noise
  LTC: tau grows to ~3 seconds, integrates the slow trend

Microwave interference:
  Periodic 15-second on/off cycle
  Normal: may confuse with human movement
  LTC: tau locks to the cycle period, autocorrelation detects periodicity
  → classified as "appliance", not "person"
```

### Spatial Calibration

Prerequisite for directional sensing. Maps each visible AP to a direction relative to the user.

```
Method 1: User tells the system
  "My router is to my right, my phone is in front of me"
  → Simple mapping, less precise

Method 2: Body-blocking
  The human body absorbs ~10-15 dBm at 2.4 GHz.
  By turning in the chair, the user's body blocks signals from one direction.

  Step 1: Face forward (baseline for all APs)
  Step 2: Turn LEFT → body blocks RIGHT → APs that drop are to the RIGHT
  Step 3: Turn RIGHT → body blocks LEFT → APs that drop are to the LEFT
  Step 4: Lean forward → body blocks BEHIND → APs that drop are BEHIND

  The direction with the largest RSSI drop for each AP = that AP's direction
```

### Detection Confidence Levels

```
variance_ratio = measured_variance / baseline_variance

> 5.0: "active_movement"    → high confidence (75-95%)
> 3.0: "likely"              → moderate confidence (50-85%)
> 1.5: "possible"            → low confidence (25-60%)
≤ 1.5: "none"               → high confidence of NO movement (80-95%)

Autocorrelation > 0.5: reclassify as "possible_interference" (periodic source)
Confidence reduced by 40% for periodic patterns.
```


path: docs/CROSS_PLATFORM_COMMANDS.md
content: # Cross-Platform Command Reference

Every OS-specific command used by the system, with exact syntax.

## OS Detection

| OS | Command | Expected Output |
|----|---------|----------------|
| macOS | `uname -s` | `Darwin` |
| Linux | `uname -s` | `Linux` |
| Windows | `$env:OS` (PowerShell) | `Windows_NT` |

## Wi-Fi Interface Discovery

**macOS:**
```bash
networksetup -listallhardwareports | grep -A 1 "Wi-Fi" | grep Device | awk '{print $2}'
# Returns: en0 (typically)
```

**Linux:**
```bash
iw dev 2>/dev/null | grep Interface | head -1 | awk '{print $2}'
# Fallback:
nmcli -t -f DEVICE,TYPE dev | grep wifi | cut -d: -f1 | head -1
# Returns: wlan0, wlp2s0, etc.
```

**Windows (PowerShell):**
```powershell
(Get-NetAdapter | Where-Object {$_.MediaType -eq "802.11"} | Select-Object -First 1).Name
# Returns: Wi-Fi (typically)
```

## Network Scanning

### Connected Network Info

**macOS:**
```bash
system_profiler SPAirPortDataType
# Returns: SSID, PHY mode, channel, signal/noise, tx rate, MCS, security
# Also lists ALL nearby networks with their channels and PHY modes
# Some nearby networks include signal/noise (typically those on nearby channels)
```

**Linux:**
```bash
# Connected network
iwconfig $WIFI_IF 2>/dev/null
# Returns: SSID, frequency, signal level, noise level, bit rate, link quality

# All visible networks
nmcli -f SSID,BSSID,SIGNAL,FREQ,CHAN,RATE,SECURITY,MODE dev wifi list
# Returns: tabulated list of all visible networks
```

**Windows (PowerShell):**
```powershell
# Connected network
netsh wlan show interfaces
# Returns: SSID, BSSID, signal (%), channel, radio type, authentication

# All visible networks
netsh wlan show networks mode=bssid
# Returns: SSID, BSSID, signal (%), channel, authentication for all visible
```

### Signal Conversion (Windows)

Windows reports signal as percentage. Approximate conversion:
```
dBm ≈ (signal_percent / 2) - 100
# 100% → -50 dBm, 80% → -60 dBm, 50% → -75 dBm, 20% → -90 dBm
```

Windows does NOT expose noise floor. Estimate -90 dBm for indoor environments.

## Performance Testing

### Latency and Packet Loss

**macOS / Linux:**
```bash
ping -c 10 -q 8.8.8.8
# Parse: "round-trip min/avg/max/stddev = X/X/X/X ms"
# Parse: "X% packet loss"
```

**Windows (PowerShell):**
```powershell
Test-Connection 8.8.8.8 -Count 10 | Measure-Object -Property Latency -Average -Maximum -Minimum
# Or:
ping -n 10 8.8.8.8
```

### DNS Speed

**macOS / Linux:**
```bash
# Current DNS
dig google.com +noall +stats 2>&1 | grep "Query time"

# Compare alternatives
dig google.com @1.1.1.1 +noall +stats 2>&1 | grep "Query time"
dig google.com @8.8.8.8 +noall +stats 2>&1 | grep "Query time"
```

**Windows (PowerShell):**
```powershell
Measure-Command { Resolve-DnsName google.com -DnsOnly } | Select TotalMilliseconds
Measure-Command { Resolve-DnsName google.com -Server 1.1.1.1 -DnsOnly } | Select TotalMilliseconds
```

### Throughput (Download Speed)

**macOS / Linux:**
```bash
curl -o /dev/null -w '{"speed_bytes_sec": %{speed_download}, "time_sec": %{time_total}}' \
  -s --max-time 15 http://speedtest.tele2.net/1MB.zip
# Returns JSON with download speed in bytes/sec
# Convert: Mbps = speed_bytes_sec * 8 / 1000000
```

**Windows (PowerShell):**
```powershell
# curl.exe is built-in on Windows 10+
curl.exe -o NUL -w "%{speed_download}" -s --max-time 15 http://speedtest.tele2.net/1MB.zip
```

## Network Configuration

### DNS Servers

**macOS:**
```bash
# Read
networksetup -getdnsservers Wi-Fi

# Write (requires sudo)
sudo networksetup -setdnsservers Wi-Fi 1.1.1.1 1.0.0.1

# Reset to DHCP default
sudo networksetup -setdnsservers Wi-Fi empty
```

**Linux:**
```bash
# Read
resolvectl status 2>/dev/null || cat /etc/resolv.conf

# Write
sudo resolvectl dns $WIFI_IF 1.1.1.1 1.0.0.1
```

**Windows (PowerShell):**
```powershell
# Read
Get-DnsClientServerAddress -InterfaceAlias "Wi-Fi"

# Write (requires admin)
Set-DnsClientServerAddress -InterfaceAlias "Wi-Fi" -ServerAddresses 1.1.1.1,1.0.0.1

# Reset to DHCP default
Set-DnsClientServerAddress -InterfaceAlias "Wi-Fi" -ResetServerAddresses
```

### DNS Cache Flush

**macOS:**
```bash
sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder
```

**Linux:**
```bash
sudo systemd-resolve --flush-caches 2>/dev/null || sudo resolvectl flush-caches
```

**Windows (PowerShell):**
```powershell
Clear-DnsClientCache
```

### DHCP Lease Renewal

**macOS:**
```bash
sudo ipconfig set $WIFI_IF DHCP
```

**Linux:**
```bash
sudo dhclient -r $WIFI_IF && sudo dhclient $WIFI_IF
# Or with NetworkManager:
sudo nmcli connection down $CONNECTION && sudo nmcli connection up $CONNECTION
```

**Windows (PowerShell):**
```powershell
ipconfig /release "Wi-Fi"
ipconfig /renew "Wi-Fi"
```

### Network Switching

**macOS:**
```bash
networksetup -setairportnetwork $WIFI_IF "TargetSSID" "password"
# Note: password only needed for first connection to a new network
```

**Linux:**
```bash
nmcli dev wifi connect "TargetSSID" password "password"
# For saved networks:
nmcli dev wifi connect "TargetSSID"
```

**Windows (PowerShell):**
```powershell
netsh wlan connect name="TargetSSID"
# Must have a saved profile. To create one first:
netsh wlan add profile filename="profile.xml"
```

### Adapter Restart

**macOS:**
```bash
networksetup -setairportpower $WIFI_IF off
sleep 2
networksetup -setairportpower $WIFI_IF on
```

**Linux:**
```bash
sudo nmcli radio wifi off
sleep 2
sudo nmcli radio wifi on
```

**Windows (PowerShell):**
```powershell
Disable-NetAdapter -Name "Wi-Fi" -Confirm:$false
Start-Sleep 2
Enable-NetAdapter -Name "Wi-Fi" -Confirm:$false
```

## Hotspot Detection

### By IP Subnet

```
172.20.10.x/28  → iPhone hotspot (Apple)
192.168.43.x    → Android hotspot (older)
192.168.49.x    → Android hotspot (newer)
Other           → Infrastructure router
```

### By SSID Pattern

```
Keywords: iPhone, Android, Galaxy, Pixel, Hotspot, Mobile,
          Moto, OnePlus, Samsung, Xiaomi, Huawei, OPPO,
          Redmi, Vivo, Realme
```

## Proxy Detection

**macOS:**
```bash
networksetup -getwebproxy Wi-Fi
networksetup -getsecurewebproxy Wi-Fi
```

**Linux:**
```bash
env | grep -i proxy
```

**Windows (PowerShell):**
```powershell
Get-ItemProperty 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' |
  Select ProxyEnable, ProxyServer
```

## Routing Table

**macOS:**
```bash
netstat -rn -f inet | head -20
```

**Linux:**
```bash
ip route show
```

**Windows (PowerShell):**
```powershell
Get-NetRoute -DestinationPrefix 0.0.0.0/0
```


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)