# Architecture Multi-channel AI messaging platform. Telegram is the **first** adapter, not the definition of the system. ## Layering ``` CHANNEL LAYER +-------+-------+-------+ |Telegram|Shopee |Lazada| |adapter |adapter|adapter| +-------+-------+-------+ | APPLICATION CORE (channel-agnostic message contract) | +--------+--------+ | | | AI/Agent n8n Tools (integrations) | | Memory PostgreSQL | | pgvector (Phase 3) | LLM APIs (LiteLLM → OpenAI/OpenRouter) ``` ## Responsibilities | Layer | Owns | Must NOT own | |---|---|---| | Channel adapter (`src/channels/*`) | Native event parsing, platform formatting/IDs, buttons, webhook setup | Business rules, prompt construction, DB schema | | Application core (`src/core`) | Normalized message contract, capabilities flags | Telegram-only assumptions | | AI layer (`src/ai`) | Prompt assembly, agent orchestration, memory/retrieval interfaces | Platform transport | | DB (`src/db`) | Users, conversations, messages, migration | Transient in-process state | | n8n (`src/integrations/n8n`) | Automation/tool calls with schema validation | Core conversational state | ## Data flow (one message) 1. Telegram `POST` → Hono webhook `POST /webhook/`. 2. grammY parses the update → `normalizeIncoming()` → `IncomingMessage`. 3. `db.saveExchange()` upserts user, opens/reuses conversation, stores the user message. 4. `Agent.respond()` builds system+business prompt (plus user-memory block when a summary exists), fetches recent history, calls LLM. 5. Reply persisted via `saveExchange()` (assistant role), returned to grammY → Telegram. 6. `Agent.updateMemoryAsync()` (fire-and-forget, never blocks the reply) writes `lang` + `last_queries` (FIFO-5) rule-based, and triggers the `MemoSummarizer` (cheap `chatSmall` call) when new facts appear → updated `summary` back into `user_memory`. ## Key contracts - `IncomingMessage` / `OutgoingMessage` (`src/core/messages.ts`) — the single normalized shape every adapter translates to/from. - `ChannelCapabilities` — flags (`supportsButtons`, etc.) so the core never assumes a feature exists on every channel. - Provider/retriever/memory interfaces — defined now, no-op until justified (Phases 2/3). ## Networking - The bot container joins the existing **`mem0_net`** (external) to reach `mem0-postgres` and `litellm` by container name. - Production webhook must be HTTPS: `bot.digikedai.com` via the DSM CF-tunnel → Traefik → this container (routing added in a later step; see `docs/OPERATIONS.md`). - PostgreSQL is never exposed publicly.