# AI Agent OAuth Implementation Tasks

Mục tiêu: thêm agent AI trả lời hội thoại Zalo theo prompt riêng, có nút robot trong chi tiết tin nhắn, đọc tối đa 30 tin gần nhất và tạo phản hồi. Hướng OAuth được triển khai theo mô hình Toolchat là OAuth Provider để ChatGPT/Codex hoặc MCP client kết nối vào Toolchat. Không dùng cách lấy `code` redirect thủ công rồi lưu lại, vì authorization code phải được exchange qua token endpoint với PKCE/client validation.

## Trạng thái triển khai

Đã triển khai MVP trong codebase:

- Agent CRUD API và trang `/agents`.
- Bảng `ai_agents`, `ai_agent_runs`, `oauth_clients`, `oauth_authorization_codes`, `oauth_access_tokens`, `oauth_refresh_tokens`.
- Service gọi OpenAI Responses API từ Laravel backend.
- API `POST /api/zalo-accounts/{id}/agent-reply`.
- Nút robot trong màn `/chat` để chọn agent, tạo nháp hoặc tự gửi tùy cấu hình agent.
- OAuth Provider tối thiểu: discovery metadata, dynamic client registration, authorize/consent, token endpoint authorization code + PKCE, refresh token.
- API external dùng bearer token OAuth cho đọc account/conversation/message, gửi message và chạy agent.

Chưa triển khai trong MVP:

- Login/logout nhân viên Toolchat và role admin/operator.
- Màn audit riêng cho `ai_agent_runs`.
- Rate limit nâng cao theo user/conversation/account.

## Nguyên tắc triển khai

- Làm MVP ổn định trước: tạo nháp bằng AI, nhân viên duyệt rồi gửi.
- Chỉ bật tự gửi sau khi đã có log, quyền hạn, rate limit và nút tắt nhanh.
- Không expose token, API key hoặc prompt nhạy cảm ra browser.
- OAuth Provider phục vụ kết nối từ ChatGPT/Codex/MCP client vào Toolchat. Phần Toolchat tự gọi model vẫn cần model backend hợp lệ hoặc một workspace-agent endpoint hợp lệ.
- Mọi lần bot chạy phải có log đầu vào, đầu ra, trạng thái, lỗi và người kích hoạt.

## Phase 1 - Agent Core

### Task 1.1 - Tạo bảng `ai_agents`

Tạo migration:

- `id`
- `name`
- `description` nullable
- `system_prompt` text
- `model_provider` string, default `openai`
- `model` string nullable
- `temperature` decimal nullable
- `reply_mode` enum: `draft`, `auto_send`; default `draft`
- `is_active` boolean
- `created_by` nullable foreign key `users.id`
- timestamps

Tiêu chí nghiệm thu:

- Chạy migrate thành công.
- Có thể tạo nhiều agent.
- Agent mặc định ở chế độ `draft`.

### Task 1.2 - Tạo model và CRUD API agent

Tạo:

- `App\Models\AiAgent`
- `App\Http\Controllers\AiAgentController`
- request validation cho store/update
- routes:
  - `GET /api/ai-agents`
  - `POST /api/ai-agents`
  - `GET /api/ai-agents/{agent}`
  - `PUT /api/ai-agents/{agent}`
  - `DELETE /api/ai-agents/{agent}`

Tiêu chí nghiệm thu:

- CRUD trả JSON thống nhất `success`, `data`, `message`.
- Không cho xóa agent đã có log chạy nếu muốn giữ audit; có thể chuyển sang `is_active=false`.
- Validate prompt không rỗng.

### Task 1.3 - Tạo trang quản lý agent

Tạo view `/agents`:

- Danh sách agent.
- Form tạo/sửa agent.
- Ô nhập prompt lớn.
- Chọn chế độ `Tạo nháp` hoặc `Tự gửi`.
- Bật/tắt agent.

Tiêu chí nghiệm thu:

- Tạo/sửa agent không reload trang.
- Prompt dài vẫn nhập/sửa được dễ dàng.
- Có link từ trang accounts/chat sang trang agents.

## Phase 2 - Bot Reply trong Toolchat

### Task 2.1 - Tạo bảng `ai_agent_runs`

Tạo migration:

- `id`
- `ai_agent_id`
- `zalo_account_id`
- `conversation_key`
- `input_messages` json
- `output_text` text nullable
- `reply_mode`
- `status` enum: `pending`, `completed`, `failed`
- `error` text nullable
- `triggered_by` nullable foreign key `users.id`
- timestamps

Tiêu chí nghiệm thu:

- Mỗi lần click robot tạo một run.
- Run lỗi vẫn lưu được lỗi để debug.

### Task 2.2 - Tạo `AiAgentReplyService`

Service nhận:

- `zalo_account_id`
- `conversation_key`
- `agent_id`
- `mode` optional

Luồng xử lý:

1. Load agent active.
2. Gọi `ZaloAccountService::getMessages()`.
3. Lấy tối đa 30 tin gần nhất.
4. Chuẩn hóa role:
   - `direction=in` thành khách.
   - `direction=out` thành shop/nhân viên.
5. Gửi prompt sang model backend.
6. Lưu `ai_agent_runs`.
7. Nếu `draft`, trả text về frontend.
8. Nếu `auto_send`, gọi lại `ZaloAccountService::sendMessage()`.

Tiêu chí nghiệm thu:

- Không gửi nếu agent inactive.
- Không gửi nếu không đọc được tin nhắn.
- Không gửi nếu output rỗng.
- Có timeout và error message dễ hiểu.

### Task 2.3 - Tạo API chạy agent

Route:

- `POST /api/zalo-accounts/{id}/agent-reply`

Request:

- `conversation_key` required
- `agent_id` required
- `mode` nullable: `draft`, `auto_send`

Response draft:

```json
{
  "success": true,
  "data": {
    "reply": "Nội dung bot đề xuất",
    "sent": false,
    "run_id": 1
  }
}
```

Response auto send:

```json
{
  "success": true,
  "data": {
    "reply": "Nội dung bot đã gửi",
    "sent": true,
    "run_id": 1
  }
}
```

Tiêu chí nghiệm thu:

- Dùng được qua fetch từ màn chat.
- Auto send dùng đúng luồng gửi Zalo hiện có.
- Sau khi gửi tự động phải clear cache message/conversation như gửi tay.

### Task 2.4 - Thêm nút robot trong màn chat

Sửa:

- `backend/resources/views/zalo/chat/index.blade.php`
- `backend/public/js/zalo-chat.js`
- `backend/public/css/zalo-chat.css`

UI:

- Nút robot nhỏ cạnh nút "Làm mới" hoặc cạnh composer.
- Loading khi agent đang chạy.
- Nếu có nhiều agent, hiển thị menu chọn agent.
- Nếu mode draft, đưa output vào `draftMessage`.
- Nếu mode auto send, refresh tin nhắn sau khi gửi.

Tiêu chí nghiệm thu:

- Không làm vỡ layout desktop/mobile.
- Nút disabled khi tài khoản Zalo chưa `READY`.
- Có lỗi rõ khi agent fail.

## Phase 3 - OAuth Provider cho ChatGPT/Codex/MCP

### Task 3.1 - Tạo OAuth data model

Tạo bảng:

- `oauth_clients`
- `oauth_authorization_codes`
- `oauth_access_tokens`
- `oauth_refresh_tokens`

Tối thiểu cần lưu:

- client id
- redirect URIs allowlist
- scopes
- code challenge / code challenge method
- token hash, không lưu plaintext token
- expires_at
- revoked_at

Tiêu chí nghiệm thu:

- Token lưu dạng hash.
- Code ngắn hạn, dùng một lần.
- Refresh token có thể revoke.

### Task 3.2 - Metadata endpoints

Tạo endpoints chuẩn OAuth/OIDC discovery mức tối thiểu:

- `GET /.well-known/oauth-authorization-server`
- `GET /.well-known/oauth-protected-resource`

Nội dung cần có:

- issuer
- authorization_endpoint
- token_endpoint
- registration_endpoint nếu hỗ trợ DCR
- scopes_supported
- code_challenge_methods_supported: `S256`

Tiêu chí nghiệm thu:

- ChatGPT/Codex/MCP client đọc được metadata.
- URL issuer khớp public domain.

### Task 3.3 - Authorization endpoint

Route:

- `GET /oauth/authorize`

Xử lý:

1. Validate `client_id`, `redirect_uri`, `response_type=code`, `scope`, `state`, `code_challenge`.
2. Nếu user chưa login Toolchat, chuyển sang login nội bộ.
3. Hiển thị màn consent quyền.
4. Tạo authorization code.
5. Redirect về `redirect_uri?code=...&state=...`.

Tiêu chí nghiệm thu:

- Chặn redirect URI không nằm trong allowlist.
- Bắt buộc PKCE S256.
- Code hết hạn nhanh, khoảng 5 phút.

### Task 3.4 - Token endpoint

Route:

- `POST /oauth/token`

Grant types:

- `authorization_code`
- `refresh_token`

Xử lý:

- Verify code.
- Verify PKCE.
- Issue access token ngắn hạn.
- Issue refresh token dài hơn.

Tiêu chí nghiệm thu:

- Code dùng lại lần hai bị từ chối.
- Token hết hạn bị từ chối.
- Refresh token revoke được.

### Task 3.5 - Scope và API bảo vệ bằng bearer token

Scopes MVP:

- `toolchat.conversations.read`
- `toolchat.messages.read`
- `toolchat.messages.send`
- `toolchat.agents.run`

Tạo middleware:

- đọc `Authorization: Bearer`
- hash token rồi lookup
- kiểm tra hết hạn, revoke, scope

Tiêu chí nghiệm thu:

- API thiếu scope trả `403`.
- Token sai/hết hạn trả `401`.

### Task 3.6 - MCP/API endpoints cho client ngoài

Tạo nhóm route `/api/mcp` hoặc `/api/external`:

- list Zalo accounts được phép
- list conversations
- get last messages
- run agent reply
- send message nếu có scope

Tiêu chí nghiệm thu:

- ChatGPT/Codex sau khi OAuth có thể gọi endpoint theo scope.
- Không trả dữ liệu vượt quyền user đã consent.

## Phase 4 - Login nội bộ và quyền hạn

### Task 4.1 - Hoàn thiện auth nhân viên Toolchat

Hiện repo có bảng `users` mặc định Laravel nhưng chưa thấy luồng login nội bộ hoàn chỉnh trong UI chính.

Cần làm:

- Login/logout.
- Middleware bảo vệ `/accounts`, `/chat`, `/agents`, `/oauth/authorize`.
- Gắn `created_by`, `triggered_by`.

Tiêu chí nghiệm thu:

- Người chưa login không vào được chat/agent.
- OAuth authorize bắt buộc login Toolchat.

### Task 4.2 - Phân quyền đơn giản

Role tối thiểu:

- `admin`: quản lý agent, OAuth client, tài khoản Zalo.
- `operator`: chat và chạy agent.

Tiêu chí nghiệm thu:

- Operator không sửa OAuth client.
- Operator không sửa agent nếu không được cấp quyền.

## Phase 5 - Ổn định vận hành

### Task 5.1 - Rate limit agent

Áp dụng giới hạn:

- theo user
- theo conversation
- theo account Zalo

Tiêu chí nghiệm thu:

- Không spam được auto send.
- Có thông báo khi bị limit.

### Task 5.2 - Guardrails trước khi auto send

Chặn auto send nếu:

- output quá dài
- output rỗng
- output chứa thông tin không chắc chắn theo rule
- agent đang inactive

Tiêu chí nghiệm thu:

- Draft vẫn hoạt động khi auto send bị chặn.
- Log ghi rõ lý do bị chặn.

### Task 5.3 - Audit screen

Tạo trang xem lịch sử:

- agent nào chạy
- ai kích hoạt
- hội thoại nào
- output gì
- đã gửi hay chỉ nháp
- lỗi gì

Tiêu chí nghiệm thu:

- Tìm theo agent, account, conversation, trạng thái.
- Có thể debug lỗi từ UI mà không phải mở log server.

## Thứ tự triển khai khuyến nghị

1. Phase 1: Agent Core.
2. Phase 2: Bot Reply trong Toolchat với mode `draft`.
3. Bật `auto_send` có kiểm soát.
4. Phase 4: Login nội bộ nếu production cần phân quyền thật.
5. Phase 3: OAuth Provider cho ChatGPT/Codex/MCP.
6. Phase 5: Rate limit, audit, hardening.

Lý do để OAuth Provider sau core bot: OAuth chỉ hữu ích khi ChatGPT/Codex cần connect vào Toolchat. Còn nút robot trong chính Toolchat cần agent service ổn định trước. Nếu làm OAuth trước khi có agent service, client ngoài connect vào cũng chưa có API đủ tốt để dùng.
