# Plan — Unify the agent into channels; channels = manager of all channels

**Created:** 2026-07-06 · **Branch:** `feat/agent-unify` · **Status:** executing
**Operator decisions (2026-07-06):**
- channels becomes the single manager of ALL channels (WhatsApp, Instagram now;
  Facebook later). Integrate `../ai-chatbot` **back into** channels.
- Use **ai-chatbot's** agent core. Do **not** use channels' embedded agent.
- **Nothing is live** — WhatsApp number not connected, channels dormant. So we
  restructure freely and prove **eval-first / in the playground**; no
  shadow/canary needed. (Matches `../ai-chatbot/plans/standalone-agent.md`:
  "agent is not live so there is no risk.")
- **UI/UX is first-class** (operator): if the admin console doesn't make new
  capabilities obvious, they get misunderstood, neglected, or misused. Every
  feature ships with its console surface, not just an API.
- **Delivery loop:** build locally → deploy to the VPS (`sopserver1`) over SSH →
  operator sees it live. Live domain is `channels.silveroakglobal.com`.

## Why this is tractable (not a rewrite)
The two agents share ancestry (ai-chatbot was extracted from channels ~Jun 25,
then hardened). ai-chatbot already has the **ports/adapters** seam
(`packages/shared/src/agent/ports.ts` + `adapters/`) built for exactly this.
channels' existing services become the concrete adapters. So we get ai-chatbot's
cleaner, hardened core AND channels' live features — nothing is thrown away.

### Divergence (facts, 2026-07-06)
- **ai-chatbot has, channels lacks:** ports.ts, adapters/, run-turn, resilience,
  observability, pricing, mandate, portable tools.ts, kb-local.
- **channels has, ai-chatbot lacks:** K1 hybrid retrieval (embedding/pgvector/
  rrfFuse — now committed on this branch), the questionnaire engine (263 refs),
  consultant-directory, deep LeadRat.
- Both diverged on: loop, system-prompt, lead-state, history, kb-structured,
  mortgage, agent.service, agent-tools, controllers, llm-config, agent.module.

A file-by-file reconciliation report drives the merge (see Phase 1).

## MUST-PRESERVE (or WhatsApp regresses when it comes back)
K1 hybrid retrieval · questionnaire engine · consultant-directory · deep LeadRat
integration. These become adapters/tools behind the ports, not deletions.

## MUST-NOT-CARRY
ai-chatbot's git history and `.env` — they contain committed secrets (its own
audit is security-FAIL-closed). Copy **source files only**. Those secrets still
need rotating in the ai-chatbot repo independently.

## Phases (test-driven; per-package vitest `--pool=forks maxForks=2`, PID cap)

### Phase 1 — Bring the agent core into channels (shared package)
Drive off the reconciliation report. Order: net-new low-dep files → ports.ts →
diverged files (per merge decision) → keep the shared package building at each
step. Keep K1 logic wherever it sits in a diverged file (e.g. kb-structured).
Gate: `pnpm --filter @whatapp/shared build` + shared vitest green.

### Phase 2 — Repoint the NestJS agent surface onto ports + adapters
`apps/api/src/agent`: implement channels adapters for every port
(KnowledgeStorePort→K1 KnowledgeService, CrmPort→LeadRat, history/contact→Prisma,
questionnaires + consultant-directory → tools), `AgentModule.forRoot({adapters})`.
Cross-module DI: providing module in `imports`. Gate: api build + agent/knowledge
vitest green + `pm2` boot + `/health` ok (local).

### Phase 3 — UI/UX pass (first-class, not an afterthought)
The admin console must make the unified agent legible: agent settings/behaviour,
prompt-module view, KB management, eval/playground surface, per-channel config,
and (Phase 5) the Instagram comment stream. Consistent with the existing
`.so-table`/`StatusPill`/`QueryState` primitives. Screenshots via
`tools/screenshot`. No feature is "done" without its console surface.

### Phase 4 — Prove eval-first
Run `tools/agent-eval` gate + dashboard playground. Operator-gated LLM spend
(standing rule — no unprompted eval/API runs).

### Phase 5 — Instagram as a channel (comments first)
Fold in `plans/instagram-comments.md`, now targeting the UNIFIED agent:
- Transport in channels' gateway: route webhook by `object`, parse IG `comments`,
  reply via `/{comment-id}/replies`, behind `INSTAGRAM_COMMENTS_ENABLED`.
- Behaviour = `../manychat-ai/` (two-tier, CTA-by-post-type, appearance/body skip,
  no @-tag) as the Instagram-comments policy/KB.
- ManyChat stays live in parallel until the platform path is proven, then cut over.
- UI: an Instagram comment moderation stream in the console.
Then IG DMs (through the full agent), then Facebook (third adapter).

### Phase 6 — Deploy to VPS
Deploy loop per CLAUDE.md: shared build → api/worker build →
`pm2 restart channels-api channels-worker --update-env` → `/health`. Web:
`pnpm --filter @whatapp/web build` then publish (operator sudo). Verify live on
`channels.silveroakglobal.com`. (Confirm SSH access + method before first deploy.)

## Open / to confirm at execution time
- SSH access + deploy mechanics to `sopserver1` (may need operator sudo for web
  publish — RUNBOOK notes no passwordless sudo).
- Base-branch endgame: `feat/agent-unify` → `main` when green; fate of the
  `elevate/ui-consistency-backend-hardening` branch (operator).
- ai-chatbot repo: rotate its committed secrets (independent remediation).
