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
The CLI does not have a separate Chat configuration command. Use the shared Plugin Resource manager to create a channel Resource, then save its Resource ID in the current Agent Binding:
city plugin resource create chat --interactive
city plugin config chat <agent_id> --resources '["telegram-a1b2c3"]'Use the generic Plugin Action command when you need to invoke a Chat Action from the terminal:
city plugin action chat send <agent_id> --input '{"chat_key":"telegram:123456","text":"Done"}' --token <token>SDK Assembly
In embedded SDK scenarios, ChatPlugin receives already constructed channel objects. The SDK host directly provides the ID, display name, and runtime credentials; it does not read City Plugin Resources:
import {
ChatPlugin,
FeishuChannel,
QqChannel,
TelegramChannel,
} from "@downcity/plugins/chat";
const plugin = new ChatPlugin({
channels: [
new TelegramChannel({
id: "telegram-sdk",
name: "SDK Telegram Bot",
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,
},
}),
],
});Plugin Resource Schema, Resolver, encrypted storage, and Binding ID resolution belong to the CLI control plane. They do not enter @downcity/agent or become a Store dependency of ChatPlugin.
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.0Use the isolated Chat plugin subpath:
import { ChatPlugin, FeishuChannel } from "@downcity/plugins/chat";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.run_action({
plugin: "chat",
action: "send",
payload: {
chat_key: "telegram:123456",
text: "Done",
},
});The common 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.run_action({
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.