dsh-companion
A small, backend-only DeepSeek Harness plugin that gives native clients a read-only JSON view of DSH workspaces and live sessions plus a configurable notification event feed.
It is designed for client shells such as dsh-native that need project and session metadata without loading or scraping the Harness web UI.
[!IMPORTANT]
This project is not the unscoped dsh-companion package on npm. That name belongs to an unrelated project. Install this plugin from this repository or one of its GitHub Release archives.
What it provides
- Three small, cache-free JSON endpoints for workspaces and live sessions.
- A reconnectable server-sent-event feed for native completion, failure, question, and approval alerts.
- Harness plugin settings that filter alert kinds and subagent events at the source.
- Explicit field projection: internal Harness objects are never serialized wholesale.
- DSH trusted-host and same-origin checks on every request.
- An installable DSH bundle with compiled JavaScript and a small settings-schema dependency.
- Clean unloading: all registered routes are removed with the plugin.
Install
From a GitHub Release (recommended)
Download dsh-companion-<version>.tgz from the latest release, then add it to the Web profile:
dsh plugin --profile web add ./dsh-companion-<version>.tgz
dsh web
Each release also includes SHA256SUMS.txt so the archive can be verified before installation.
Directly from GitHub
For the newest revision on main:
dsh plugin --profile web add github:leonardoxr/dsh-companion
dsh web
A local checkout can be linked in place while developing:
dsh plugin --profile web add /absolute/path/to/dsh-companion
dsh web
Verify the plugin after DSH starts:
curl http://127.0.0.1:3080/api/companion/workspaces
API
| Route | Response |
|---|
GET /api/companion/workspaces | { workspaces: [...] } — durable workspaces and their member session IDs |
GET /api/companion/sessions | { sessions: [...] } — live sessions and their latest folded titles |
GET /api/companion/session/<id> | One live-session summary, or a JSON 404 |
GET /api/companion/notifications | text/event-stream feed of configured native alerts |
Example session-list response:
{
"sessions": [
{
"id": "session-1",
"title": "Implement native navigation",
"cwd": "/work/dsh-native",
"createdAt": 1787356800000
}
]
}
JSON responses use Content-Type: application/json and all routes use Cache-Control: no-store. The notification route uses SSE, emits 15-second heartbeats, accepts a prior cursor in Last-Event-ID or ?since=, and keeps a bounded in-memory replay window. A fresh connection starts at the live tail but receives interactions that are still waiting for a question answer or approval. Non-GET requests return 405.
Notification settings
The Harness plugin settings page exposes these options under Native notification forwarding:
| Setting | Default | Alert |
|---|
completed | on | Successful turn/end events |
blocked | on | Blocked turns |
errors | on | Failed turns and live agent errors |
maxTokens | on | Turns that reach the output-token limit |
aborted | off | Cancelled or aborted turns |
questions | on | Pending ask_user_question interactions |
approvals | on | Pending tool approvals |
subagents | off | Include events from sessions marked as subagents |
Changing these settings reapplies the plugin and restarts its bounded event feed. Disabling the plugin itself remains the Cordis loader's responsibility.
Each notification payload is versioned and contains only a stable key, kind, session ID/title, short body, and timestamp. Raw messages, tool arguments, commands, icons, and click-through URLs are never forwarded.
Security model
The endpoints expose workspace paths, session IDs, titles, timestamps, session lineage, and—when enabled—short question, approval, and error text. They enforce the Harness web runtime's trustedHosts policy and reject cross-site browser requests, but this is a network trust boundary, not user authentication.
Do not expose the DSH server to networks whose clients should not read that metadata. See SECURITY.md for private vulnerability reporting.
How it works
The package is a plain Cordis module with name, Config, inject, and apply exports. It declares webServer, webRuntime, apiProxy, sessions, sessionTitle, and workspaceRegistry as required services, then registers its routes and consumes the host's existing event streams when the bundle loads.
Harness capabilities arrive through injected Cordis services. The only runtime import is the Harness-compatible Schemastery package used to render and normalize plugin settings; TypeScript emits the installable entry point to dist/index.js. Unloading or reconfiguring the plugin aborts event subscriptions, closes SSE clients, and removes every route.
Compatibility
DeepSeek Harness is currently in developer preview, so its plugin service contracts may change. This version targets the service contracts in the DSH 0.1.1 release-candidate line and requires Node.js 22 or newer. CI covers Node.js 22 and 24.
Development
npm ci
npm test
npm pack --dry-run
npm test rebuilds dist/ before running tests against the compiled entry point. The committed dist/ directory is intentional: GitHub dependencies are installed under node_modules, where Node does not strip TypeScript syntax at runtime.
If a source change alters generated output, include the updated dist/ files in the same pull request.
Contributing and releases
Contributions are welcome. Read CONTRIBUTING.md for the local workflow and pull-request expectations, and follow the Code of Conduct.
Successful CI runs publish a short-lived, installable package artifact. Version tags such as v0.1.1 publish the same compiled .tgz plus its checksum as a permanent GitHub Release. Maintainers can follow docs/RELEASING.md.
License
MIT