version tests license transport

๐Ÿ•Š๏ธ dsh-lark-bridge

Run a full DeepSeek Harness coding agent inside Feishu / Lark
Native thinking process, approval cards, live goal/todo cards, subagent fan-out, bilingual slash panel โ€” no public webhook URL needed.

ไธญๆ–‡ ยท Quick Start ยท Features ยท Slash Commands ยท Configuration ยท Architecture ยท Development

--- ## 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 ` 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 | | โšก **Full slash panel** | `/stop /help /preset /sessions /tools /schedules /jobs /feedback /context /audit /config` 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 ```sh npx @deepseek-ai/dsh plugin --profile web add github:moyu-good/dsh-lark-bridge \ && npx @deepseek-ai/dsh web ``` 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. > 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 ``` 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. `npx @deepseek-ai/dsh web` (or `--profile chat`) 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 (272 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).