dsh-task-dispatch
Deliver a message to a real DSH session by session id, and optionally wait for its reply.
It removes the human copy-paste relay between two agents: Codex, a script, or CI only needs HTTP to hand a task to a running DSH session and read the answer back.
Zero dependencies. It declares no @deepseek-ai/* or third-party packages — only Node built-ins plus the host's existing sessionController / agents / sessions services.
Why not the existing bridge
hermes-dsh-bridge is broader, but as of 0.11.4 every @deepseek-ai/* dependency is pinned to ^0.1.2-rc.1. On DSH 0.2.x that installs a second copy of the same packages, the host loads the wrong version (observed: boot failure that also reset the profile's patch layer), and the plugin never runs.
This plugin deliberately declares no host packages, so that class of conflict cannot happen. The trade-off: it only sends and waits — no file tools, approval bridge, or preset management.
Use from Codex (MCP)
The plugin only speaks HTTP. To expose it as Codex tools, use the bundled mcp/ adapter:
codex mcp add dsh -- node /absolute/path/dsh-task-dispatch/mcp/index.mjs
codex mcp list
Codex then gets dsh_send / dsh_ask / dsh_result / dsh_wait / dsh_tasks / dsh_sessions / dsh_workspaces.
Install
Not published to npm yet. Install from GitHub:
# Option 1: the packed artifact from a release (recommended)
dsh plugin --profile desktop add https://github.com/vinson1101/dsh-task-dispatch/releases/download/v0.6.0/dsh-task-dispatch-0.6.0.tgz
# Option 2: straight from git (pulls mcp/ and test/ too)
dsh plugin --profile desktop add github:vinson1101/dsh-task-dispatch
# Option 3: a local directory (development; symlinked, edits apply immediately)
dsh plugin --profile desktop add /absolute/path/dsh-task-dispatch
Restart DSH afterwards; the log shows:
[dsh-task-dispatch] ready http://127.0.0.1:7391
30-second check
curl -s http://127.0.0.1:7391/health
# {"ok":true,"name":"dsh-task-dispatch","version":"0.6.0",...}
curl -s -X POST http://127.0.0.1:7391/send \
-H 'content-type: application/json' \
-d '{"workspace":"/your/project","text":"reply with the single word: ok"}'
curl -s http://127.0.0.1:7391/tasks/<taskId-from-the-previous-call>
Configuration
| Field | Default | Meaning |
|---|---|---|
host |
127.0.0.1 |
Loopback only; any other value fails activation |
port |
7391 |
Listen port |
token |
none | When set, requests need Authorization: Bearer <token> |
API
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Liveness probe, no token |
GET |
/sessions |
List live sessions (id / cwd / title / running) |
POST |
/send |
Deliver a message, return immediately (202) |
POST |
/ask |
Deliver, wait for the turn, return assistant text |
POST /send
{ "sessionId": "session-23af063e-...", "text": "run the tests", "mode": "steer" }
sessionIdrequired; cold sessions are resumed automaticallymodeoptional,steerinterrupts instead of queueing- Returns
{ ok, sessionId, requestId, afterSeq }
POST /ask
{ "sessionId": "session-23af063e-...", "text": "done yet?", "timeoutMs": 120000 }
- Same as
/sendplus waiting; returns{ ok, reason, text, truncated } reason:idle/input-required/timeouttextis extracted from this turn'sassistant/messageeventstimeoutMsdefaults to 120000
Use from Codex
curl -s -X POST http://127.0.0.1:7391/send \
-H 'content-type: application/json' \
-d '{"sessionId":"session-23af063e-...","text":"check the task status"}'
Wrap that curl in an MCP server if you want Codex to call it as a tool.
Security
- Listens on
127.0.0.1only; non-loopback hosts are rejected in code - Any local process can call it, so set
tokenbefore exposing anything - It drives a real agent, which can execute commands and edit files — treat it as local shell-level access
Callback: not supported yet
The task result is not pushed back to the caller. There is no MCP server→client notification implementation, and whether such a notification could wake an idle Codex session is unverified.
notify is a delivery ledger, not proof of receipt: unsupported / pending / sending / sent / failed. A webhook returning 200 only means the HTTP endpoint answered.
Retrieve results by querying: GET /tasks/:id (dsh_result) or POST /wait (dsh_wait). Neither resends the task.
Limitations
/sessionslists live sessions only; a cold session still accepts/sendif you know its id/askwaits for one turn; for long work use/sendplus polling, or raisetimeoutMs- No streaming; only the turn's final text is returned
License
MIT
No comments yet. Be the first to write one.