@syghar/dsh-tool-signal
v0.1.0
Published
Native Signal messaging tools (signal_send / signal_poll) for the DeepSeek Harness, over a local signal-cli-rest-api instance
Maintainers
Readme
dsh-tool-signal
Native Signal messaging for the DeepSeek Harness (dsh): two model-facing tools over a local signal-cli-rest-api instance, so the agent can send and receive Signal messages without shell calls.
signal_send— send a text message from the registered identity to one or more recipients.signal_poll— long-poll for incoming messages, attachments, and delivery/read receipts.
The REST client is dependency-free (lib/client.mjs) and honors the harness cancellation
signal; the plugin entry (lib/index.mjs) owns schemas, validation, prompt guidance,
and result shaping on ctx.tools / ctx.systemPrompt.
Install
From the repository root, add the plugin to the dsh web profile:
npm install @syghar/dsh-tool-signal…and add "@syghar/dsh-tool-signal" to dsh.profile.bundles in the profile's package.json
(done alongside the npm install). Restart the dsh web process; the tools appear in new sessions.
The Harness SDK packages are intentionally peer dependencies. They must be resolved from
the host Harness installation, not installed as ordinary plugin dependencies: two physical
copies of @deepseek-ai/dsh-tools in one process have different scheduler symbols and
make every tool call fail with Cannot read properties of undefined (reading 'prepare').
If this profile already installed an older copy, repair it once from the profile directory:
npm install "@deepseek-ai/dsh-tools@file:$APPDATA/npm/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/dsh-tools"
npm installOn Windows PowerShell, replace $APPDATA with the actual npm global prefix shown by
npm prefix -g, and use that path in the link: specifier. The link must point to the
SDK shipped with the active Harness installation. Then restart dsh web.
Configuration
All keys are optional, on the tool-signal row's config (base layer of the Settings section;
a profile cordis.patch.yml entry with - id: tool-signal overrides them).
| Key | Default | Meaning |
|---|---|---|
| enabled | true | Register the tools at all. |
| apiBaseURL | http://127.0.0.1:8889 | signal-cli-rest-api base URL (loopback). |
| senderNumber | "+15555550100" | The registered Signal identity (replace this example with your deployment value). |
| sendTimeoutMs | 30000 | Cooperative budget for one send. |
| pollTimeoutMs | 15000 | Default long-poll wait requested from the API. |
| maxMessages | 20 | Cap on events returned by one poll. |
timeout_ms is a model argument to signal_poll (1–30000 ms, clamped).
Integration check
Against the live instance (no harness boot needed):
node -e "import('@syghar/dsh-tool-signal/client').then(async (m) => { const r = await m.signalPoll({ apiBaseURL: 'http://127.0.0.1:8889', senderNumber: process.env.SIGNAL_SENDER_NUMBER ?? '+15555550100', timeoutMs: 3000, fetchTimeoutMs: 10000 }); console.log('envelopes:', r.envelopes.length) })"Known limitations
- One registered identity per API instance (the account list is single-tenant here).
- Polls are request-scoped and destructive: the receive endpoint is the only message store, and a poll drains what arrived. Anything a poll returns (or fails to parse) is gone — the client therefore fails loudly on unrecognized response shapes instead of returning empty.
/v1/receiveresponse shape drifts across API/signal-cli builds: the live v0.100 build (swaggerdata.Message) returns a single{"envelope":{...},"account":"..."}object; older builds returned{"value":[...]}, and multi-message batches may arrive as a JSON array or NDJSON.client.mjsnormalizes all of these to{ envelopes }; the regression tests intest/client.test.mjspin every shape.- Rate-limit challenges (400 +
challenge_token) surface as send errors; solve the captcha at https://signalcaptchas.org/challenge/generate.html and lift the limit viaPOST /v1/accounts/{number}/rate-limit-challenge(see the profile's AGENTS.md notes).
License
MIT
