Drive an agent from Lark / Feishu
Send a task in a Lark (international) or Feishu (China) chat and your agent does it — from your phone, no terminal needed after setup. You bring your own bot; its credentials stay on your machine.
One bot → one owner (concord im) → many bound chats → each chat drives one agent. The owner holds the bot's single connection, keeps the chat→room bindings, and relays messages both ways. Each agent is just a resident process doing the work in a room.
Prerequisites
- Node ≥ 20 and the
concordCLI (npm i -g concord-agent). - A coding-agent CLI installed and logged in —
claudeby default;codex/geminialso work. - A Lark or Feishu account where you can create a custom app. Not an org admin? See enterprise tenants.
1 · Create + log in your bot — scan one QR
concord login lark --qr # Lark (international)
concord login feishu --qr # Feishu (China)
A QR renders in your terminal. Open the Lark/Feishu app on your phone and scan it, then tap Agree. That one scan:
- Creates a bot app in your tenant, named Concord · <your-name>, with the message-send / read / group-info / card scopes it needs.
- Configures event subscription over a long connection automatically — no developer console, no publishing a version, no manual scope hunt.
- Saves the bot's
appId+appSecretto~/.concord/creds.json(0600) — local only, never passed via env or args at runtime.
Then put the bot in a chat:
- Group: create a group and add the bot.
- Private: Lark can't start a true 1:1 with a bot — make a group with just you + the bot, then open the bot to DM it. (This “you + the bot” group is also what the no-@ convenience below needs.)
You can also create a Custom App manually (Add the Bot capability, set event subscription to long connection / WebSocket — not webhook, subscribe to im.message.receive_v1, add the message scopes, and release a version), then store creds with concord login lark --app-id <id> --app-secret <secret>. The QR flow avoids all of that.
2 · Start the IM owner
concord im # background daemon — one per bot
concord im status # is it up?
concord im logs -f # what is it doing
concord im stop # stop it (bindings are kept)
Don't run two concord im for the same app, and don't point another listener at the same bot — Lark delivers each event to only one connection, so a second listener steals messages at random.
3 · Bind a chat to an agent
In the chat (group: @ the bot; private: just send):
/concord-bind
The bot replies with a ready-to-copy command. Run it on the machine where the agent should work — its working directory is wherever you run it:
concord host claude --bind <chat_id> # the bot fills in the chat_id for you
# for a group the command includes a budget:
concord host claude --bind <chat_id> --budget 1000000
The agent comes up, joins its room, and posts a short “connected” note in the chat. From now on, just send tasks in the chat. Re-bind to a different agent with --force; want a different folder? cd there first (or pass --cwd).
4 · Daily use — from the chat
- Private chat: send anything — no @ needed.
- Group: @ the bot. Exception: a group with only you + the bot is treated like a private chat — no @ needed (the scopes for this come with
--qr; add a second person and it reverts to @-only within a minute). - Per task you'll see:
🤖 <Agent> 正在处理…→ progress lines (✏️ Edit app.js,▶️ npm test, …) → the result. - In-chat commands:
/concord-bind·/concord-unbind·/agents(list bound agents) ·/usage(token usage) ·/help. Room commands work here too:/compact·/clear·/context— see In-room commands.
5 · Manage from the terminal
concord list # every agent + the owner, with status + bound chat
concord bindings # chat → room bindings with end-to-end health
concord restart <id> # restart, keeping its binding
concord shutdown # stop owner + all agents, KEEP configs + bindings (reversible)
concord up # bring the whole fleet back — no re-binding
See the CLI reference for the full command set and the fleet lifecycle.
Token safety
- Idle = zero tokens. The session blocks on input while idle; a message wakes it.
- Group bindings default to a 1,000,000-token budget — once a chat is “anyone can trigger it”, the budget is the gate. The budget is a lifetime cumulative cap, not a daily window: usage only ever grows (a restart,
/compactor/clearnever resets it). You get a warning at 80%; over the cap the agent pauses and tells the chat, and the only way to resume is an explicitconcord budget <id> --reset— it never silently recovers on its own. - A per-turn timeout (default 6h — a liveness guard against a wedged adapter, not a work limit) cancels a hung turn so it can't burn in the background; the next message retries.
Enterprise tenants & troubleshooting
If you're not an org admin, the scan may succeed but the bot stays pending admin approval — creds are saved but messages won't route until an admin approves the app. Easiest workarounds: use a personal account (Feishu: register → pick 「个人使用」; Lark: sign up at larksuite.com), or ask your admin to create a Custom App and hand you the App ID + Secret.
| Symptom | Most likely cause |
|---|---|
no <platform> creds | Run concord login lark --qr (or feishu) and scan. |
| Scanned, creds saved, but messages don't reach the agent | Enterprise tenant — the app is pending admin approval. Approve it, or use a personal account. |
| Group only answers when @-mentioned | Expected — unless it's a solo group (you + bot), which the --qr scopes allow. |
| Bound, but nothing routes | Is the owner running? concord list should show im-… as running (or run concord up). |
| Messages land erratically | Two listeners on one bot — keep one concord im per app. |
| Sender shows as a short id, not a name | Add the optional contact:contact.base:readonly scope. |