deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
English · Chinese
A DSH (DeepSeek Harness) bundle plugin that keeps the questions and options
generated by the ask_user_question tool pronoun-consistent. At
prompt-assembly time it rewrites the tool's questions.description in full
into a description carrying a fixed narrative-voice rule, and you can turn it
on and off anytime with the /voice command.
The ask_user_question tool asks the human a concise question and offers a few
clickable options. Without a fixed rule, the model drifts pronouns inside a
single option:
Two different "I"s in one option, and the user cannot tell them apart: who is providing the action? whose perspective is "I"?
This plugin fixes it at the prompt level: it writes a fixed narrative-voice rule into the tool's own description, so the model keeps question, header, label, and description in one consistent perspective throughout, and never calls the answerer "the user".
The rule splits the tool's fields into two groups:
voice: "user") — the answerer narrates: "I" = the
person answering, "You" = the AI.voice: "ai") — the AI narrates: "I" = the AI, "You" = the
person answering.Why this design? Why split the tool's fields into two groups?
question and header — this is the AI asking the user a question. The asker naturally speaks in the first person: "I (the AI) would like to ask you (the user) a question". So these two fields are fixed in the AI's point of view: "I" = the AI, "You" = the user, identical in both schemes and never changed — otherwise an "I" referring to the user would appear inside a question, and the reader would think the user is asking themselves.
label and description — this is the answer the user is about to give. An option is essentially a reply that the AI guessed in advance and wrote on the user's behalf. But whose voice should that reply use? There are two equally reasonable views — this is exactly where Scheme A and Scheme B diverge:
Scheme B (the answerer narrates): the user treats the option as a reply typed with their own hand — the first person is in the user's hands, and the AI merely guesses what the user would type and hands it back as an option. So inside an option: "I" = the user ("I'll restart it myself…"), "You" = the AI ("you'll guide me…").
Scheme A (the AI narrates): the user treats the option as a multiple-choice question handed to them by the AI — like the AI saying "I've run into a problem, here are two options, you pick: A, I'll handle it this way; B, I'll handle it that way". So inside an option: "I" = the AI ("I'll handle it for you…"), "You" = the user.
The rule applies only inside the ask_user_question tool (ordinary replies
and other tools are never affected), and only where pronouns actually
appear — it never forces pronouns into a question or option that does not
naturally need them.
Before every model request, DSH runs SystemPrompt.assemble(), puts the
assembled prompt into an assembly object, and dispatches it through the
Cordis waterfall event system-prompt/assemble — the waterfall's return
value is what is actually sent to the model.
This plugin registers a global: true listener on the waterfall: while enabled,
it finds ask_user_question in assembly.tools and rewrites
parameters.properties.questions.description (the real assembled shape is
JSON-Schema) in place, then return next() lets it through. Only the
per-request clone is touched — the registry schema and the parameter-validation
closure are unaffected.
assemble() runs once per message, so the /voice toggle takes effect from
the next message — the same in any conversation, including ones already in
progress.
One command, straight from this GitHub repo (verified in practice):
dsh plugin --profile <profile> add "github:TellToday/dsh-narrative-voice#main"
#main tracks the latest commit;dsh web).Equivalent alternatives:
# full git URL
dsh plugin --profile <profile> add "git+https://github.com/TellToday/dsh-narrative-voice.git"
# or a local directory (for development)
dsh plugin --profile <profile> add "C:\path\to\dsh-narrative-voice"
The package declares dsh.bundle.patch, so dsh plugin add automatically
appends it to dsh.profile.bundles as a bundle layer. Uninstall:
dsh plugin --profile <profile> remove @dsh-user/narrative-voice.
Prerequisite:
pnpmon PATH. A git-hosted install clones the repo through your system git (honoring your git proxy settings).
| Command | Effect |
|---|---|
/voice on |
enable the rewrite (effective from the next message) |
/voice off |
disable it (the tool description is restored) |
/voice user |
switch to Scheme B (the answerer narrates) and enable |
/voice ai |
switch to Scheme A (the AI narrates) and enable |
/voice |
show the current state (on/off + the active scheme) |
The command is handled by the host's commands service (not by the model), so
it takes effect instantly — no HMR dependency, no restart.
| Key | Default | Meaning |
|---|---|---|
voice |
user (Scheme B) |
which narrative scheme to use |
defaultActive |
true |
enabled right after install |
To change the defaults (instead of switching at runtime with /voice), override
the row by id in the profile's patch file
$DSH_HOME/profiles/<profile>/cordis.patch.yml. The patch replaces the whole
config, so list every key:
- id: narrative-voice
config:
voice: user # user (Scheme B: the answerer narrates) | ai (Scheme A: the AI narrates)
defaultActive: true # false = disabled by default, until /voice on
The config is validated by Config (a dependency-free Standard Schema
implementation): an invalid value fails the plugin load with a clear error.
dsh-narrative-voice/
├── lib/index.js # plugin body: Config, the assemble listener, the /voice command
├── cordis.patch.yml # bundle patch: inserts the plugin row into the host plane
├── test/
│ ├── functional.mjs # isolated functional tests (39 assertions)
│ └── run-test.ps1 # runs the tests directly (no install, no junction)
├── package.json # bundle metadata (dsh.bundle.patch; zero deps)
├── LICENSE # MIT
└── README.md / README.zh-CN.md
pwsh ./test/run-test.ps1
The plugin has no bare imports, so the tests run with plain node —
nothing to install, nothing to clean up.
MIT — see LICENSE.
CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。