
Security News
Deno Goes the Way of the Dinosaur
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.
dsh-ringcentral
Advanced tools
RingCentral Team Messaging IM channel plugin for deepseek-harness (dsh)
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 | 中文说明
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 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.
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
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.
TeamMessaging, ReadAccounts, WebSocketsSubscription.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).
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.
| Config | Type | Default | Description |
|---|---|---|---|
botToken | string | required | Bot static JWT (env: RC_BOT_TOKEN) |
ownerCredentials.clientId / clientSecret / jwt | string | - | Owner JWT (env: RC_USER_*) |
server | string | https://platform.ringcentral.com | API server (env: RC_SERVER_URL) |
access.dmMode | enum | open | DM handling: disabled, allowlist, open |
access.dmAllow | string[] | [] | Person ids allowed in DMs; empty or ["*"] = allow all |
access.groupMode | enum | open | Group handling: disabled, allowlist, open |
access.groupAllow | string[] | [] | Chat ids allowed in groups; empty or ["*"] = allow all |
requireMention | boolean | true | Require @-mention in group chats |
groupPrompt | string | - | Extra system prompt for group chats |
directPrompt | string | - | Extra system prompt for DMs |
processingPlaceholder.enabled | boolean | false | Post 👀 → ⏳ while the agent works (texts/delay are fixed) |
historyMessageLimit | number | 250 | Default record count for the history tool |
homeChannel | string | - | Fallback target for the history tool |
textChunkLimit | number | 4000 | Max chars per outgoing post |
provider / model | string | host default | LLM route (fallback chain: per-peer prefs → config → host agentDefaultModel) |
preset | string | - | Agent preset id |
cwd | string | process.cwd() | Agent working directory |
sessionIdleTimeout | number | 1800000 | Idle session eviction (ms) |
showToolResults | boolean | false | Show successful tool results (errors always show) |
debug | boolean | false | Debug logging (includes inbound message logs) |
Every row except the secret entries is editable from the Web GUI card described below.
Installed into a web profile (dsh plugin --profile web add dsh-ringcentral),
the plugin registers a settings namespace and a configuration card under
Settings → Plugins → Plugin configuration. The card:
RC_BOT_TOKEN, RC_USER_*) as write-only
controls: it reports only whether each is configured and writes through the
settings domain (fallback to the host credentials domain where reachable);
secret values are never sent back to the page. Credential changes apply
live: within ~1s the plugin rebuilds its REST clients and reconnects the
websocket with the new token — no restart, and the per-peer session context
survives rotation (the account key derives from the stable bot identity).Without a settings service (a bare custom cordis.yml), the card simply does
not mount and the plugin behaves exactly as before.
| Command | Description |
|---|---|
/new (/reset, /clear) | Start a new session (clear context) |
/compact | Compress session history (summary replaces old records) |
/model | Show or switch the model |
/stop | Abort the current generation |
/rc-ping | Connectivity test |
/rc-version | Plugin version |
/rc-status | Current session status |
/rc-help | List all commands |
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.
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:
multi_select
accepts "1, 3"), or type a free-text answer.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.
inject = ['agents']; tools/compaction/presets are optional seams.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).
pnpm test runs 143 cases across 17 files. No network is touched — clients,
websockets, and harness services are stubbed.
| File | Covers |
|---|---|
tests/accounts.test.ts | Secret resolution precedence, schema-default fallbacks, legacy __FROM_ENV__ compat, numeric clamping |
tests/account-scope-key.test.ts | Stable-identity account key: rotation keeps session keys; token-fingerprint fallback |
tests/credentials.test.ts | resolveSecret chain (service → env → managed file), inject handle, file parser, file watcher |
tests/settings-merge.test.ts | Live config merge into runtime/account copies, secret-field exemption, server/placeholder branches |
tests/settings-redact.test.ts | role('secret') wire redaction and its sidecar |
tests/settings-section.test.ts | Cross-version settings mounting: installSection (harness ≥ 0.1.2) vs legacy installSettingsSection fallback (≤ 0.1.1-rc.2), failure containment |
tests/monitor.test.ts | WS frame extraction, post filtering (own echoes, answer markers), websocket URL building |
tests/rotation-scheduler.test.ts | Debounce coalescing, single-flight, dispose (extracted module, fake timers) |
tests/gateway-rotation.test.ts | Fake-harness integration: rotate() rebuilds clients, re-registers the history tool, reconnects the websocket; no-op on same token; fail-safe on missing token |
tests/session-manager.test.ts | Deterministic session ids, per-scope isolation, setAccountKey re-derivation |
tests/inbound.test.ts | Admission (dm/group allowlists), mention gating, prompt injection, command routing |
tests/outbound.test.ts | Reply chunking, showToolResults gating, placeholder lifecycle |
tests/history-tool.test.ts | Reader precedence (bot → owner), record clamping, target resolution |
tests/threading.test.ts | Thread-participation tracking and follow-up classification |
tests/targets.test.ts | Reply-target resolution and thread anchoring |
tests/markdown.test.ts | Mini-Markdown conversion |
tests/user-questions.test.ts | IM answer surface for agent questions |
tests/debug-log.test.ts | On-disk debug log format and gating |
| Symptom | Likely cause | Fix |
|---|---|---|
| Plugin not starting | RC_BOT_TOKEN missing | Set RC_BOT_TOKEN (env or $DSH_HOME/.credentials.yaml) or botToken in config |
Debug log shows [cred] … service=absent | In desktop-style deployments, bundle-row plugins cannot read host services via ctx.get (isolate scoping) | Not an error: 0.3.15+ ships a file fallback and 0.3.17 an inject-based path — see docs/credentials-service-unreachable.md |
| Bot never replies in a group chat | access.groupMode: disabled, not allowlisted, or no mention | Check access.groupMode / access.groupAllow and @-mention the bot |
| DM ignored | access.dmMode: disabled or sender not in access.dmAllow | Check access.dmMode / access.dmAllow |
| History tool returns nothing | Chat not visible to bot or owner | Reads 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 RingCentral | Web GUI provider registered (web profile) | Questions go to the web UI; use a dedicated profile or answer in the web UI |
| Plugin config card missing from the Web GUI, or save/reset fails, after a harness upgrade | Harness ≥ 0.1.2 moved settings registration to settings.installSection() and removed connection.api; ≤ 0.4.1 used the removed APIs | Upgrade to dsh-ringcentral ≥ 0.4.2 (dual-path: installSection on ≥ 0.1.2, legacy fallback on ≤ 0.1.1-rc.2) |
dsh-ringcentral ≥ 0.4.2 supports both harness lines: ≥ 0.1.2-alpha (settings via settings.installSection, writes via settingsScope.mutate) and 0.1.1-rc.2 and earlier (legacy installSettingsSection + connection.api.settings.mutate fallback).dsh-ringcentral ≤ 0.4.1 requires the ≤ 0.1.1-rc.2 settings API; it does not start on harness ≥ 0.1.2.FAQs
RingCentral Team Messaging IM channel plugin for deepseek-harness (dsh)
The npm package dsh-ringcentral receives a total of 83 weekly downloads. As such, dsh-ringcentral popularity was classified as not popular.
We found that dsh-ringcentral demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
Cloudflare is acquiring Deno’s team, which will provide another year of bug fixes and security updates before ending support for the runtime.

Security News
Anthropic’s free OSS Scanner uses Mythos and other Claude models to find open source vulnerabilities and send maintainers reports with proposed fixes.

Security News
On Front Lines’ Early Adopters: CISO podcast, Andrew Becherer shares lessons from Datadog, weighs the long-term costs of AI-built tools, and explains how founders can actually gain a CISO’s trust.