WASD258-jpg
dsh-prompt-inject
Per-session system prompt injection for DSH: template library, per-session picker, default template for new sessions.
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 15, 2026
- Updated
- Aug 15, 2026
Introduction
dsh-prompt-inject
中文 | English
Per-session system prompt injection for DeepSeek Harness (DSH): every session carries its own custom prompt section, rendered right after the persona and before tool guidance.
This is a community project. It is not an official DeepSeek plugin and does not imply any endorsement by DeepSeek.
Features
- Per-session injection — each session independently picks a template (or "none"); different sessions run different prompts without interfering. The section is registered from
agent.ctx(cordis caller binding → that session's scope layer), so it affects only top-level sessions. - Template library — save / edit / delete named templates (
name + text + order); a default template applies to new sessions automatically. - Composer tool-row dropdown — the "会话提示词" selector (
conversation.input.rightslot) picks the current session's template straight from the input row, no typing required. - Settings sections — Settings → "会话提示词模板" manages the library and the new-session default; Settings → "系统提示词" is a read-only viewer of any live session's fully assembled system prompt.
- Layered viewer — sections grouped by origin: global layer (all presets), preset layer (that mode's sections), session layer (this session), and the injected section — each with name, order, and character count.
- Variables —
{{model}}/{{cwd}}interpolation works in template text. - Live switching — switching disposes the old section and registers the new one; the session's next model request sees it. No restart.
- Verification endpoint —
GET /assemblereturns the assembled prompt plus acompleteBlocksInjectionflag when the preset locks the prompt. - Multiple entry points —
/promptcommand,prompt_injecttool, HTTP API, and the UI all drive the same configuration.
Install
This is a bundle-type plugin: it runs inside the DSH web profile process and registers host-side services plus a web client module. Requires the DSH web profile (@deepseek-ai/dsh-web-app).
-
Clone or download this repository:
git clone https://github.com/WASD258-jpg/dsh-prompt-inject.git -
Install into the DSH profile dependency directory (choose one):
Option A: npm install (recommended)
cd "$env:USERPROFILE\.dsh\profiles" npm install @wasd258/dsh-prompt-inject --no-saveOption B: copy from source
$src = '.\dsh-prompt-inject' $dst = "$env:USERPROFILE\.dsh\profiles\node_modules\@wasd258\dsh-prompt-inject" New-Item -ItemType Directory -Force -Path "$dst\lib" | Out-Null Copy-Item "$src\package.json", "$src\lib\index.js", "$src\lib\client.js", "$src\LICENSE", "$src\NOTICE" $dst -ForceOn Linux/macOS use
~/.dsh(or$DSH_HOMEif set) instead of$env:USERPROFILE\.dsh. -
Add the package to the profile's bundle list — append
"@wasd258/dsh-prompt-inject"to thedsh.profile.bundlesarray in$DSH_HOME/profiles/<profile>/package.json(e.g.web):{ "name": "dsh-profile-web", "private": true, "dependencies": {}, "dsh": { "profile": { "bundles": [ "@deepseek-ai/dsh-base", "@deepseek-ai/dsh-web-app", "@wasd258/dsh-prompt-inject" ] } } } -
Restart DSH web (or hot-assemble the bundle), then refresh the browser page so the client module (
lib/client.js) loads (immediately: true). The "会话提示词" dropdown and the two Settings sections appear.
Usage
Commands
| Command | Description |
|---|---|
/prompt (or status/list/?) | Show current prompt, default template, and library |
/prompt use <templateId|none> | Apply / detach a template for the current session |
/prompt save <名称> <文本> | Save a template (name = single whitespace-free token; rest of the line = text) |
/prompt delete <templateId> | Delete a template (also clears it as default if it was) |
/prompt default <templateId|none> | Set / clear the default template for new sessions |
Example session (output as emitted by the plugin; ids are auto-generated as slugified-name + 4-char base36 timestamp, e.g. code-reviewer-9k2m):
/prompt
当前会话提示词:「Code Reviewer」(code-reviewer-9k2m)
默认模板:(无)
模板库:
code-reviewer-9k2m「Code Reviewer」(120 字) ← 当前会话
safety-auditor-1b7e「安全审查员」(85 字)
用法:
/prompt use <模板id|none> —— 当前会话应用 / 移除模板
/prompt save <名称> <文本> —— 保存新模板
/prompt delete <模板id> —— 删除模板
/prompt default <模板id|none> —— 设置新会话默认模板
/prompt use code-reviewer-9k2m
当前会话已应用「Code Reviewer」(下一轮生效)。
/prompt use none
当前会话已移除提示词(下一轮生效)。
/prompt save 安全审查员 You are a strict safety auditor.
模板已保存:安全审查员-abc1「安全审查员」
使用:/prompt use 安全审查员-abc1
/prompt delete safety-auditor-1b7e
模板已删除:safety-auditor-1b7e
/prompt default code-reviewer-9k2m
默认模板已设置:code-reviewer-9k2m(此后新会话自动应用)。
/prompt default none
已清除默认模板。
/prompt use missing-id
模板不存在:missing-id(/prompt list 查看)
UI
- Composer tool row → "会话提示词" dropdown: pick the current session's template ("提示词:无" = none). Applied immediately; effective on the next request; refreshes from the server every 5 seconds.
- Settings → "会话提示词模板": create / edit / delete templates (delete asks for confirmation) and set the new-session default template.
- Settings → "系统提示词": pick a live session, view its assembled prompt grouped by layer (global / preset / session / injected); an amber warning appears when the preset's complete persona blocks injection.
prompt_inject tool
The model can manage prompts itself via the prompt_inject tool ("Per-session system prompt injection… Changes take effect on the NEXT model request of that session"):
| action | Required params | Effect |
|---|---|---|
get | — | Current template, default template, full library |
apply | templateId ("none" detaches) | Set / detach the current session's template |
save | name, text; optional templateId, order | Create a template, or update if templateId matches an existing one |
delete | templateId | Remove a template |
default | templateId ("none"/"" clears) | Set the new-session default |
HTTP API
Routes under /api/dsh-prompt-inject:
| Method | Path | Description |
|---|---|---|
| GET | /status?sessionId= | Current template + default + library (sessionId optional) |
| GET | /assemble?sessionId= | Assembled system prompt + per-section layer breakdown + complete-persona detection |
| GET | /sessions | Live (root) sessions with preset ids |
| POST | /apply | {"sessionId","templateId"} — apply a template to a session |
| POST | /save | {"name","text","id"?,"order"?} — create/update a template |
| POST | /delete | {"id"} — delete a template |
| POST | /default | {"templateId"} — set the new-session default |
GET /status
GET /api/dsh-prompt-inject/status?sessionId=abc123
{ "ok": true, "sessionId": "abc123", "current": "code-reviewer-9k2m", "currentName": "Code Reviewer", "defaultTemplate": "", "templates": [{ "id": "code-reviewer-9k2m", "name": "Code Reviewer", "text": "You are a strict code reviewer…", "order": 1 }] }
GET /assemble
GET /api/dsh-prompt-inject/assemble?sessionId=abc123
{ "ok": true, "sessionId": "abc123", "presetId": "standard", "presetName": "Standard",
"length": 3842, "injectedLength": 120, "injected": "You are a strict code reviewer…",
"complete": null, "completeBlocksInjection": false,
"sections": [
{ "name": "persona", "order": 0, "text": "…", "layer": "global", "category": "system" },
{ "name": "tool-guidance", "order": 100, "text": "…", "layer": "standard", "category": "system" },
{ "name": "prompt-inject", "order": 1, "text": "…", "layer": "agent", "category": "injected" } ],
"prompt": "…" }
sections breakdown: layer is global, the preset id, or agent; category is injected (this plugin) or system. On a complete-persona preset the response sets complete: "<section name>" and completeBlocksInjection: true.
Error cases
| Condition | Status | Body |
|---|---|---|
| Non-loopback client | 403 | {"ok":false,"error":"prompt-inject API is loopback-only"} |
Invalid Host header | 403 | {"ok":false,"error":"invalid host header"} |
| Cross-origin POST | 403 | {"ok":false,"error":"cross-origin request rejected"} |
Non-JSON Content-Type | 415 | {"ok":false,"error":"Content-Type 必须是 application/json"} |
| Invalid / oversized JSON (cap 128 KiB) | 400 | {"ok":false,"error":"请求体不是有效 JSON"} |
| Unknown route | 404 | {"ok":false,"error":"unknown route: <route>"} |
Session not live (/assemble, /apply) | 404 | {"ok":false,"error":"会话 <id> 不在运行中"} |
| Template not found / bad input (business) | 200 | {"ok":false,"error":"模板不存在:<id>"} (or name 不能为空, 文本过长(上限 65536 字符)) |
How it works
- Agent-scope injection — on
agent/createdthe plugin resolves the session's template (sessions[id] ?? defaultTemplate) and registers aprompt-injectsection fromagent.ctx; the cordis caller binding puts it in that agent's scope layer, so it affects only that session. Only root sessions are injected;agent/disposedcleans up. - Live switching —
applyForSessiondisposes the old section and registers the new one; sectiontextis a function evaluated on every assembly, so the next request sees the change — no restart needed. - Persistence — config at
$DSH_HOME/dsh-prompt-inject.json(templates,sessions,defaultTemplate), written atomically (tmp file + rename); re-applied automatically when agents are re-created on resume/restart. - Ordering & variables — templates default to
order: 1(persona at 0, tool guidance at 100+); text supports{{model}}/{{cwd}}; empty text sections disappear from the assembly automatically. - KV cache note — injected text joins every request of that session; changing it changes the KV cache prefix for the next request (normal cost).
- Host half — injects
systemPrompt,tools,commands,agents,webServer; the web half injects theslotssystem (conversation.input.right+ twosettings.sectionentries).
Privacy & security
- Threat model — the API is unauthenticated by design and serves only the local machine: any local process can read assembled prompts and the library and drive writes. Run DSH in a trusted local environment only; the loopback gate is defense-in-depth, not an authentication boundary.
- Loopback only — source address must be
127.0.0.1,::1, or::ffff:127.0.0.1; anything else gets 403. - DNS-rebinding protection —
Hostmust be a loopback hostname on the current port (a missingHostfalls back to the address check). Applies to GET and POST alike, since/assemblecan return sensitive data. - Write protection — POST additionally requires a same-origin
OriginandContent-Type: application/json, blocking malicious web pages. - No telemetry — no outbound requests, no analytics, no credential access. Config is plaintext at
$DSH_HOME/dsh-prompt-inject.json; templates may be sensitive, so keep the home directory private. - Request bodies are capped at 128 KiB (131072 bytes).
Compatibility
- Requires a DSH (DeepSeek Harness) web profile (
@deepseek-ai/dsh-web-app). - Node.js >= 22.19.0. Current release:
0.1.1(@wasd258/dsh-prompt-inject). - Tested against the official repository at commit
47f9438(bundle interfaces, web profile, Windows 11). - complete persona boundary — presets with a
complete: truepersona (e.g.minimal,anchored-standard) lock the system prompt to that single section: injection does not take effect there./assemblereports this viacompleteBlocksInjection; the Settings viewer shows an amber warning. Use a non-complete preset (Standard, Creator, …) or put the text into the preset's persona. - The viewer prefers selecting a non-complete session (only those show meaningful injection results).
Official ecosystem requirements
Per the DeepSeek Harness CONTRIBUTING, the official repository does not accept external PRs; community plugins live in their own repositories and carry the dsh-plugin topic. This repository follows that: an independent community project, not affiliated with or endorsed by DeepSeek.
License
MIT — see LICENSE. Portions reference DeepSeek Harness; see NOTICE for provenance and the non-affiliation statement.