Bundle
@deepseek-ai/dsh-lark-bridge
Feishu (Lark) IM bot channel for DeepSeek Harness agents — per-chat sessions, native thinking process, interactive approval cards, WebSocket long connection.
- Source
- moyu-good
- stars
- 1 stars
- License
- BSD-3-Clause
- Updated
- Updated 14 days ago
Readme
<p align="center">
<img src="https://img.shields.io/badge/dsh--lark--bridge-0.3.1-blueviolet" alt="version">
<img src="https://img.shields.io/badge/tests-273-green" alt="tests">
<img src="https://img.shields.io/badge/license-BSD--3--Clause-blue" alt="license">
<img src="https://img.shields.io/badge/transport-WebSocket%20long--connection-orange" alt="transport">
</p>
<h1 align="center">🕊️ dsh-lark-bridge</h1>
<p align="center">
<b>Run a full DeepSeek Harness coding agent inside Feishu / Lark</b><br/>
<i>Native thinking process, approval cards, live goal/todo cards, subagent fan-out,
bilingual slash panel — no public webhook URL needed.</i>
</p>
<p align="center">
<a href="README.zh.md">中文</a> · <a href="#quick-start">Quick Start</a> · <a href="#features">Features</a> · <a href="#slash-commands">Slash Commands</a> · <a href="#configuration">Configuration</a> · <a href="#architecture">Architecture</a> · <a href="#development">Development</a>
</p>
---
## What is this?
`dsh-lark-bridge` is a **Feishu/Lark IM channel for DeepSeek Harness** — a plugin that makes
your coding agent work right inside a chat. Each conversation (DM or group) drives its own
dsh agent with:
- **Native thinking process** — model reasoning renders as Feishu's own "thinking" message,
tool calls with icons, results as code blocks. No black box.
- **Interactive approval cards** — operations needing confirmation become clickable
cards (Allow once / Deny), with the decider written back.
- **Live lifecycle reactions** — `OK` → `THINKING` → `DONE` / `ERROR` on every message.
- **Live goal & todo cards** — goal phase changes and todo snapshots update a card in the
chat, so a long-running task is never a silent gap.
- **WebSocket long connection** — no public callback URL, no reverse proxy.
Feishu is the carrier; the work is still done by DeepSeek Harness itself.
## ✨ Features
| | |
|---|---|
| 🧠 **Native thinking process** | `cot` renders reasoning as Feishu's native thinking message; older clients fall back to `stream` typewriter card |
| ✅ **Live reactions** | `OK` → `THINKING` → `DONE`/`ERROR`, states replace each other, configurable |
| 🗂️ **One agent per conversation** | `sessionScope`: whole chat / topic thread / single sender; sessions persist across restarts |
| 📋 **Approval cards** | Host approval questions render as Allow-once / Deny cards; decision + decider written back |
| 🎯 **Goal cards** | Live goal phase (active/paused/blocked/complete) updates a card; `/goal` works, `autoResumeGoals` re-arms after restarts |
| ✅ **Todo cards** | `todo_write` snapshots update a live card in the chat |
| 🧑💻 **Subagent fan-out** | Workflow runs stream as text lines: run start, child open, child end, run end |
| 📦 **Compaction transparency** | "Compacting…" → summary text + released tokens; prunes report trimmed count |
| ⏰ **Scheduled reminders** | `schedule_create/list/delete` tools + `/schedules` view (compose `@deepseek-ai/dsh-schedule` yourself; the plugin ships the full listener) |
| 🔍 **Session history search** | `/sessions <keyword>` full-text searches this chat's stored history with snippets |
| ⚙️ **Background job notifications** | `run_in_background` jobs and direct subagents announce their terminal outcome in the chat |
| 🧩 **Skill ecosystem surface** | `/skills` lists the workspace's discoverable dsh skills; `/skills <name>` peeks at one — the dsh plugin ecosystem from inside Feishu |
| 🤖 **Model switching** | `/model` views the deployment default model; `/model <provider>/<model>` switches it through the host `saveSelection` seam (persistent with a settings layer) |
| 🖥️ **PC-parity tooling** | Compose `dsh-terminal*` / `code-runtime-worker-thread` / `dsh-mcp-client` in your profile patch and the chat agent gains persistent PTY sessions, Code Mode `run_code`, and external MCP servers — the same official opt-in families the desktop UI offers |
| 🗺️ **Workspace visibility** | `/ws` lists registered workspaces and marks where new sessions land |
| ⚡ **Full slash panel** | `/stop /help /preset /sessions /tools /schedules /jobs /feedback /context /audit /config /skills /model /ws` plus host commands (`goal`, `plan`, `compact`, `permission`) |
| 🌐 **Bilingual commands** | Panel and `/help` follow the platform: English on international Lark, Chinese on domestic Feishu; `locale` overrides |
| 🖼️ **Image input (opt-in)** | `attachImages` downloads chat images into the host attachment store |
| 📎 **File delivery** | Agent `send_file` delivers files with caption into the chat |
| 🔑 **QR onboarding** | First boot prints a QR code; scanning creates the Feishu app (event subscription included), credentials persist |
| 🔒 **Authorization narrowing** | `senderAllowlist` / `groupAllowlist` / `approvers` narrow further than the app's visibility scope |
| 🧩 **Deep dsh adaptation** | Everything goes through host service contracts: `agents`, `agentPresets`, `agentDefaultModel`, `settings`, `workspaceRegistry`, `loader`, `invariants`, `approval`, `goals` — self-contained, no host source needed |
## 🚀 Quick Start
### One command (recommended)
```sh
npm i -g dsh-lark-bridge # or: npx dsh-lark-bridge@latest start
dsh-lark-bridge start
```
`start` installs dsh if needed, wires the plugin into a profile, patches the
config, and boots the bridge. The console prints a QR code → scan with Feishu
to create the app → fill in your DeepSeek API Key in Settings → Models → DM
the bot or @ it in a group.
Daily loop: `dsh-lark-bridge status` · `logs` · `restart` · `stop`.
### Manual (already using dsh)
```sh
npx @deepseek-ai/dsh plugin --profile web add github:moyu-good/dsh-lark-bridge \
&& npx @deepseek-ai/dsh web
```
> Already using `dsh`? Drop the `npx @deepseek-ai/` prefix.
The package ships **prebuilt** (`lib/` is committed) — no build step on install.
A `prepare` hook rebuilds automatically only when the compiled output is missing
(e.g. a source clone without the committed output).
## 💬 Slash Commands
| Command | Description |
|---|---|
| `/stop` | Cancel the running turn |
| `/help` | Show this listing |
| `/preset` | View / switch agent preset (standard / code / minimal / cordis) |
| `/sessions` | List this chat's session history |
| `/tools` | View / deny / allow tools at runtime |
| `/schedules` | View this chat's scheduled reminders |
| `/jobs` | View this chat's background jobs |
| `/audit` | Operation audit summary for the session |
| `/context` | View current context token pressure |
| `/config` | View the bridge's live configuration |
| `/goal` | View / set the goal (host) |
| `/plan` | Enter / leave plan mode (host) |
| `/compact` | Compact older history (host) |
| `/feedback` | Rate the last answer: `positive`/`negative` + optional note |
| `/permission` | Switch permission preset (host) |
Panel descriptions are bilingual: **English** when the platform domain is
`open.larksuite.com` (international Lark), **Chinese** for `open.feishu.cn`
(domestic Feishu). Set `locale: zh|en` to force one.
## vs. other Feishu/Lark bridges
| Capability | **dsh-lark-bridge** | xmanrui/dsh-im | omdsh-dev/dsh-lark | AX1202/ax-feishu-bridge |
|---|---|---|---|---|
| Positioning | Deep Harness channel | Multi-platform gateway | Scan-to-use | Pi + DSH dual bridge |
| Native thinking process (Feishu CoT) | ✅ | — | — | — |
| Approval cards + decider trail | ✅ | — | — | remote approve |
| Live goal/todo cards | ✅ | — | — | — |
| Workflow fan-out + phase/log lines | ✅ | — | — | — |
| Compaction transparency | ✅ | — | — | — |
| Goal auto-resume after restart | ✅ | — | crash-safe | — |
| Bilingual slash panel sync | ✅ | — | — | panel buttons |
## ⚙️ Configuration
| Field | Default | Meaning |
|---|---|---|
| `appId`, `appSecret` | first-boot QR registration | Feishu/Lark app credentials |
| `domain` | Feishu | Open-platform domain; Lark: `https://open.larksuite.com` |
| `locale` | `auto` | Command language: `auto` (Lark→en, Feishu→zh) / `zh` / `en` |
| `cwd` | host process cwd | Absolute workspace directory for chat agents |
| `provider`, `model` | host `agentDefaultModel` | Model routing for chat agents |
| `preset` | roster default | Agent preset chat agents join |
| `sessionScope` | `chat` | `chat` / `chat-thread` / `chat-sender` |
| `output` | `cot` | `cot` (native thinking process) or `stream` (typewriter card) |
| `showProcess` | `true` | Show reasoning and tool calls |
| `reactionFeedback` | `true` | Live reaction feedback |
| `hideProcessWhenDone` | `false` | Hide finished process (`cot` only) |
| `attachImages` | `false` | Pass chat images to the model |
| `syncSlashCommands` | `true` | Publish commands to bot's `/` panel (reconciles: creates missing, removes stale, refreshes drifted descriptions) |
| `autoResumeGoals` | `false` | Re-arm an active goal when a session returns after a restart |
| `approvalReminderMs` | `0` | Nudge the chat when an approval card is unanswered this many ms (0 = off) |
| `denyTools` | `[]` | Tools chat agents may not call |
| `requireMention` | `true` | In groups, only respond when @-mentioned |
| `senderAllowlist` | `[]` | Open ids allowed to DM |
| `groupAllowlist` | `[]` | Only these `oc_…` group chats when non-empty |
| `approvers` | `[]` | Open ids allowed to answer approvals |
| `outbound.allowedFileDirs` | unset → file sending disabled | Directories `send_file` may read **local** paths from. Required for delivering generated artifacts (HTML reports, screenshots, documents). Example: `outbound: { allowedFileDirs: ['/home/user/work'] }` |
> ⚠️ **File delivery is default-deny.** Without `outbound.allowedFileDirs`,
> `send_file` with a local path fails with
> `local file source requires outbound.allowedFileDirs to be configured` —
> the agent appears to send, nothing arrives. URLs and raw buffers always work.
Credentials resolve in three layers, later wins: bundle patch config → settings
document plugin section → first-boot QR registration.
## 🔐 Required app permissions
A **newly created** Feishu app needs these scopes published before the panel
and messaging work. The QR onboarding flow grants them automatically; a
manually created app must add them in Developer Console → Permissions, then
**create and publish a version** (scopes added after the last publish are not
visible to the API until a new version ships):
| Scope | Needed for |
|---|---|
| `application:app_slash_command` (read + write) | Slash command panel — without it, `syncSlashPanel` fails with `99991672` and the `/` list stays empty |
| `im:message` | Send and receive messages |
| `im:message:readonly` | Read message content |
| `im:message.receive_v1` event | Receive message events (Events & Callbacks → long connection) |
| `im:resource` | Upload / send images and files |
| `im:chat:read` | Group chat info (group scenarios) |
| `im:message.reactions:read` / `write_only` | Live reaction feedback |
Debug with the API directly — the console page shows **granted**, the API shows
what the **published version** carries:
```sh
# 1. token
curl -s -X POST https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal \
-H 'Content-Type: application/json' \
-d "{\"app_id\":\"$APP_ID\",\"app_secret\":\"$APP_SECRET\"}" | jq -r .tenant_access_token
# 2. slash commands (should list your commands after sync)
curl -s "https://open.feishu.cn/open-apis/application/v7/app_slash_commands?page_size=50" \
-H "Authorization: Bearer $TOKEN"
# 3. published scopes (check application:app_slash_command is present)
curl -s "https://open.feishu.cn/open-apis/application/v6/applications/$APP_ID/app_versions?lang=zh_cn" \
-H "Authorization: Bearer $TOKEN"
```
The slash panel sync runs on session create/resume — after granting the scope,
send the bot one message to trigger it.
## 🧭 Architecture
```
┌─ CLI 引导器 (bin/dsh-lark-bridge) ── npm i -g → dsh-lark-bridge start
│ 安装 dsh / 创建 profile / 写入 cordis.patch.yml / 启动
└──────────────────────────────────────────┐
▼
Feishu / Lark ── WebSocket 长连接 ──► dsh-lark-bridge (dsh 进程内的 feishu-channel 插件)
(聊天/审批/图片) │
▼
host 服务契约: agents / sessions / tools / approval /
goal / workspace / settings / commands
│
▼
DeepSeek Harness 本体
```
The bridge runs **inside the dsh process** as the `feishu-channel` plugin — it is
not a separate server. `dsh-lark-bridge start` (or manually
`npx @deepseek-ai/dsh web`) boots dsh with this plugin composed; the plugin
opens the WebSocket long connection and drives everything from there. Any
launcher (shell script, systemd, supervisor) can host it; it has no dependency
on any other agent framework.
## 🛠️ Development
```sh
pnpm install
pnpm run build # clean + tsc + tsdown (emits into lib/, committed)
pnpm test # vitest (273 tests)
node plugin-contract-test.mjs # standalone contract tests
```
The repo is self-contained: only published packages
(`@deepseek-ai/cordis`, `@deepseek-ai/schemastery`, `@larksuite/channel`) are
dependencies, no host source needed.
**Packaging notes** (why `lib/` is committed):
- Git-dependency installs (`github:user/repo`) never ran a build, and without a
committed `lib/` the plugin failed at boot with `ERR_MODULE_NOT_FOUND` — fixed
by committing the compiled output.
- The `prepare` hook is a safety net for source clones: it exits immediately
when `lib/` exists and only rebuilds when it is genuinely missing.
- `build` cleans `lib/` first (tsdown itself runs `clean: false` because its
entry points live inside the output dir).
## 📋 Known limitations
- Configuration is read once at startup; changes need a restart
- Events during a long-connection outage are not replayed (transport has no cursor)
- The Feishu app must use **long-connection** event subscription (self-built app);
webhook mode receives no events
- `schedule_create/list/delete` tools require composing `@deepseek-ai/dsh-schedule`
in your dsh profile (the bridge already listens for `schedule/change` and
renders `/schedules`; the tools are the model-side half)
## 📄 License
BSD-3-Clause. Architecture inspired by [dsh-lark](https://github.com/Roy-oss1/dsh-lark) (also BSD-3-Clause).
Install
dsh plugin --profile web add github:moyu-good/dsh-lark-bridge
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install deepseek-ai-dsh-lark-bridge from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.