chat Plugin
Channel runtime, queue worker, chat authorization, chat system text, and chat-facing plugin actions
chat Plugin
chat is the channel runtime plugin.
It owns:
- channel state
- the chat queue worker
- the chat queue store
- chat authorization hooks and resolves
- chat-facing actions
- chat system text
Main shape
lifecycleactionshooksresolvessystem
What it is for
Use chat when you need:
- Telegram, Feishu, or QQ chat channel runtime
- one queue worker per agent/plugin instance
- chat delivery and inbound message execution
- inbound user authorization, principal observation, and role resolution
How users use it
Most users do not control the channel runtime directly. They use the downcity chat shortcuts that Downcity exposes for chat sessions:
downcity chat
downcity chat list
downcity chat info --chat-key <chatKey>
downcity chat send --chat-key <chatKey> --text "Done"
downcity chat react --chat-key <chatKey> --message-id <messageId> --emoji "👍"
downcity chat context --chat-key <chatKey>
downcity chat history --chat-key <chatKey> --limit 30
downcity chat delete --chat-key <chatKey>downcity chat with no subcommand opens the interactive chat account manager. Use it to add Telegram, Feishu, or QQ accounts before connecting an agent to those accounts.
Downcity intentionally hides channel-runtime actions such as status, test, open, close, reconnect, configuration, and configure from the user-facing downcity chat shortcut. Those actions exist inside the plugin runtime, but they are not the normal CLI path for users.
SDK Assembly
In embedded SDK scenarios, ChatPlugin receives channel objects. Each channel owns its env or account binding details:
import {
ChatPlugin,
FeishuChannel,
QqChannel,
TelegramChannel,
} from "@downcity/plugins";
const plugin = new ChatPlugin({
channels: [
new TelegramChannel({
env: {
TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN,
},
}),
new FeishuChannel({
env: {
FEISHU_APP_ID: process.env.FEISHU_APP_ID,
FEISHU_APP_SECRET: process.env.FEISHU_APP_SECRET,
FEISHU_DOMAIN: process.env.FEISHU_DOMAIN,
},
}),
new QqChannel({
env: {
QQ_APP_ID: process.env.QQ_APP_ID,
QQ_APP_SECRET: process.env.QQ_APP_SECRET,
QQ_SANDBOX: process.env.QQ_SANDBOX,
},
}),
],
});channelAccountId is still available as an account-pool binding, but the SDK-first path is to pass channel-specific env to the channel object.
Feishu dependency
@downcity/plugins keeps the Feishu/Lark SDK outside its default production dependencies. Apps that do not enable Feishu can import @downcity/plugins without installing Feishu runtime dependencies.
If your host enables FeishuChannel, install the SDK in that host:
npm install @larksuiteoapi/node-sdk@^1.66.0The import path does not change:
import { ChatPlugin, FeishuChannel } from "@downcity/plugins";When Feishu is enabled without the SDK installed, the runtime error tells you which package to install before enabling channel "feishu".
SDK action use
In an embedded SDK scenario, call the visible chat actions by plugin name:
await agent.plugins.runAction({
plugin: "chat",
action: "send",
payload: {
chatKey: "telegram:123456",
text: "Done",
},
});The main user-facing actions are list, info, send, react, context, history, and delete.
Built-in Authorization
Chat authorization now belongs to ChatPlugin itself. You no longer attach a separate authorization plugin.
It participates in inbound chat execution through three chat plugin points:
chat.observePrincipal: records observed users and chatschat.authorizeIncoming: decides whether the current message may enter the agent based on roles and permissionschat.resolveUserRole: enriches history / queue metadata with the current user role
Authorization actions also live under chat:
| Action | Purpose |
|---|---|
authorization-snapshot | read the authorization catalog, config, observed users, and observed chats |
authorization-read-config | read the current authorization config |
authorization-write-config | replace the authorization config |
authorization-set-user-role | set a user's role for a channel |
SDK example:
await agent.plugins.runAction({
plugin: "chat",
action: "authorization-set-user-role",
payload: {
channel: "telegram",
userId: "12345678",
roleId: "admin",
},
});Important runtime semantics
- queue worker lifecycle belongs to the plugin instance
- channel bot state also belongs to the plugin instance
- inbound authorization belongs to
ChatPlugin, while still being dispatched through generic plugin hooks / resolves Agentonly assembles it; it does not own chat domain state itself
Public status
ChatPlugin is exported from @downcity/plugins.
That makes it one of the few lifecycle-oriented built-ins that SDK users may still directly understand and attach in local embedded scenarios.