dsh-qq-plugin
Manifest valid★ 2No project description is available for this repository yet.
dsh-qq-plugin
Drive the DeepSeek Harness agent on this computer from your phone, in QQ.
It goes through Tencent's official QQ bot platform (api.bot.qq.com, bound by scanning a QR
code), so there is no public IP to expose, no reverse proxy, and no third-party protocol client
in the middle. Bind once, and it keeps working.
Runs on the current DSH desktop app — developed and tested against DSH 0.2.0-rc.2.
This is the QQ counterpart of dsh-wechat-plugin. It is feature-aligned with it, with a few deliberate departures where the QQ protocol makes the WeChat machinery unnecessary — see Differences from the WeChat version.
Ask for a file in one sentence and it lands on your phone. Say "send me that DSH self-portrait from my desktop" in QQ and the agent finds the file on the computer, sends it back, and confirms with "sent — DSH 自画像.png on the desktop (29 KB, PNG)". The + at the right of the composer is the attachment entry: images, videos, voice and files all go in that way (see Media in both directions).
Features
- Text both ways. A QQ message becomes a DSH prompt, and the reply comes back to QQ.
- Images, files and voice in; images, videos and files out. Attachments are saved into the
session workspace and handed to the agent; the agent sends files back with
send_to_qq. A reply that runs long is turned into a.mdfile and sent to your phone instead of being cut. - One QQ chat, many DSH conversations.
/new,/list,/switch,/current,/cancel— or just say "switch to the other one". The list has session folders and sessions as two levels. - Pick the model and the reasoning level from your phone too.
/modellists the models with each one's reasoning levels;/model 2and/model 等级 highswitch them, effective from the next step. - Answer permission prompts and questions from your phone. Approvals are answered with 「允许」/「拒绝」 (allow/deny) or a numbered option; the agent's multiple-choice questions arrive in QQ with numbered options and a digit answers them — being away from the desk does not stall the task.
- Proactive push.
notify_qqlets any session on this machine message you — that is what makes "tell me when it is done" work. It works in practice: a ten-message burst was delivered in full, and the last six of those went out as proactive pushes. (Tencent's rules for unsolicited sends can change and the plugin assumes no quota; anything undelivered is queued and flushed the next time you speak.) - Scheduled tasks. Run through the DSH host's own
schedule_create/schedule_list/schedule_delete/schedule_update— just say "wake me at 23:12". The plugin does not reimplement what DSH already has; it is only the channel. - Settings page under Settings → QQ — nine settings, a native folder picker, and diagnostics.
Install
For the DSH desktop app on a machine that stays on. It needs DSH's workspace, tool and UI services — the standard desktop composition.
| DSH desktop | @deepseek-ai/dsh-desktop 0.2.0-rc.2 or newer |
| Phone | mobile QQ, to scan one QR code |
| QQ bot | nothing to register in advance. The bot application is created by that scan; you do not need an AppID/AppSecret beforehand |
| Tencent's rules | an uncertified bot is usable only by its administrator — that is, only by you, which is exactly what a personal remote control wants |
Open the plugin entry in the sidebar and choose 添加插件 (add plugin). The field takes a package name, a Git URL, a tarball, or a local path.
The name to install is
dsh-qq-pluginThe repository and the package share one name, so there is nothing to reconcile — but the
-pluginsuffix is easy to drop by accident. Copydsh-qq-pluginrather than typing it. The install command has to match the package name exactly.
From a terminal, that is one line (--profile web works just as well if that is the profile you use):
dsh plugin --profile desktop add dsh-qq-plugin
From npm — nothing to configure
dsh-qq-plugin
Add a version if you need one: dsh-qq-plugin@1.0.2.
From a local checkout — there is no build step to wait for
C:\path\to\dsh-qq
Point it at the directory that holds package.json — the folder is dsh-qq, the package it
declares is dsh-qq-plugin.
From a tarball
npm pack
# → dsh-qq-plugin-1.0.2.tgz
Paste the absolute path to that .tgz into 添加插件.
Install from a terminal instead
The app drives pnpm underneath, so the same thing headlessly:
dsh plugin --profile desktop add dsh-qq-plugin
dsh plugin --profile desktop add C:\path\to\dsh-qq
You then have to enable the bundle yourself — which is what 立即启用 does for you — in
~/.dsh/profiles/desktop/package.json:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-qq-plugin"
]
}
}
}
Wait for the install to finish, click 立即启用 (enable now), and restart DSH.
Getting started
After restarting DSH, open Settings → QQ and click 开始扫码 (start the QR bind). Scan the code with mobile QQ and confirm on the phone.
The scan is the whole registration. The bot application is created by that scan, so you do not need to register anything on Tencent's open platform first and you do not need an AppID/AppSecret in advance — nor a public IP or a reverse proxy.
The credentials land straight in DSH's credential store — the plugin's own state file records only the AppID, never the AppSecret. The channel connects by itself once bound: send any message to the bot in QQ and you should get a reply.
That is what it looks like in real use, after installing it: the agent put together a 13-poem
anthology on the computer and sent both the docx and the pdf straight to the phone; in the same
conversation, "tell me the computer's current time" came back as 2026-10-13 22:43:28 Tuesday,
along with an offer to set a reminder.
The computer was locked at that moment — the phone says 22:43 and so does the lock screen:
Locking the screen does not stop the agent, so it keeps working after you walk away while the conversation on your phone carries on. The reverse is also true: locking is exactly what you should do when you leave (see Security).
Usage
Just send a message and it reaches the agent:
你好
What follows is grouped by capability, and each group carries a real screenshot showing what it looks like and what it proves.
Conversation management, from your phone
One QQ chat sits in front of any number of DSH conversations. /list opens this workspace's
conversations, ten per screen:
One shot, four things proved: ten rows per screen, a six-character id at the end of every row
(7cee80, 4b55e1…), the current conversation marked ← 当前, and that footer line. The list also
tells you there are 89 more conversations in the other session folders.
/list all is the level above — the session folders (across projects and workspaces), each with
its conversation count; reply with a number or a name to enter one, and what you get is that
folder's conversation list (the screenshot replies 1, so it enters 「QQ 会话」):
Entering a folder only moves where you are browsing, not which conversation you are bound to — to actually switch you still pick a conversation inside it.
A bare digit is /switch <digit>: with the list on screen there is no need to type /switch,
just reply 2 — and it tells you which one you landed on, short id included, so you can check it
against the row you meant:
★ Quoting freezes the numbering: long-press the list message and quote it before replying with a number, and the number is read as that screen printed it. Sessions running while you read cannot make you land on the wrong one — the numbering is frozen inside that message. The list footer says so itself: 「引用这条消息再回编号,可以避免编号过期。」
Plain language works too. Here the user sends 「查看所有会话」 (show all conversations) — not a command:
The agent judges intent rather than matching fixed rules: it recognised "list conversations", listed the session folders, and told you that a number switches and 「查看其余」 turns the page. A switch also reports whether it succeeded or failed.
Type these to control which DSH conversation you are talking to:
| Command | What it does |
|---|---|
/new [title] | Start a fresh conversation. Anything after it becomes the title: /new 修复登录 starts a conversation called 「修复登录」 |
/list | Go straight to this workspace's conversations (ten per screen) |
/list all | List the session folders (workspaces) on this machine, one per line with its conversation count; reply with a number or a name to enter |
/switch 3 | Move to the 3rd row of that screen |
/switch e716f0 | Move by the short id at the end of the row — unrelated to numbering, and it never expires |
| Reply 「查看其余」 | Walk down ten rows (both levels, all the way to the end). Numbering runs on across screens (1–10, then 11–20) |
Reply 3, a name, or a trailing id | No need to type /switch while the list is on screen |
Quote a list message → /switch 3 | Numbered by that screen; the numbering is frozen inside that message |
/model | Which models there are, and each one's reasoning levels, with the one in use marked |
/model 2 | Switch to the 2nd one; a model name works too |
/model 等级 high | Change the reasoning level of the current model (the level names are the ones the list prints) |
/model 2 等级 high | Change the model and the level together |
/current | Which conversation you are in |
/cancel | Stop the task that is running |
/help | This list |
Spaces are optional: /switch3, /switch 3 and /switch:3 all work — these commands get typed
on a phone keyboard, where a dropped space is normal. A word that merely starts with a command is
not one, so /news is not /new.
Numbering expires: sessions run while you are reading the list, so by the time you reply "3" the 3rd row may be someone else. Every row therefore ends with a short id of at most 6 characters — reply with that, or quote that screen's list message and the number is read as that message printed it.
The list has two levels, ten rows per screen: /list opens this workspace's conversations;
/list all lists the session folders (workspaces), which you enter by number or name. Both
levels walk down ten rows with 「查看其余」, and numbering runs on across screens. When you reach
the end it says so instead of inviting you to keep going. Entering a folder only moves where you
are browsing, not which conversation you are bound to — to actually switch you still pick a
conversation inside it.
Models and reasoning levels
/model lists which models there are (marked 「(当前)」/「(默认)」 — current / default) and each
one's reasoning levels. /model <name|number> switches model; /model 等级 high changes only the
reasoning level; /model 2 等级 high changes both. A switch takes effect from the next step.
Reasoning levels come from the host adapter. The bundled DeepSeek adapter publishes
off/low/high/max(printed asOff/Low/High/Max), and those are the names/model 等级accepts, case-insensitively. There is no separate Chinese spelling — the level names are the ones the list prints, exactly as the WeChat channel does it.
Plain language works here too — "switch to the other one", "what conversations do I have", "stop". The agent judges the intent rather than matching fixed rules, and a switch tells you whether it succeeded or failed.
Media in both directions: images, video, voice, files
In: send the agent an image / video / voice message / file from your phone. Voice is transcribed server-side and you never see that step. Document attachments (Word, Excel) are readable too.
Here is a photo of a dog sent in, with a single question — "what is this":
What is worth noticing is not that it recognised a dog but the two details only someone who really looked at the image could give: the yellow-bodied orange-beaked duck toy, the person's legs at the left edge of the frame, and its unprompted remark that the photo is sideways (the phone was held the wrong way round).
Now a PDF sent in, again with a single question — "what is this":
Document attachments are read too: it says this is a five-page recruitment task document, exported from MarkText, and summarises it as "Part 1 environment setup / Part 2 using AI plus engineering discipline / Part 3 traditional OpenCV, four tasks".
Out: the agent sends files from this computer to your phone with send_to_qq — images arrive
as photos, videos as playable clips, everything else as a file attachment:
This is the story the cover image at the top of the page tells, and it is a complete pickup: say which file you mean, the agent finds it on the computer and sends it back, and it confirms with the filename and size so you can see at a glance whether it sent the one you meant.
★ A reply that runs long is turned into a .md file and sent to your phone (4000 characters by
default, adjustable; 0 = no limit):
Something that would not fit on one phone screen is far more usable as a file — not truncated, and not split into a dozen messages that flood the chat. The threshold is the settings page's "单条回复字数上限" (per-reply character limit).
Voice: hold to talk on the phone and the agent receives text (transcribed server-side, with nothing extra for you to do):
Under that 3-second voice bubble in the screenshot is a complete weather answer — the middle step, turning speech into text, is invisible on your phone, and does not need to be visible.
Interaction
These three have no screenshots — they are a question and an answer inside the same conversation, so a picture of one would look much like the ones above; prose says it better. This is what arrives on your phone:
- Permission approvals: when the agent wants to touch your files, a pending request appears in QQ saying what it intends to do. Reply 「允许」 (allow), 「拒绝」 (deny), or a numbered option — all three answer it, and the task carries on. (For such a prompt to appear at all, the permission preset has to be one of the two that can show prompts — see Settings.)
- Either/or and multiple-choice questions: the agent's question arrives with numbered options, and a single digit answers it.
- Quoting as context: long-press and quote one of its answers before speaking — for example "expand the second point in this one" — and it knows which answer "this one" is and which point "the second" is.
Proactivity: tell me when it is done
★★ notify_qq: any session on this machine can push a message to you — that is what "tell me
when it is done" is built on. On the left is the desktop session doing the work; on the right is
what arrives on mobile QQ:
Note that the two sides are not the same session: the work was done by that desktop session, and
the message arrived in the QQ conversation you are bound to. That is exactly why
allowCrossSessionNotify is on by default — without it, the session doing the work could not report
its own result.
It works in practice: a ten-message burst was delivered in full, and the last six of those went out as proactive pushes. (Tencent's rules for unsolicited sends can change and the plugin assumes no quota; anything undelivered is queued and flushed the next time you speak.)
Scheduled tasks run through the DSH host's own schedule_create / schedule_list / schedule_delete / schedule_update — the plugin does not reimplement what DSH already has, it is only
the channel. Plain language is enough:
When the time comes it really does push one over (this is a proactive push, not you asking):
"Remind me every morning at 9 what I have to do today", "send me the current time every hour",
"show me which scheduled tasks I have" all belong here. Showing them is schedule_list, and so are
deleting and changing them.
Reliability
- A session that is archived or disappears is re-bound automatically, with a line telling you so rather than the message being swallowed: the old conversation was archived → a new one is created to carry on, and you are notified.
- State writes on Windows have retries and backoff (for the "file is in use" kind of EPERM).
- Inbound messages with empty content or an unknown frame type leave a log line instead of leaving no trace at all.
Tools the agent can use
| Tool | What it does |
|---|---|
send_to_qq | Send a file from this machine to the conversation that asked. Images arrive as photos, videos as playable clips, everything else as a file attachment |
notify_qq | Push to your current QQ conversation from any session. This is what "tell me when it is done" is built on, and it works in practice (a ten-message burst arrived in full; the last six were proactive pushes) |
qq_session_control | Look at, switch, create or interrupt conversations; list (or switch) models and their reasoning levels |
The
qq_/_qqin those names is deliberate: tool names are unique across the whole host scope, and the WeChat plugin already occupiessession_control. Two plugins claiming the same name means whichever registers second fails silently.
Those three come from the plugin. Scheduling is not among them because it does not need the
plugin to implement anything: it runs through the DSH host's own schedule_create /
schedule_list / schedule_delete / schedule_update, and the plugin's part is only to carry the
result over the QQ channel.
Settings
Settings → QQ, in six groups, in the order the page renders them:
- 帮助 (Help) — the one button on the page that opens the user handbook
(
docs/HANDBOOK-USER.md, the file that lists every command and every setting) - 连接 (Connection) — channel on/off, the QR bind, the bound account, unbind
- 权限 (Permissions) — the permission preset for QQ conversations (only
autoand «Follow the DSH setting, same as the desktop» can still show approval prompts) - 行为设置 (Behaviour) — the table below
- 指令 (Commands) — the slash-command reference the page prints (
/help,/new,/list,/switch,/current,/cancel,/model) - 诊断 (Diagnostics) — log tail, workspace path, conversation count, pending interactions
This is the page in full (the ones below are the same page section by section):
These are the top two cards — and the first thing you see after installing it — 帮助 (Help) and 连接 (Connection):
The QR bind is in the lower half of that shot: click 重新扫码, scan with mobile QQ and confirm on the phone. You do not have to register on Tencent's open platform first, and you do not need an AppID/AppSecret — the bot application is created by that scan. The 已绑定账号 row is what it looks like once bound (AppID ← an irreversible fingerprint · bind time), and 已连接 beside it is the channel's state right now.
The middle two groups are permissions and the command reference:
The permission preset has three Chinese options, with the default selected in the shot:
| Option | Meaning |
|---|---|
完全访问:不询问,直接执行 — full access, do not ask | The default. The agent does not ask you — and an ask would go to QQ, where the task then waits for your answer |
自动审核:电脑上判断 — automatic review, judged on the computer | Let the policy on the computer decide; prompts are possible |
跟随 DSH 设置:和桌面端一样 — follow the DSH setting, same as the desktop | Exactly what the desktop does; prompts are possible |
Only the last two can still send a permission prompt to QQ. To see for yourself that an approval can flow back to a phone, set this to one of the presets that can prompt — a pending request then arrives in QQ, and 「允许」/「拒绝」 or a numbered option answers it.
That group is /help as the page prints it, and the user handbook is what every command and
setting is measured against. The line underneath — that plain language works too — is also true:
DSH first says what it is about to do and waits for you to agree; turn off 「会话操作先确认」 above to
skip that step.
Lower on the page, the behaviour settings and the session folder:
The session folder setting has a native folder picker — the 选择… button. The box holds the text you typed and 当前生效 to its right is the path actually in use; keeping the two apart is deliberate, so that when a path is wrong you can see what you wrote and change it while still knowing which folder is really being used.
Last is the diagnostics block, your first clue when something goes wrong:
Diagnostics gives you the log path plus the tail of the log, so most problems you can see for
yourself — whether a send failed, whether you were rate-limited, whether an attachment never
landed. The last line in the shot happens to be a successful send_to_qq.
Adding those cards up, there are nine settings in total:
| Setting | Default | What it does |
|---|---|---|
permissionPreset | danger-full-access | Permission preset for QQ conversations. A permission prompt that arrives on a phone stalls the task until you answer it, so the default keeps the gate open — see Security |
requireConfirmation | on | Whether the agent asks you once before it performs a conversation operation |
allowCrossSessionNotify | on | Turn it off and notify_qq may only push to the caller's own conversation (the stricter posture) |
mergeWindowMs | 10000 | How long an attachment-only message waits for you to add an instruction. 0 = do not wait |
autoReplyAttachments | on | Whether the attachments are handed to the agent when no instruction arrives inside that window. Off does not mean "ignore the attachments" — it means "wait for the user to say something" |
maxReplyChars | 4000 | Above this, the reply is sent as a .md file. 0 = no limit |
settleMs | 1200 | How long the stream must go quiet before the reply is sent |
presenceNote | a short Chinese note | Sent to the agent with every message: you are on a phone, you cannot see this computer's screen, and if it wants you to look at something it should just send it |
workspacePath | empty = $DSH_HOME\dsh_qq | The conversation folder. Must be an absolute path; a bad value only means "did not take effect", never "the plugin will not start" |
Settings are persisted and survive a restart. An older state file (one with no settings
block) still reads fine.
Every setting is defined in exactly one place — lib/settings.js's SETTINGS_SCHEMA, one
validator plus one default per entry. Loading, saving, the settings page and the round-trip test
are all derived from it, so adding a setting is one line there and nothing else can drift out of
step. Plain JavaScript has no compiler to catch a forgotten setting, so
scripts/verify.mjs asserts that the schema's key set, the key set the runtime actually holds, and
the key set written to disk are the same set — that assertion is the compiler we do not have.
Next to workspacePath, the currently in effect line shows the path actually in use, while the
box holds the text you typed. Keeping the two apart is deliberate: when a path is wrong you can see
what you wrote and change it, while still knowing which folder is really being used.
⚠️ Changing the path does not move existing conversations. They stay in the old folder; only conversations created afterwards use the new path. That is the only safe behaviour — the host compares
cwdwhen it adopts an existing conversation, and a mismatch raisesApiSessionCwdConflict, after which that conversation can never be picked up again.
Where files land
| What | Path |
|---|---|
| Conversation workspace (shown in the desktop app as 「QQ 会话」) | $DSH_HOME\dsh_qq (changeable with workspacePath) |
Inbound media and long replies as .md | <conversation workspace>\媒体 |
| Plugin state and log | $DSH_HOME\qq\state.json, $DSH_HOME\qq\boot.log |
If something does not work
| Symptom | Where to look first |
|---|---|
| No QQ block in the settings page | The client plugin did not load. Look at the renderer console section of %APPDATA%\@deepseek-ai\dsh-desktop\logs\crash-*-web-boot.log |
| The whole app will not start | Same file. The fatal error itself says very little; the real cause is in the renderer console |
| The bot does not reply | boot.log for 入站; the errors array of GET /.dsh-qq-plugin/status |
| Images or voice do nothing | boot.log for 附件下载失败 or 附件已停靠 |
| "It will not send" | boot.log for 待发已入队 — proactive-push rules can change and the plugin assumes no quota; anything undelivered is flushed the next time you speak |
The debugging HTTP endpoints all live under /.dsh-qq-plugin/: status, log, settings,
permission-presets, accounts, login, start, stop and a few more.
Differences from the WeChat version
Feature-aligned, but a few things differ on purpose, because the QQ protocol hands us something the WeChat one does not:
| Quoted messages | QQ's msg_elements carry the quoted text directly, so WeChat's quoteHistory cache is not needed |
| Voice | The QQ service returns voice_wav_url plus asr_refer_text, so there is no SILK decoding step and none of its caveats |
| Send queue | WeChat has to queue because iLink forbids unsolicited sends; QQ allows proactive push, and the queue exists to re-send anything that was not delivered (network, platform rejection, changed rules) rather than to cover one specific failure |
| Tool names | See above — global uniqueness across the host scope |
| Installation | There is no build step and no allowBuilds prompt: the plugin is plain JavaScript, so a Git install packs and runs it as-is |
Security
Only you can reach it. The channel talks to Tencent's official QQ bot service and nothing else — no third-party service, no self-hosted relay in the middle. The bot is a separate application whose administrator is your QQ account, and it was created by your own scan; an uncertified bot is limited to its administrator. It is not a QQ contact you can hand out: nothing searches for it and nobody can add it. There is no "someone else could message it" surface, which is why no allowlist exists: the platform's own rules already are one.
The AppSecret is a long-lived key, and it is treated as one. It is written to DSH's credential
store as a grant record rather than into the plugin's plaintext state file, which keeps only the
AppID. Rotating it means binding again from the settings page.
Three things are still true and worth knowing:
Conversations from QQ run with full permissions by default — the agent does not ask before it
acts. That is deliberate: a permission prompt is delivered to QQ and the task then waits there, so
on a phone every command would stall until you answered a conversation you had walked away from. Set
Permission preset for QQ conversations to auto (or to the profile's own policy) in the settings
page if you want the same guardrails you have at the desk.
The agent runs as you. It can execute commands and read and write files, with your user's permissions — that is what it is for, and it means anything you can do on this machine, it can do. So: skim what you asked for before walking away, and lock the screen when you leave. Locking does not stop the agent, and it keeps anyone passing by out of your desktop.
Any session on this machine can push to your QQ by default. That is what makes "tell me when it is done" work: the session doing the work is usually not the one your QQ conversation is bound to, and without this it could not report its own result. Turn off allowCrossSessionNotify to restore the stricter rule, where only the bound session may send.
Documentation
| docs/HANDBOOK-USER.md | The user handbook — what the page's one help button opens, and the file that lists every command and every setting (Chinese) |
| docs/POSTMORTEM.md | Every real failure met while building this — each one cost something, and each one is why a checker exists (Chinese) |
| docs/SETTINGS-CONTRACT.md | The host/client route contract behind the settings page, read off the actual call sites (Chinese) |
| docs/RELEASING.md | How a version gets published: the tag, OIDC, and the one-time Trusted Publisher setup (Chinese) |
| docs/STEP3-能力调研与实现规格.md | QQ interactive-message and streaming research. Status: paused, not implemented — no buttons and no streaming exist in this code today; the measurements are real and are kept for whenever that work resumes (Chinese) |
Development
Plain JavaScript, no build step, no transpiler. CI runs Node 24; the manifest declares no engines
range, because the plugin only has to run inside the DSH host that is already there.
npm ci
node scripts/verify.mjs # host side
node scripts/verify-client.mjs # client registration shape (not a visual check)
node scripts/verify-send-path.mjs # the send path, against a fake SDK
782 host checks, 108 client checks, 60 send-path checks.
Those three numbers are read out of the runs above, not typed by hand: tools/check-doc-counts.mjs
compares them against what the scripts actually print, and the CI job fails naming the file if any of
them disagrees. Change a test and the number here, or the gate stops the build.
Standing checkers
Four checkers live in tools/. Each one corresponds to a real incident, so run them before
committing:
node tools/check-unbound.mjs . # used but never imported
node tools/check-unused-imports.mjs . # imported but never used
node tools/check-doc-counts.mjs --run # the documented counts, and the repository name in publish.yml
node tools/check-cross-plugin.mjs <profileDir> # shared resources across plugins
check-doc-counts.mjs reads the three numbers out of the actual output of the three suites and
compares them against this file, README.zh.md and docs/RELEASING.md, naming the file, the number
written and the number measured when they disagree. In CI it reads the output the three test steps
captured, so it costs a second rather than a second full run. --run is the local form: it runs the
suites once itself.
It carries a second job, for the same reason: the four Trusted Publisher fields in
publish.yml's header are typed by hand and a typo in them shows up as a 403 at publish time and
as nothing else, so the checker compares that block against package.json (organization and
repository), the workflow's own filename and its name:, and both READMEs' CI badges. The
repository it names was once dsh-qq while every other copy said dsh-qq-plugin — that is the
failure this half exists to catch.
check-cross-plugin.mjs is a local check and does not run in CI: it reads a real DSH profile —
the dsh.profile.bundles list and the plugins installed under that profile's node_modules — and a
CI runner has neither. It checks the two things every plugin shares: the client plugin top-level
scope (the shell concatenates every enabled client.js into one script, so a top-level const in
one plugin collides with another's) and host tool names, which are globally unique.
Each checker carries a reverse control: it has to catch the failure it was written for, or it is declared untrustworthy and exits non-zero. A checker that always prints "pass" is worse than no checker at all — it makes people believe something was verified.
Two probes under smoke/ (step3-probe.mjs and step4-probe.mjs) talk to Tencent's live service
and need real bot credentials; the other two (step4-surface.mjs and check-sdk-surface.mjs) are
offline surface checks that need neither credentials nor a network. None of the four is in CI —
the two probes because credentials do not belong in a public repository's CI, the two surface
checks because they are one-off research probes that print observations rather than assert
pass/fail.
Licence
MIT — see LICENSE.
Versions
| Latest version | Published | Size |
|---|---|---|
| 0.0.0-stage | — | — |
| 1.0.0 | — | — |
| 1.0.1 | — | — |
| 1.0.2 | — | — |
Comments
Loading…
From the same category
by ranxianglei
基本稳定可用 100K tokens is enough. Universal context-compression proxy for ALL AI coding agents,10w上下文足矣
★ 1.2k
↓ 619.8k/wk
MIT
TypeScript
Oct 14, 2026
dsh plugin --profile web add billion-contextby Han-1413141
DeepSeek Harness session cost meter plugin: session/daily cost, budget, history, OpenCode Go quota, official & custom-provider balance, Codex-like token heatmap, peak/off-peak pricing with pre-switch
★ 410
↓ 24.7k/wk
MIT
JavaScript
Oct 13, 2026
dsh plugin --profile web add dsh-cost-meterby Nwflower
Import 14+ external agent chat histories (Claude Code, Codex, ChatGPT, Cursor, Gemini, Reasonix, opencode, ZCode, Grok Build, OpenClaw, Pi, Hermes, Kimi CLI, DSH) into DeepSeek Harness as resumable se
★ 230
MIT
JavaScript
Oct 13, 2026
dsh plugin --profile web add dsh-chat-importby Totoro-qaq
DeepSeek Harness plugin for previewable cross-preset session migration. Fixed-schema handoffs preserve state, source-model intent, and unresolved images; the original session stays untouched.
★ 163
MIT
JavaScript
Oct 12, 2026
dsh plugin --profile web add dsh-plugin-bridgeby Anionex
deepseek harness对话和代码状态回退插件 | DSH — rewind conversation and workspace state, powered by a persistent Change Ledger
★ 133
BSD-3-Clause
JavaScript
Oct 1, 2026
dsh plugin --profile web add @anionex/dsh-turn-rewindby MichengAI
DSH Archive Manager — 在 DeepSeek Harness 中安全查看、恢复和管理已归档会话 · Safely view, restore, and manage archived sessions in DSH
★ 131
↓ 17k/wk
Apache-2.0
JavaScript
Oct 13, 2026
dsh plugin --profile web add @michengai/dsh-archive-manager