Skip to main content

dsh-interconnect

34Stars4Forks1Issues0Watchers

Enable WebSocket communication between multiple DSH instances using a shared secret: exchange text messages, push session lifecycle events, and enumerate peer's online sessions.

Evidence5/5methodologySourceInstallMaintenanceDSH versionSecurity scan
Machine-auditedInstall commandRepo verifieddsh-plugin topicLicenseREADMEAI wiki
Language
TypeScript
License
MIT
Branch
main
deepseek-harnessdshdsh-plugininterconnect

Install

cmdweb profile
$ dsh plugin --profile web add dsh-interconnect

Run the command above in your terminal to install this plugin via the dsh CLI. You can switch Profile in the top-right corner. New to dsh? Read the beginner tutorial

Install via your agent

Install the DeepSeek Harness plugin Chinesezjc/dsh-interconnect for me: review the repository at https://github.com/Chinesezjc/dsh-interconnect first, then run the install command and verify the plugin loads successfully.

Paste this instruction to the DSH Web GUI assistant — it will install and verify for you.

One-Sentence Positioning

dsh-interconnect is a cross-instance communication plugin on DSH. It enables a DSH instance on one machine to send text messages to another DSH instance on the same machine, another machine, or a different DSH instance on another machine, mutually push session lifecycle events, and enumerate the peer's currently running sessions—all transmissions go through a shared-key authenticated WebSocket persistent link.

Core Capabilities

  • Deliver text messages between multiple DSH instances through persistent WebSocket links, addressing by instanceId lookup for the corresponding link
  • Real-time push of local session lifecycle events (create/destroy/state change/subagent end) to all configured peers
  • Provides four model-visible tools: interconnect_send, interconnect_ping, interconnect_list, interconnect_reply
  • Support waking up peer's persisted but not running sessions by message (resume is off by default, receiver can veto with one vote)
  • Use sender identity to allow bidirectional multi-round chained reply without re-addressing the peer
  • Use shared token + timing-safe verification Bearer authentication, fails closed when token not configured

Technical Implementation

  • Language: TypeScript (ES2022 / ESM)
  • Key Dependencies: @deepseek-ai/cordis (Cordis service framework) / @deepseek-ai/dsh-llm (user message construction) / @deepseek-ai/dsh-host-webserver (WebSocket upgrade routing) / ws (WebSocket implementation) / @deepseek-ai/schemastery (Config validation)
  • Architecture Pattern: Inject two host plugins into host composition via cordis.patch.yml; interconnect is a Service class, injecting three dependencies: webServer / agents / credentials; tool-interconnect registers four tools to ctx.tools surface via defineTool
  • Entry Files: src/index.ts (root re-export) / src/interconnect/index.ts (host service) / src/tool-interconnect/index.ts (model tools)

Applicable Scenarios

When you need multiple DSH instances to work together across machines (e.g., let an agent on one machine actively assign tasks to a session on another machine, aggregate agent outputs from different instances, or do event synchronization between multiple instances)—install dsh-interconnect, both ends communicate via shared-key WebSocket channel, and models can also actively address and deliver messages using tools; regular single-machine users usually don't need this plugin.

Prerequisites and Compatibility

DependencyMinimum VersionDescription
DeepSeek Harness (DSH)Not declaredPeer dependencies @deepseek-ai/dsh-{agent,api-remotes,credentials,llm,session,subagent,host-apiproxy,host-webserver,tools,invariants} and @deepseek-ai/cordis; this repo uses sibling checkout to link to dsh monorepo source; no explicit engines version constraint
Node.js>=22.0.0esbuild compiles to node22, devDep @types/node ^24.0.0
Credential DSH_INTERCONNECT_TOKENRequiredShared key; both ends' credentials must set the same value; missing causes server to fail-closed with 403
ws package* (peer)Provided by host DSH profile's node_modules; pure JS, no native dependencies

Installation

dsh plugin --profile web add github:Chinesezjc/dsh-interconnect

Configuration

ConfigTypeDescriptionDefault
instanceIdstringSelf-reported ID of this instance, appears in ping/send/list responses, also how peers address this machine'dsh'
requestTimeoutMsinteger (≤60000)Timeout threshold for outbound request waiting for result; returns unreachable on timeout10000
peers{ [instanceId]: origin }Peer routing mapping; value is the origin to dial the peer from this machine (e.g., http://127.0.0.1:13080); automatically establishes persistent WebSocket link to each peer when activated{}
deliveryfollowup / steer / injectDefault delivery mode when inbound message doesn't carry delivery'followup'
allowResumebooleanWhether to allow sender to wake up this machine's offline session with resume; receiver can veto with one vote using thistrue

The shared key for authentication is not in this config but comes from DSH credentials' DSH_INTERCONNECT_TOKEN; at runtime, peers can also be dynamically added/removed via ctx.interconnect.subscribe(instanceId, origin) / ctx.interconnect.unsubscribe(instanceId).

FAQ

Q: What can regular users do with this plugin?

A: Mainly for users who want multiple DSH instances to collaborate across machines. For example, let an agent on one machine actively assign tasks to a session on another machine, aggregate running results from different instances, or synchronize session lifecycle events between two instances. Both ends need to install this plugin and configure the same shared key; single-machine users don't need it.

Q: What else needs to be done after installation to enable communication?

A: Configure the same DSH_INTERCONNECT_TOKEN in both ends' DSH credential store (missing causes server to fail-closed with 403), then write the peer's instanceId and reachable origin into local config.peers. When activated, the plugin automatically establishes persistent WebSocket links to each peer—no manual link call needed.

Q: Why do messages sometimes fail to send? What is the reason field for?

A: Because different failures require different responses, the plugin uses reason to distinguish: session-not-live means the peer's session is not running (change target or carry resume); unreachable means transport or authentication failure (can retry); resume-refused / resume-failed related to wake-up; session-owned-by-subagent means that session belongs to subagent routing (go through parent agent); no-sender-known means reply can't find sender record.

Q: Is resume option safe? Why is it off by default?

A: Wake-up triggers a complete agent turn on the peer (wakeDriver → kick → turn → llm.stream), which is a billed model call, and carries the session's full tool set. So it's off by default: sender must explicitly carry resume, receiver can also veto with allowResume: false to avoid remotely triggering billed calls in unattended sessions.

Q: What's the difference between the three delivery modes?

A: followup queues the message as an independent turn, waiting for the receiver's current turn to finish; steer cuts in at the nearest step boundary within a running turn, for urgent messages that can't wait for the whole turn; inject only writes the message to context but doesn't wake the idle agent, which may never read it. Sender can override receiver's default config per message.

Q: Why can't some sessions be seen in interconnect_list?

A: The list only contains live sessions with currently running agents (this is the set send can reach); sessions owned by subagents are also excluded—the delivery right for such sessions belongs to its parent agent, and direct delivery here would compete with the parent agent. The criterion directly reuses Host's hasApiRemoteSubagentOwner, this plugin doesn't reimplement it.

Q: Can old calling methods still work after upgrading to 0.9?

A: No. 0.9 is a breaking major version: removes all HTTP endpoints, send/reply/ping/list all go through /interconnect/link persistent link; addressing changes from baseUrl to instanceId; peers changes to { instanceId: origin } mapping; sender removes baseUrl, becomes addressless identity; returns unreachable directly to unconfigured or unreachable peers, no HTTP fallback.

Learning Curve

Advanced — single instance installation is one command, but to truly enable cross-instance communication, you also need to configure shared key + write peer peers mapping on both ends, and understand the WebSocket persistent link, resume wake-up side effects, three delivery modes, and how peer version and deployment form (subagent owner, headless without api-proxy) affects behavior.

Known Issues and Limitations

  • 0.9.0 is a breaking major version: HTTP endpoints all removed, addressing changed to instanceId, peers changed to mapping, old calling methods all invalid (CHANGELOGOG.md:11-21)
  • When peer runs pre-0.9 version without sender, calling interconnect_reply on that session from this end returns no-sender-known (src/interconnect/index.ts:284-288)
  • Sessions with too old disk format cannot be woken (returns resume-failed), this is a limitation from Host upstream resume() path, this plugin doesn't bypass (README.md:166-168)
  • Deployments without Host agent lookup (headless / no api-proxy profile) degrade session-not-live when calling resume, no error (src/interconnect/index.ts:556-563)
  • Sessions owned by subagent cannot be directly sended, returns session-owned-by-subagent; criterion directly reuses Host's hasApiRemoteSubagentOwner, doesn't reimplement (src/interconnect/index.ts:599-601)
  • Shared key must be identical on both ends; when not configured, interconnect server fails closed with 403, no automatic negotiation or fallback (src/interconnect/index.ts:692-695)
  • Exceptions thrown by inbound event ctx.on listeners are swallowed and logged as warn, to avoid one listener exception crashing the socket's message callback; local listener exceptions may not directly manifest (src/interconnect/index.ts:752-758)
  • Outbound send/reply waiting for result is bound by requestTimeoutMs (default 10s, max 60s), timeout directly returns unreachable, no guarantee peer actually received (src/interconnect/index.ts:365-371)

Read the usage guide →

Install steps, key points, FAQ and compatibility for this plugin — auto-derived from indexed fields.

Listing badge

Listed on deepseek-plugin.org
[![Listed on deepseek-plugin.org](https://img.shields.io/badge/listed_on-deepseek--plugin.org-007EC6)](https://deepseek-plugin.org/plugins/Chinesezjc/dsh-interconnect)

Paste this markdown into your GitHub README to link back to this listing. The badge only states the listing — not a security endorsement.

← Back to plugin directory