# Next Session: WhatsApp Web + Playwright Migration

**Date:** 2026-04-07
**Status:** DECISION MADE — migrate from mobile u2 scraping to WhatsApp Web + Playwright
**Previous:** `docs/NEXT-SESSION-WHATSAPP-AGENT-V6.md` (u2 approach, partially working)

## Why We're Migrating

Mobile u2 screen scraping is too brittle:
- `d(text="Rajesh").click()` opened contact info instead of chat → delivery failed
- Resource IDs change with WhatsApp updates
- Tasker notification metadata unreliable (sender="WhatsApp", text truncated to 23 chars)
- Every session spent debugging u2 navigation, unlock, timing

**What DID work:** The scraper successfully read the full message from the WhatsApp screen, trigger matched, LLM classified `direct_request`, response generated correctly. Only delivery failed.

## What We Want

Annie monitors WhatsApp via **WhatsApp Web** running in headless Chrome on Panda, automated by **Playwright**. No Tasker, no ADB, no u2, no phone screen interaction.

## Architecture

```
Panda (headless Chrome + Playwright)
  └── WhatsApp Web session (linked to Pixel's WhatsApp)
       ├── Poll for new messages (DOM observer or interval)
       ├── Detect "Annie" mentions → trigger pipeline
       ├── Generate response via Gemma 4 on Titan
       └── Type + send reply in WhatsApp Web

Pixel phone: only needed for initial QR scan + re-link every ~14 days
Titan: Gemma 4 26B for LLM classification + response generation (unchanged)
```

## Implementation Plan

### Phase 1: WhatsApp Web Session Setup
1. Install Playwright on Panda (`pip install playwright && playwright install chromium`)
2. Launch headless Chrome with persistent browser context (stores WhatsApp Web session)
3. Navigate to `web.whatsapp.com`
4. Display QR code for Rajesh to scan from Pixel's WhatsApp → Settings → Linked Devices
5. Verify session established (wait for chat list to load)
6. Session persists in browser profile dir — survives restarts

### Phase 2: Message Reading (replace scraper.py)
1. Playwright watches for new messages:
   - Option A: Poll every 5-10 seconds (check unread badge / new message elements)
   - Option B: MutationObserver on the chat panel (instant notification)
2. When new message detected in Rajesh's DM:
   - Extract sender + text from DOM
   - Feed into existing trigger pipeline (check_regex → classify_trigger → generate_response)

### Phase 3: Message Sending (replace responder.py u2 delivery)
1. Click on the right chat (by chat name in sidebar)
2. Type message in the input box (`div[contenteditable="true"]` or similar)
3. Click send button or press Enter
4. Verify message sent (check for tick marks)

### Phase 4: Integration
1. Replace `scraper.py` calls in `agent.py` with Playwright-based reader
2. Replace `send_response()` u2 calls with Playwright-based sender
3. Keep Tasker + receiver as optional backup (or remove entirely)
4. Health endpoint reports WhatsApp Web session status

## Research Needed

Before coding, investigate:
1. **WhatsApp Web DOM selectors** — what CSS selectors identify: chat list items, message bubbles, sender names, input box, send button. These change occasionally but are more stable than Android resource IDs.
2. **Session persistence** — how long does the linked session last? Multi-device mode means phone doesn't need to be online. What triggers re-linking?
3. **Anti-detection** — does WhatsApp detect Playwright automation? Strategies: headful mode, human-like delays, `playwright-stealth` plugin.
4. **Existing libraries** — check `whatsapp-web.js`, `@nickolay/playwright-whatsapp` or similar for selector patterns we can reuse (don't import the lib, just learn from their selectors).

## Key Files (current u2 approach — to be replaced)

| File | Current | After Migration |
|------|---------|-----------------|
| `scraper.py` | u2 screen reader | Playwright DOM reader |
| `responder.py` | u2 delivery (`_send_via_u2`) | Playwright type + send |
| `receiver.py` | Tasker webhook | May become unnecessary (Playwright polls directly) |
| `agent.py` | Drain loop + trigger | Keep, but source is Playwright instead of buffer |
| `tasker_watchdog.py` | Checks Tasker process | May become unnecessary |

## Environment

- **Panda** (192.168.68.57): WhatsApp agent, will run headless Chrome
- **Titan** (192.168.68.52): Gemma 4 on port 8003 (unchanged)
- **Pixel** (192.168.68.60): WhatsApp account, only for QR scan

## Verification

```bash
# After setup, check WhatsApp Web session is alive:
ssh panda "curl -s http://localhost:8780/v1/health | python3 -m json.tool"
# Should show: whatsapp_web_connected: true

# Send test message from Rajesh's phone to Annie's WhatsApp
# Watch logs:
ssh panda "tail -f /tmp/whatsapp-agent.log"
# Should see: [PLAYWRIGHT] New message from Rajesh: "..."
# Then: [TRIGGER] → [RESPONDER] → message sent via WhatsApp Web
```

## What to Keep

- `trigger.py` — regex + LLM classification (unchanged)
- `config.py` — constants, circuit breaker (unchanged)
- `compaction.py` — hourly/daily compaction (unchanged)
- `context_mgr.py` — 128K context tracking (unchanged)
- `agent.py` — main daemon loop (modified to use Playwright instead of u2)
- `responder.py` — response generation via Gemma 4 (keep, replace only u2 delivery)
- Real-time injection in response prompt (keep — fixes hallucinated timestamps)
