> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tuturuuu.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Agent Deployment

> Operate apps/web-hosted Discord and Zalo AI agents backed by Chat SDK and AI SDK.

The internal Infrastructure → AI Agents module manages root-only agent configs
that are served by `apps/web` webhook routes. It is the first production path
for bidirectional agent presence across Discord and Zalo.

## Runtime Boundary

Deploying an agent enables an `apps/web` webhook endpoint:

```txt theme={null}
/api/v1/webhooks/ai-agents/:adapter/:channelId
```

`apps/web` loads the channel config from root `workspace_secrets`, creates a
Chat SDK runtime through `@tuturuuu/ai/chat-sdk`, and runs the response loop with
AI SDK `ToolLoopAgent`.

Production deployments use Redis so thread subscriptions, dedupe, and locks
survive cold starts and multiple instances. Runtime resolution checks, in order:

1. Root workspace secret `AI_AGENT_CHAT_SDK_STATE_REDIS_URL`.
2. Environment variables `AI_AGENT_CHAT_SDK_STATE_REDIS_URL`, `REDIS_URL`, or
   `DOCKER_WEB_REDIS_URL`.
3. The bundled blue/green Docker Redis service at `redis://redis:6379` when a
   blue/green runtime mount or Docker SRH URL is present.

Local development may use the Chat SDK memory state adapter when no durable
Redis runtime is configured.

Agent response loops and manual external-chat draft generation use the shared
Tuturuuu AI memory layer. Memory is scoped to the operator or channel actor plus
the channel workspace, then stored through the first-party memory sidecar using
the `ai_agents` product metadata. Do not add agent-specific browser memory
calls; all recall, writes, and deletes must stay behind the server-owned
`@tuturuuu/ai/memory` boundary and workspace memory APIs.

Webhook URLs are generated from the canonical public platform origin, not the
incoming request host. Set `AI_AGENT_WEBHOOK_ORIGIN` for an explicit override;
otherwise `WEB_APP_URL`, `NEXT_PUBLIC_WEB_APP_URL`, `NEXT_PUBLIC_APP_URL`,
`PLATFORM_BUILD_DEPLOYMENT_URL`, and production fallback `https://tuturuuu.com`
are used before local request-origin fallback.

## Configuration Storage

Agent configuration stays under root workspace secrets:

```txt theme={null}
AI_AGENT_REGISTRY:<agentId>:meta
AI_AGENT_REGISTRY:<agentId>:instructions
AI_AGENT_REGISTRY:<agentId>:channel:<channelId>:meta
AI_AGENT_REGISTRY:<agentId>:channel:<channelId>:secret:<name>
AI_AGENT_IDENTITY:<wsId>:zalo:<oaId>:<zaloUserId>
AI_AGENT_ZALO_PERSONAL_ENABLED
```

Admin APIs redact channel secrets. One-time generated values are shown only
after rotation.

Feature flags for this surface are also root workspace secrets. Experimental
personal Zalo account support requires
`AI_AGENT_ZALO_PERSONAL_ENABLED = true`; do not gate it with process
environment variables.

External chat history is mirrored separately in the `private` schema:

```txt theme={null}
private.ai_agent_external_threads
private.ai_agent_external_messages
```

Only server-owned `apps/web` routes call the private RPCs for listing,
syncing, drafting, and sending. Browser clients never read these private tables
directly.

## Chat Discovery

The shared chat UI displays mirrored external AI-agent threads as read-only AI
conversations in the workspace selected for the agent channel. This works in
`apps/web` and the standalone `apps/chat` app. Chat users can inspect mirrored
history there, while root AI-agent admins can use the setup, operations, and
manual-response controls from Infrastructure > AI Agents or the `apps/chat`
agent details sidebar.

The root workspace may still show enabled agent channel setup entries so
operators can jump to channel configuration. Those setup entries are not the
message mirror; mirrored external threads use `ai-agent-thread-<uuid>`
conversation IDs.

Root AI-agent admins can also manage those setup entries from the standalone
`apps/chat` internal workspace. The `internal` workspace resolves to
`ROOT_WORKSPACE_ID`, and admin-visible setup conversations include the minimum
agent/channel IDs needed by the sidebar operations panel. Non-admin chat
viewers still receive scrubbed setup metadata.

## Manual Response Console

Infrastructure > AI Agents is the full setup and operations surface:

* Configure Discord and Zalo credentials, workspace mapping, auto-response, and
  history sync per channel.
* Inspect mirrored external threads and messages.
* Trigger an on-demand external history sync when the adapter supports it,
  including phone-approved transfer sync for personal Zalo accounts.
* Generate a draft with a custom operator prompt and mirrored history context.
* Send the reviewed draft back to the external platform exactly as edited.

Discord can fetch recent thread/channel history through the Chat SDK adapter
when the bot token has the required scopes. Official Zalo channels still rely on
webhook/runtime events. Personal Zalo channels can import Zalo Web-visible
history through **Sync history** and can request phone-approved mobile transfer
history through **Sync phone** when older messages are only present on the
paired mobile device.

The same external-thread operations are available from `apps/chat` for root
AI-agent admins when an external AI-agent thread is selected: sync external
history, draft a manual response, send the reviewed response, and refresh the
mirrored conversation.

## Testing From Apps/Chat

Use `apps/chat` as the operator smoke-test surface after deployment:

1. Open the `internal` workspace and select the AI-agent setup conversation.
2. Confirm setup data loads instead of the metadata-hidden notice.
3. Run **Test** as a readiness check for required credentials and channel
   status. The diagnostics list should pass for agent enabled, channel enabled,
   deployed status, required secrets, webhook URL, workspace mapping, adapter
   account mapping, and recent error state. This does not replace a live
   external-platform round trip.
4. For Discord, set the generated webhook URL as the Interactions Endpoint URL
   so Discord performs its endpoint verification. Then install the app and send
   a live mention/message in the mapped channel. The official references are
   [Discord interactions](https://docs.discord.com/developers/interactions/overview)
   and the
   [Discord quickstart](https://docs.discord.com/developers/quick-start/getting-started).
5. For personal Zalo, set the channel to `Personal account`, open the
   operations tab, run **Pair QR**, scan the QR code from the Zalo mobile app,
   then run **Validate**, **Sync history**, **Sync phone**, **Deploy**, and
   **Start**. Approve the transfer-sync prompt on the phone when **Sync phone**
   requests it. Use a second Zalo account or group sender for the live
   inbound-message test.
6. From the setup conversation, open the **Thread** tab to list recent mirrored
   external threads for the selected agent channel. Use **Sync external thread**
   from that list when a mirrored thread exists.
7. Select a mirrored `ai-agent-thread-<uuid>` conversation and use **Sync
   external thread**. A zero-message result means the adapter found no newer
   messages.
8. Draft and send a manual response from the thread panel, then confirm the
   mirrored conversation refreshes.

## Discord And Zalo Setup

For Discord, configure the app with the generated webhook URL, then store the
application ID, public key, bot token, guild ID, and optional mention role IDs
on the Discord channel.

Discord message events require Gateway delivery. For a self-hosted watcher,
run `apps/discord/ai_agent_gateway_watcher.py` with:

```txt theme={null}
DISCORD_AI_AGENT_GATEWAY_BOT_TOKEN=<the AI-agent Discord bot token>
DISCORD_AI_AGENT_GATEWAY_PLATFORM_URL=https://<platform>
DISCORD_AI_AGENT_GATEWAY_WATCHER_SECRET=<root watcher secret>
```

Store the same `AI_AGENT_DISCORD_GATEWAY_WATCHER_SECRET` value as a root
workspace secret in apps/web. The watcher calls the deployed apps/web watcher
config endpoint to discover enabled, deployed Discord channel webhooks, and
apps/web only returns channels mapped to `ROOT_WORKSPACE_ID`. Set
`DISCORD_AI_AGENT_GATEWAY_CHANNEL_ID` when one watcher should pin a single
root-internal Discord channel.

The watcher forwards raw Gateway packets using the Chat SDK Discord forwarding
contract (`GATEWAY_<event>` JSON plus `x-discord-gateway-token`) so apps/web
continues to own AI-agent runtime, identity mapping, mirroring, and responses.
Gateway-token forwarded webhook requests are rejected for non-root workspaces;
normal Discord HTTP interactions still use the configured channel webhook path.
Keep the bot token value in runtime secrets only.

For local smoke tests, `DISCORD_AI_AGENT_GATEWAY_WEBHOOK_URL` may be set to one
generated Discord channel webhook URL instead of using auto-discovery.

For Zalo, configure the bot webhook URL in the Zalo Bot dashboard, set the
secret token to the channel's `webhookSecret`, and store the bot token plus OA ID
on the Zalo channel.

For experimental personal Zalo account testing, set the Zalo account mode to
`Personal account`, then use **Pair QR** from the `apps/chat` setup
conversation or the shared AI-agent operations panel. The QR flow runs
`zca-js` `loginQR()` server-side, stores `personalCookieJson`, `personalImei`,
and `personalUserAgent` as channel secrets, and records `zaloPersonalOwnId`
after confirmation. The browser receives only QR/session status, never raw
cookies, IMEI, or user agent values.

Manual cookie JSON, IMEI, and user agent entry remains a fallback for local
debugging. After either setup path, use **Validate** to confirm login, then
use **Sync history** to import Zalo Web-visible personal user and group threads
into Tuturuuu Chat. When the mobile device owns additional history, use
**Sync phone** and approve the transfer-sync request in the Zalo mobile app.
The phone-transfer action is owned by `apps/web`; `apps/chat` reaches it only
through the internal API proxy, and the browser never receives cookies, IMEI,
user agent values, private transfer keys, or raw transfer payloads. Historical
sync persists mirrored conversations and messages only; it does not trigger AI
auto-responses for old messages. Deploy the channel and use **Start**/**Stop**
for the live listener lifecycle.
Personal Zalo channels do not receive webhooks, and only one Zalo Web listener
can be active per personal account at a time. Use test accounts only; the
upstream `zca-js` package is an unofficial Zalo Web automation API and can
trigger account locks or bans.

For experimental root-internal external chatbots, choose the explicit
`Internal` workspace option in the AI-agent workspace picker. It stores
`workspaceId = ROOT_WORKSPACE_ID` on the channel and keeps the runtime, test,
deploy, and mirror paths scoped to the internal workspace.

## Mapped User Requirement

Agents never write as a global service user in v1. Each inbound external user
must map to a Tuturuuu workspace user before task or calendar tools can run.

Discord uses the existing `discord_guild_members` mapping for the configured
guild. Zalo uses root secret identity links:

```txt theme={null}
AI_AGENT_IDENTITY:<wsId>:zalo:<oaId>:<zaloUserId> = <platformUserId>
```

The mapped Tuturuuu user's permissions are enforced. Task writes require
`manage_projects`; calendar writes require `manage_calendar`. If permissions are
missing, the agent replies with a permission-aware message instead of writing.

## Tool Allowlist

V1 exposes only workspace context, members, task handoff, and calendar tools.
Finance, time tracking, memory, image generation, web search, delete tools, and
broad marketplace tool selection stay out of scope until there is a separate
approval and audit model.
