# Next Session: WhatsApp Agent → Annie Tool Routing

## What

Route WhatsApp queries through Annie's `/v1/chat` SSE endpoint (same as Telegram) so that maps, weather, AQI, web search, memory search, and all other tools work on WhatsApp. Currently, WhatsApp uses its own Gemma 4 responder with zero tool calling — "How do I get to Nandi Hills?" gets hallucinated directions instead of real Google Maps data.

**Design:** Always route to Annie. On 503 (voice session active), timeout, or error — fall back to existing Gemma 4 responder. Two independent circuit breakers (Annie + Gemma 4).

## Plan

Read the approved plan first: `~/.claude/plans/witty-gliding-bird.md`

It has the full implementation, all adversarial review findings (34 findings, 17 addressed), state machine, pre-mortem, and code sketches.

## Key Design Decisions (from adversarial review)

1. **Separate circuit breaker for Annie** — Annie failures must NOT open the Gemma 4 breaker. Create `annie_circuit_breaker = LLMCircuitBreaker(threshold=3, reset_after_s=300.0)` in config.py.
2. **CE_TOKEN validation** — `chat_with_annie()` must raise RuntimeError if CE_TOKEN is empty (not silently fall back).
3. **Client-side filename regex** — Validate `^map_[a-f0-9]{12}\.png$` before downloading map images (defense-in-depth).
4. **180s timeout** (not 120s) — Balances responsiveness vs tool chain execution time.
5. **10s image timeout** (not 5s) — More margin for network hiccups.
6. **Bounded session dict** — `_annie_sessions` max 100 entries with LRU eviction.
7. **"whatsapp" channel = "open" sensitivity** — Same as Telegram (private channel from Rajesh).
8. **Fix agent.py:9 docstring** — Says "Panda (192.168.68.52)" but .52 is Titan. Panda is .57.

## Files to Modify (in order)

1. `services/annie-voice/server.py` — Add "whatsapp" to `_VALID_CHANNELS` + `_CHANNEL_SENSITIVITY` (~2 lines)
2. `services/whatsapp-agent/config.py` — Add ANNIE_VOICE_URL, timeouts, annie_circuit_breaker (~10 lines)
3. `services/whatsapp-agent/annie_client.py` — NEW: SSE client with `chat_with_annie()` + `_download_map_image()` (~130 lines)
4. `services/whatsapp-agent/agent.py` — Fix docstring, wire Annie routing in `_check_trigger()`, add `_annie_sessions` (~50 lines)
5. `services/whatsapp-agent/tests/test_annie_client.py` — NEW: 11 test cases (~220 lines)
6. `services/whatsapp-agent/tests/test_agent_annie_routing.py` — NEW: 5 integration tests (~150 lines)

## Reference Files (read these to understand the patterns)

- `services/telegram-bot/context_client.py` — `chat_with_annie()` reference implementation (SSE parsing, 3-tuple return, session management)
- `services/telegram-bot/bot.py:459-485` — How Telegram unpacks the 3-tuple, sends text + photos
- `services/whatsapp-agent/agent.py:398-440` — Current trigger handling (insertion point for Annie routing)
- `services/whatsapp-agent/wa_sender.py` — `send_image()` API for map delivery
- `services/whatsapp-agent/config.py:182-235` — `LLMCircuitBreaker` class to reuse

## Start Command

```
cat ~/.claude/plans/witty-gliding-bird.md
```

Then implement the plan. All adversarial findings are already addressed in it.

## Verification

### Automated
```bash
cd services/whatsapp-agent && python -m pytest tests/ -v
# All existing 181+ tests + ~16 new tests should pass
```

### Deploy
1. Commit → git push
2. Titan: `git pull` → `./stop.sh annie-voice && ./start.sh annie-voice`
3. Panda: `git pull` → verify `.env` has `ANNIE_VOICE_URL=http://192.168.68.52:7860` + `CE_TOKEN` → `./stop.sh whatsapp && ./start.sh whatsapp`

### Manual E2E
1. WhatsApp → "How do I get to Nandi Hills?" → real directions + static map image
2. WhatsApp → "What's the weather in Bangalore?" → temperature from tool + map pin
3. WhatsApp → "Is it safe to run outside?" → AQI data from tool
4. WhatsApp → "Good morning Annie" → warm natural response
5. Start voice session → WhatsApp query → Gemma 4 fallback (no tools, but responds)
6. Verify auth: `ssh panda "grep CE_TOKEN ~/workplace/her/her-os/.env"` matches Titan's token
