Customer-Agent Interaction Design
Overview
Заголовок раздела «Overview»Enable customers to communicate with an AI agent (walrider) through WebSocket, with messages routed via RabbitMQ. Each project automatically gets an agent thread on creation.
Two branches of work:
- feat/project-service — auto-create agent thread on project creation, link Project ↔ WalriderThread in DB
- feat/ai-agent-service — new NX service with WS gateway, RabbitMQ integration, HTTP endpoints for history/state
Additionally: remove obsolete walrider HTTP integration from user-api, move repositories to ai-agent-service.
Schema Changes
Заголовок раздела «Schema Changes»WalriderThread — add projectId
Заголовок раздела «WalriderThread — add projectId»model WalriderThread { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid customerId String @map("customer_id") @db.Uuid projectId String @unique @map("project_id") @db.Uuid externalThreadId String? @map("external_thread_id") @db.Text
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade) project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) messages WalriderMessage[] states WalriderThreadState[]
@@index([customerId]) @@map("walrider_threads")}WalriderMessage — add autoincrement messageId
Заголовок раздела «WalriderMessage — add autoincrement messageId»model WalriderMessage { id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid messageId Int @default(autoincrement()) @map("message_id") threadId String @map("thread_id") @db.Uuid role WalriderMessageRole content String @db.Text externalMessageId String? @map("external_message_id") @db.Text metadata Json? @db.Json
createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at")
thread WalriderThread @relation(fields: [threadId], references: [id], onDelete: Cascade) states WalriderThreadState[]
@@index([threadId]) @@map("walrider_messages")}Project — add relation
Заголовок раздела «Project — add relation»model Project { // ... existing fields walriderThread WalriderThread?}RabbitMQ Infrastructure
Заголовок раздела «RabbitMQ Infrastructure»New lib: rabbit-client (libs/apis/utils/rabbit-client/)
Заголовок раздела «New lib: rabbit-client (libs/apis/utils/rabbit-client/)»Following the redis-client pattern:
RabbitClientModule— dynamic module withforRoot(options)RabbitClientService— wraps amqplib connection with publish/subscribe/ack methods- Connection management and reconnection
New config: rabbit (libs/apis/configs/shared/rabbit/)
Заголовок раздела «New config: rabbit (libs/apis/configs/shared/rabbit/)»Environment variables:
RABBITMQ_HOST, RABBITMQ_PORT, RABBITMQ_USERNAME, RABBITMQ_PASSWORD, RABBITMQ_PREFETCHRABBITMQ_REQUEST_QUEUE, RABBITMQ_REQUEST_EXCHANGE, RABBITMQ_REQUEST_EXCHANGE_TYPERABBITMQ_RESPONSES_QUEUE, RABBITMQ_RESPONSES_EXCHANGE, RABBITMQ_RESPONSES_EXCHANGE_TYPEMessage Schemas
Заголовок раздела «Message Schemas»Request (ai.requests):
{ "messageId": "int", "threadId": "string", "content": "string" }Response (ai.responses):
{ "messageId": "int", "threadId": "string", "response": "string", "phase": "string", "isComplete": false, "plan": {}}feat/project-service — Agent Auto-Creation
Заголовок раздела «feat/project-service — Agent Auto-Creation»On POST /customers/projects:
- Create Project in DB (existing)
- Create WalriderThread with
{ customerId, projectId }in the same transaction - Thread’s
id(UUID) serves asthreadIdin RabbitMQ messages
Implementation: import WalriderThreadRepository into ProjectModule, extend createProject.
feat/ai-agent-service — New NX Service
Заголовок раздела «feat/ai-agent-service — New NX Service»App: apps/ai-agent-service/
Заголовок раздела «App: apps/ai-agent-service/»Standard NestJS bootstrap with CORS, validation pipe, Prisma filter, Swagger.
Libs: libs/apis/providers/ai-agent-service/
Заголовок раздела «Libs: libs/apis/providers/ai-agent-service/»features/ agent-chat/ — WS gateway + RabbitMQ handler + HTTP controllerdata-access/ — DTOs for WS events and HTTP responsesWebSocket Gateway
Заголовок раздела «WebSocket Gateway»- Socket.IO based (
@WebSocketGateway) - Connection: client sends JWT token (query param) + projectId
- Gateway validates JWT, verifies customer owns the project
- Stores socket mapping in Redis:
ws:project:{projectId}→ socketId - Client events:
sendMessage { content }→ save to DB, publish to RabbitMQ - Server events:
message { messageId, response, phase, isComplete, plan }
RabbitMQ Integration
Заголовок раздела «RabbitMQ Integration»- Producer: on WS
sendMessage, publish to ai.requests exchange - Consumer: subscribe to ai.responses queue
- Save ASSISTANT message to DB
- Save WalriderThreadState to DB
- If active socket → emit via WS
- If no socket → persisted in DB for later retrieval
HTTP Endpoints
Заголовок раздела «HTTP Endpoints»GET /agents/:projectId/history— paginated chat historyGET /agents/:projectId/state— current agent state (latest thread state)
Both protected by CustomerGuard with project ownership validation.
Cleanup: Remove Old Walrider Feature
Заголовок раздела «Cleanup: Remove Old Walrider Feature»Remove libs/apis/providers/user-api/features/walrider/ entirely:
- Move repositories (thread, message, state) to ai-agent-service libs
- Remove walrider-http.service.ts (replaced by RabbitMQ)
- Remove walrider controller and service
- Remove walrider module import from user-api
Data Flow
Заголовок раздела «Data Flow»Customer creates project → project-api: create Project + WalriderThread (same transaction)
Customer opens chat → WS connect to ai-agent-service with JWT + projectId → Validate auth + ownership, store session in Redis
Customer sends message → WS "sendMessage" → save USER message to DB → publish to ai.requests
Walrider responds → Consumer reads ai.responses → save ASSISTANT message + state to DB → If active socket → emit "message" to client
Customer fetches history → GET /agents/:projectId/history → paginated messages from DBDecisions
Заголовок раздела «Decisions»| Decision | Choice | Rationale |
|---|---|---|
| Architecture | Single ai-agent-service | Matches requirements, follows monorepo patterns, minimal complexity |
| Project-Thread link | projectId on WalriderThread (unique 1:1) | Simple, each project gets exactly one agent thread |
| WS auth | JWT token in query param | Reuses existing JWT infrastructure (CustomerGuard pattern) |
| RabbitMQ threadId | WalriderThread.id (UUID) | No extra fields, direct mapping |
| RabbitMQ messageId | DB autoincrement int on WalriderMessage | Guaranteed unique, simple |
| Old walrider cleanup | Move repos to ai-agent-service, remove from user-api | Clean separation, no dead code |