← Back to home@IvanWu2015

dsh-connect

Bridge DeepSeek Harness (DSH) agents to Feishu/Lark & DingTalk — chat, stream replies, and arrange work from your messaging app.

Stars
1
Language
TypeScript
Created
Aug 15, 2026
Updated
Sep 3, 2026
GitHub repo

Introduction

dsh-connect

English | 中文

Connect DeepSeek Harness (DSH) agents to chat platforms — Feishu / Lark first, with DingTalk and others to follow. Send tasks from your messaging app, watch the agent execute with live streaming output, keep multi-turn context, and get result summaries pushed back when a task finishes.

Features

  • Bidirectional messaging: Feishu messages → DSH agent (agent.followup); agent replies stream back to Feishu as typewriter-style cards.
  • Multi-turn context: each Feishu chat (DM or group) is bound to a DSH Session, automatically resumed after a process restart.
  • Work arrangement: pushes a result-summary card when a task ends; ctx.connect.notify() lets goals/jobs hooks push progress proactively.
  • Task-end stats: when a task finishes, a card reports the model used, input/output/cached tokens, step count, duration and context usage, with a /compact suggestion when the context is ≥ 75% full.
  • Notification levels: full (stream everything) / important (key milestones, default) / result (answer only) — switchable per chat via the settings menu or /notify, persisted across restarts.
  • Instant feedback + proactive progress: every task is acknowledged the moment it is received (“✅ 已收到,开始处理”, with the queued-message count when busy), key milestones (thinking, tool calls with step counters, questions, permissions) react live, and a configurable watchdog sends a standalone status card when a turn has been silent for too long (default 5 min, per-chat adjustable via /progress or /settings).
  • First-time welcome: the first message in each chat triggers a one-time welcome card introducing the bot's capabilities and common commands.
  • Actionable errors: failed tasks show a suggestion matched to the error — permission / network / model-quota problems each get their own fix hint instead of a bare error string.
  • Safe destructive actions: /clear, /new and the menu's “新建对话” ask for confirmation first, so history is never wiped by accident.
  • User choices & permission approvals in chat: when the agent asks a question (ask_user_question) or requests a permission approval (sandbox escalation etc.), an interactive card with buttons appears right in Feishu — answer by tapping or by replying with text (number or option label); no need to open the Web GUI.
  • Security: groups require @mention by default; user/chat allowlists; Feishu credentials via environment variables or config.
  • Interactive menus: /menu offers hierarchical point-and-click navigation (workdir / chats / settings / plugins / compact, …) — the same card updates in place, supports back/exit, and stays usable across consecutive actions.
  • Smart image & file handling: images sent to the bot are downloaded automatically; if the main model supports vision it sees them directly, otherwise a vision-model sub-task describes them and the description is injected — so a text-only main model never stalls on images. Attached files/audio/video are also downloaded into the workdir.
  • Web mirror: each chat can mirror its DSH session into the DSH Web GUI (/mirror, or automatic via autoMirror). The mirror lock is enforced only on the Feishu side (lockOwner): the Web GUI reads/writes the DSH session directly and never consults the lock, so mutual exclusion is one-sided (not fixable at the repository level — documented as-is). /new, /clear or switching sessions resets the mirror target; autoMirror rebuilds it for new sessions.
  • Scheduled reminders: /remind 10分钟 喝水 (or 2h / 14:30) persists a chat-level reminder that fires without waking the agent — no model tokens spent — and survives process restarts. /schedule lists these together with the agent's own in-session reminders.
  • Send files back to the chat: /send <path> delivers a workspace file — images inline, other files as attachments (feishu / telegram).
  • Admin broadcast: /broadcast <text> pushes a message to every bound chat across all channels (admins listed in allowUsers).
  • Thread isolation (feishu, optional): threadIsolation: true keeps each group thread in its own DSH session.
  • Local commands (no model tokens): /status /task /chat /dir /workspace /workspaces /plugins /compact /history /export /goals /schedule /remind /send /broadcast /model /notify /progress /mirror /unlock /renew /new /clear /stop /settings /help.
  • All-in-one, multi-platform: dsh-connect is the single plugin — the core connect service plus every channel adapter (Feishu/Lark, Telegram, DingTalk) and the web-settings stack, all behind one channels selector. Enable exactly the channels you use.

Repository layout

packages/
  connect/           dsh-connect all-in-one plugin: core connect service + channel adapters + web-settings stack
    src/channels/    per-channel adapters: feishu (Feishu long connection, normalization, streaming replies), telegram (Bot API long-polling, streaming edits), dingtalk (stream-mode bidirectional + webhook push), web (mirror monitor)
    src/settings/    web-settings stack: host RPC, credential store, settings pane/service
docs/
  QUICKSTART.md      step-by-step run guide (DSH side + Feishu side)
  feishu-setup.md    Feishu Open Platform configuration manual
  telegram-setup.md  Telegram BotFather setup manual
  dingtalk-setup.md  DingTalk group custom-robot setup manual
  PUBLISHING.md      naming + GitHub/npm discoverability guide
examples/
  profile-cordis.patch.yml

Channel matrix

ChannelAdapter (in dsh-connect)DirectionTransportNotes
Feishu / Larkfeishu channelbidirectionalWebSocket long connectionfull features (streaming, menus, images)
Telegramtelegram channelbidirectionalBot API long pollingfull features (streaming edits, inline keyboards)
DingTalkdingtalk channelbidirectional (stream) / one-way pushstream gateway (STOMP over WebSocket) / group-robot webhookstream mode: @-mention triggers the agent, replies & numbered-text menus; webhook mode: push service (sendMarkdown / sendText / @mentions)
Web mirrorweb channeloutbound no-opmonitortracks mirror sessions for DSH Web GUI (no synthesized messages)

All channels share the same dsh-connect core: commands, /menu, notification levels, the proactive progress watchdog, interactive choices & approvals, and per-chat settings work identically on every channel. Enable the channels you want via the channels selector; per-channel secrets can live in the DSH credential store.

Quick start

Install

The package is published to npm automatically on every GitHub Release — .github/workflows/publish.yml runs pnpm build + typecheck first, then publishes dsh-connect. Install it once — one plugin, one config block, and enable only the channels you use:

dsh plugin --profile web add dsh-connect

(Installing the single package pulls in the core connect service, every channel adapter, and the web-settings stack. Enable the channels you need via the channels selector.)

For local development (before the package is published), load the built package by absolute path as shown in docs/QUICKSTART.md.

Configure

Append to the profile's cordis.patch.yml ($DSH_HOME/profiles/web/cordis.patch.yml). The plugin registers itself automatically via its bundle manifest, so this file only overrides its config — do not insert it again (a duplicate id makes dsh refuse to boot with duplicate loader entry id):

- id: connect
  name: dsh-connect
  config:
    channels: [feishu, telegram, dingtalk]   # which channels to enable (default: all built-in)
    channelDefaults:
      language: zh                            # common keys inherited by every channel
    feishu:
      appId: cli_xxxx
      appSecret: cli_secret_xxxx
      transport: websocket
      requireMention: true
      dmMode: open
    telegram:
      botToken: "123456:ABC-YourBotToken"     # from @BotFather
      requireMention: true
    dingtalk:
      webhookUrl: "https://oapi.dingtalk.com/robot/send?access_token=xxx"
      # stream: { clientId: xxx, clientSecret: xxx }   # enable bidirectional stream mode

Credentials (appSecret/botToken/clientSecret) can instead live in the DSH credential store and be written from the Web settings pane — see docs/config-reference.md.

Run

Restart dsh web (Host plugins require a process restart to load), complete the platform-side subscription per docs/feishu-setup.md, docs/telegram-setup.md or docs/dingtalk-setup.md, then chat with the bot.

Detailed step-by-step instructions (including Feishu-side setup and verification) are in docs/QUICKSTART.md.

Command list

CommandDescription
/menuOpen the main menu (hierarchical point-and-click; the same card updates in place; back / exit supported)
/settings (/set)Settings: switch model / reasoning effort / notification level / config overview
/modelShow the current model, tap to switch
/notify (/notice)Choose the notification level: full / important / result (takes effect immediately)
/progressChoose how long a silent task may run before a proactive progress card is sent (default 5 min; 关闭 disables)
/mirror [--timeout N]Create (or show) the Web mirror session for this chat; optional lock timeout in minutes
/unlockManually release the session lock (Feishu/Web mirror scenario only)
/renew (/renew-lock)Renew the current session lock timeout
/statusSession status, model, workdir, queue length, context tokens, session ID
/task (/tasks /todo)Show the current task list
/schedule (/reminders)Show scheduled reminders for this session
/chat (/session /sessions)List chats; tap to switch or create a new one
/dir (/cd /pwd)Switch workdir (tap to pick, or /dir <absolute path>)
/workspace <absolute path>Create a new workspace
/workspacesList all workspaces
/pluginsList installed plugins
/compactCompact the current session context
/history [count]Show recent session messages
/export [markdown]Export conversation history as Markdown
/goalsShow current goals
/new (/reset)Start a new conversation (asks for confirmation)
/clearClear the current conversation (asks for confirmation)
/stop (/cancel)Stop the current task
/helpList all commands

All / commands are executed locally by the plugin and consume no model tokens; any other text is sent to the DSH agent as a task.

Configuration

dsh-connect (core)

KeyDefaultDescription
agentPresetunset = roster defaultAgent preset used for each bound session (e.g. standard)
workDirfirst DSH workspaceAgent working directory (absolute path, can be set explicitly)
workspaces[]Workdirs listed in the /dir interactive picker
visionModelauto-detectedVision model {provider, model} for the image sub-task; when unset, the first image-capable model is auto-detected
languagezhUser-facing message language: zh (default) or en
allowUsers[]Sender allowlist (empty = allow all)
allowChats[]Chat allowlist (empty = allow all)
stateDir./.dsh-connectDirectory for the binding route bindings.json
autoMirrortrueAutomatically create a Web mirror session for every new chat
streamHeartbeatMs60000Streaming-card liveness heartbeat (ms); 0 disables it
notifyLevelimportantDefault notification level: full (stream everything) / important (key milestones) / result (answer only); per-chat override via /settings or /notify
progressTimeoutMs300000Proactive progress-notice interval (ms): when a turn has sent nothing for this long, a standalone status card is pushed; 0 disables; per-chat override via /settings or /progress

Feishu channel (feishu)

KeyDefaultDescription
appId / appSecretenv FEISHU_APP_ID / FEISHU_APP_SECRET, or one-click onboardingApp credentials (when unset, onboarding mode starts and creates the app via QR scan)
transportwebsocketwebsocket (default, long connection); webhook needs a public HTTPS callback URL — the adapter hosts its own HTTP service and auto-answers the url_verification challenge
webhookPort9000HTTP listen port for webhook transport mode
webhookPath/Feishu event callback path
verificationToken / encryptKeyemptyOnly needed for webhook mode
requireMentiontrueGroups only respond when the bot is @mentioned
dmModeopenDM policy: open / allowlist / pair / disabled (disabled = ignore DMs)
languagezhUser-facing message language: zh (default) or en

One-click onboarding: start the plugin without appId/appSecret and it prints an onboarding link (valid ~10 minutes). Scan it with Feishu (or click and confirm) and the bot app is created automatically with permissions and event subscriptions preset; credentials are saved to $DSH_HOME/.dsh-connect/feishu-credentials.json.

How it works

  • Agent create/resume: reuses the standard DSH driving pattern (see dsh-headless) — ctx.agents.create({ meta:{cwd, agentPreset}, agentOptions:{provider,model}, setup }); resume goes through ctx.agents.resume. Model selection per session is owned by the DSH api-proxy (selectionFor), so switching models in the Web GUI applies to the bound sessions.
  • Preset mounting: setup mounts the configured agent preset (ctx.agentPresets.mount), giving bound sessions the standard toolset (bash/fs/…).
  • Streaming: assistant/chunk events (reasoning/text deltas, block starts/ends) on session/event are bridged via createAsyncQueue into the Feishu streaming card; blocks are separated by blank lines, reasoning is streamed live, tool calls show a status line, and a configurable heartbeat keeps the card alive during long silent phases. turn/end decides the turn outcome and posts the task-stats card.
  • Proactive progress: each message is acknowledged immediately; if no standalone card/text has been sent for progressTimeoutMs, a status card reports the latest milestone (thinking / last tool call) so a long turn never looks frozen.
  • Interactive choices & approvals: the plugin acts as an in-process client of the host api-proxy (ctx.apiProxy): it subscribes to the same mux stream the Web GUI uses, renders question/requested / approval/requested frames for connect-bound sessions as Feishu cards with buttons, and feeds the user's answer back through apiProxy.respond — the Web GUI stays fully functional, first answer wins.
  • Serialization: one AgentRunner per chatKey — messages are queued and executed serially; agent.followup naturally queues.

Testing

All suites are node:test, run through the consolidated runner (build lib/ first):

pnpm build        # build first (generates lib/)
pnpm test         # runs every suite via packages/connect/test/run-all.mjs
  • packages/connect/test/run-all.mjs: imports every suite in-process (see each suite below).
  • packages/connect/test/unit.test.mjs + packages/connect/test/smoke.mjs (connect core suite): command parsing, binding persistence, async queue, turn outcome derivation; plus loading the plugins into a real Cordis context to verify the plugin contract, including the isChatAllowed allowlist pre-filter assertion.
  • packages/connect/test/feishu.test.mjs: button grid, label alignment, filename sanitization, error extraction.
  • packages/connect/test/telegram.test.mjs: HTML escaping, @mention detection, offset confirmation semantics.
  • packages/connect/test/dingtalk.test.mjs: signature verification, retry/rate-limit, 20000-character truncation.
  • packages/connect/test/web.test.mjs: mirror records, no-synthesized-message regression test.
  • packages/connect/test/settings-*.test.mjs, rpc-client.test.mjs, credential-store.test.mjs, apply.test.mjs, web-settings-*.test.mjs: the all-in-one config + web-settings stack (channel activation, host RPC, settings persistence, credential store, round-trip).

Documentation

License

MIT