DeepSeek Harness plugin

dsh-ringcentral

RingCentral Team Messaging IM channel plugin for deepseek-harness (dsh)

Jump to install

Source facts

Repository
ringclaw/dsh-ringcentral
Latest update
Aug 21, 2026
Category
Workflow & Automation
GitHub stars
0
Format
plugin
Catalog evidence
Upstream dsh.bundle evidence
Evidence path
package.json#dsh.bundle
Checked against
0.1.0-rc.8
Upstream check date
2026-08-20

This evidence comes from the upstream catalog. This site has not installed, run, or security-reviewed the plugin.

Install

Start with a prompt that asks an agent to review the GitHub repository and source. Switch to the command if you want to install it yourself.

Copy this prompt into DSH, Codex, or another agent and ask it to review the GitHub repository and source first.

Do not install or run any commands yet. Read this plugin's GitHub repository, README, and relevant source code. Then answer the questions below clearly and directly so I can decide whether it fits my needs:

1. What is this plugin, and what problem does it solve?
2. Who is it for, and what are its typical use cases?
3. How is it used after installation? Include one minimal example.
4. What known limitations or privacy, security, compatibility, or maintenance risks does it have?
5. Give a clear recommendation: recommend, conditionally recommend, or do not recommend, with reasons.

Distinguish statements documented by the repository, inferences from source code, and unknowns. If evidence is insufficient, say so explicitly. Do not guess or simply repeat the README.

GitHub: https://github.com/ringclaw/dsh-ringcentral
Plugin: dsh-ringcentral
Author: ringclaw

Check the source files

Read the README and other files from this plugin directory before installing.

File explorer4 files
README.mdSource · read only
README language

dsh-ringcentral

RingCentral Team Messaging IM channel plugin for deepseek-harness (dsh). It turns a RingCentral Bot Add-in into a first-class frontend for a dsh agent: inbound posts from RingCentral chats drive the agent loop, and assistant replies flow back as threaded RingCentral posts.

English | 中文说明

Architecture

RingCentral user ──▶ WebSocket (PostAdded) ──▶ im-ringcentral ──▶ ctx.agents ──▶ dsh agent loop ──▶ LLM
                                                │                                     │
                                                └── admission / session / event ◀────┘
                                                     (assistant reply ──▶ RingCentral post, threaded)

The plugin is a pure Cordis plugin following the dsh "Plugins, not loop changes" principle. It speaks the RingCentral Team Messaging v1 REST API + WebSocket subscription stream directly (no external SDK) and reuses the host dsh services for agents, sessions, models, compaction, and tool presentation.

Install

1. Via dsh plugin manager

# install into a profile
npx @deepseek-ai/dsh plugin --profile ringcentral add dsh-ringcentral

# start
export RC_BOT_TOKEN="your-bot-jwt"
export DEEPSEEK_API_KEY="your-deepseek-key"
npx @deepseek-ai/dsh --profile ringcentral

Or run the bundled installer: sh install.sh.

2. Local path

cd /path/to/dsh-ringcentral
pnpm install && pnpm build
npx @deepseek-ai/dsh plugin --profile ringcentral add /path/to/dsh-ringcentral
export RC_BOT_TOKEN="your-bot-jwt"
npx @deepseek-ai/dsh --profile ringcentral

3. --patch development mode

The --patch overlay loads the plugin from a local absolute path without installing it into a profile. Generate the machine-local patch first, then boot:

cd /path/to/dsh-ringcentral
pnpm install && pnpm build        # dist entry (npx dsh cannot resolve .js -> .ts)
node scripts/gen-dev-patch.mjs    # writes cordis.local.yml with the real path
export RC_BOT_TOKEN="your-bot-jwt"
npx @deepseek-ai/dsh web --patch ./cordis.local.yml

Use pnpm dev (tsc --watch) while iterating: the loader hot-reloads the plugin whenever dist/ changes. Pointing the patch at src/index.ts only works inside a deepseek-harness source tree (pnpm dsh), not with the npx-installed package.

RingCentral bot setup

1. Sign in at <https://developers.ringcentral.com/>. 2. Create an app with the Bot platform type. 3. Grant at least: TeamMessaging, ReadAccounts, WebSocketsSubscription. 4. Install or publish the bot to your RingCentral account. 5. Copy the bot JWT and use it as RC_BOT_TOKEN.

Optional owner credentials (JWT REST API app for your own account, with TeamMessaging + WebSocketsSubscription + ReadMessages) give ringcentral_get_recent_messages owner read fallback and outbound owner fallback. The history tool always reads through the bot client (RC_BOT_TOKEN) first; without owner credentials it uses only the bot client (chats the bot is a member of).

Configuration

Config follows dsh practice: the cordis config tree is the single source (profile cordis.patch.yml / cordis.yml), with Schema defaults applied automatically. Secrets (RC_BOT_TOKEN, RC_USER_CLIENT_ID, RC_USER_CLIENT_SECRET, RC_USER_JWT_TOKEN) resolve through the host credentials domain — an explicit config value wins, then environment → managed $DSH_HOME/.credentials.yaml → project/user .env, with the plain process environment as the final fallback. RC_SERVER_URL works the other way for operational overrides (e.g. sandbox): the environment value wins over the configured/default server. Secrets are never persisted into profile YAML. To drive any other setting from an environment variable, use the cordis loader's !!js tag (double bang — a single !js is not evaluated), e.g. access.groupMode: !!js process.env.RC_GROUP_MODE ?? 'open'.

The access-control block mirrors @tencent-connect/dsh-qqbot exactly (QQ's c2c surface is dm here). RingCentral's three non-DM chat types (Team / Everyone / Group) are all governed by the group surface.

ConfigTypeDefaultDescription
botTokenstringrequiredBot static JWT (env: RC_BOT_TOKEN)
ownerCredentials.clientId / clientSecret / jwtstring-Owner JWT (env: RC_USER_*)
serverstringhttps://platform.ringcentral.comAPI server (env: RC_SERVER_URL)
access.dmModeenumopenDM handling: disabled, allowlist, open
access.dmAllowstring[][]Person ids allowed in DMs; empty or ["*"] = allow all
access.groupModeenumopenGroup handling: disabled, allowlist, open
access.groupAllowstring[][]Chat ids allowed in groups; empty or ["*"] = allow all
requireMentionbooleantrueRequire @-mention in group chats
groupPromptstring-Extra system prompt for group chats
directPromptstring-Extra system prompt for DMs
processingPlaceholder.enabledbooleanfalsePost 👀 while the agent works (texts/delay are fixed)
historyMessageLimitnumber250Default record count for the history tool
homeChannelstring-Fallback target for the history tool
textChunkLimitnumber4000Max chars per outgoing post
provider / modelstringhost defaultLLM route (fallback chain: per-peer prefs → config → host agentDefaultModel)
presetstring-Agent preset id
cwdstringprocess.cwd()Agent working directory
sessionIdleTimeoutnumber1800000Idle session eviction (ms)
showToolResultsbooleanfalseShow successful tool results (errors always show)
debugbooleanfalseDebug logging (includes inbound message logs)

Commands

CommandDescription
/new (/reset, /clear)Start a new session (clear context)
/compactCompress session history (summary replaces old records)
/modelShow or switch the model
/stopAbort the current generation
/rc-pingConnectivity test
/rc-versionPlugin version
/rc-statusCurrent session status
/rc-helpList all commands

Session routing

sessionKey: ringcentral:<accountScopeKey>:<scope>:<peerId> where scope is direct (peer = person id), group (peer = Group DM chat id), or channel (peer = Team/Everyone chat id), and accountScopeKey is a SHA-256 fingerprint of server + bot token. The SessionId is derived deterministically (SHA-256), so the same user/chat always routes to the same session and survives restarts. Resolution order: in-process reuse → persisted resume → fresh create.

Agent questions (ask_user)

When the agent calls ask_user_question, the plugin renders the question into the chat (thread-anchored) and waits for the user to reply in the same session:

  • Reply with an option number or label to select a choice (multi_select

accepts "1, 3"), or type a free-text answer.

  • Multi-question asks are answered one question at a time.
  • Answers resolve the pending question and are not appended to session

history (same semantics as the web GUI).

  • Waiting times out after 10 minutes (the ask is cancelled with a notice).

Note: the provider registers on the userQuestions service seam. In a web profile the GUI provider takes precedence — questions then appear in the web UI, not in RingCentral. Use a dedicated profile for IM-only operation.

Design principles

  • Pure Cordis plugin — follows dsh "Plugins, not loop changes".
  • Declarative depsinject = ['agents']; tools/compaction/presets are optional seams.
  • Session isolation — one agent per RingCentral peer.
  • Mini-Markdown outbound — replies are converted to RingCentral Mini-Markdown and chunked.
  • Threading — replies always anchor on the triggering post (threadId preferred), with owner fallback and unthreaded retry.
  • Idle eviction — inactive agents are disposed automatically.
  • Defensive degradation — missing tools/presets/owner credentials never crash the plugin.

Not in v1 (planned follow-ups)

  • Adaptive Card / note / calendar / task artifact tools
  • Cron and out-of-process notification sender
  • Multi-account support
  • Native streaming (RingCentral has no stream API; the processing placeholder is the typing affordance)

Local development

pnpm install
pnpm build          # or: pnpm dev (watch)
pnpm test
pnpm typecheck

# run against the npx-installed dsh
export RC_BOT_TOKEN="xxx"
node scripts/gen-dev-patch.mjs
npx @deepseek-ai/dsh web --patch ./cordis.local.yml

scripts/gen-dev-patch.mjs emits the gitignored cordis.local.yml directly, with the entry resolved to an absolute path on the machine (default dist/index.js; pass src/index.ts to point at the TypeScript entry).

Troubleshooting

SymptomLikely causeFix
Plugin not startingRC_BOT_TOKEN missingSet RC_BOT_TOKEN (env or $DSH_HOME/.credentials.yaml) or botToken in config
Bot never replies in a group chataccess.groupMode: disabled, not allowlisted, or no mentionCheck access.groupMode / access.groupAllow and @-mention the bot
DM ignoredaccess.dmMode: disabled or sender not in access.dmAllowCheck access.dmMode / access.dmAllow
History tool returns nothingChat not visible to bot or ownerReads try the bot first, then the owner; pass a bare chat id or channel:<chatId> and make sure one client is a member
Agent question not answered from RingCentralWeb GUI provider registered (web profile)Questions go to the web UI; use a dedicated profile or answer in the web UI

License

[MIT](./LICENSE)