Back to home

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.right slot) 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 endpointGET /assemble returns the assembled prompt plus a completeBlocksInjection flag when the preset locks the prompt.
  • Multiple entry points/prompt command, prompt_inject tool, 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).

  1. Clone or download this repository:

    git clone https://github.com/WASD258-jpg/dsh-prompt-inject.git
    
  2. 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-save
    

    Option 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 -Force
    

    On Linux/macOS use ~/.dsh (or $DSH_HOME if set) instead of $env:USERPROFILE\.dsh.

  3. Add the package to the profile's bundle list — append "@wasd258/dsh-prompt-inject" to the dsh.profile.bundles array 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"
          ]
        }
      }
    }
    
  4. 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

CommandDescription
/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"):

actionRequired paramsEffect
getCurrent template, default template, full library
applytemplateId ("none" detaches)Set / detach the current session's template
savename, text; optional templateId, orderCreate a template, or update if templateId matches an existing one
deletetemplateIdRemove a template
defaulttemplateId ("none"/"" clears)Set the new-session default

HTTP API

Routes under /api/dsh-prompt-inject:

MethodPathDescription
GET/status?sessionId=Current template + default + library (sessionId optional)
GET/assemble?sessionId=Assembled system prompt + per-section layer breakdown + complete-persona detection
GET/sessionsLive (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

ConditionStatusBody
Non-loopback client403{"ok":false,"error":"prompt-inject API is loopback-only"}
Invalid Host header403{"ok":false,"error":"invalid host header"}
Cross-origin POST403{"ok":false,"error":"cross-origin request rejected"}
Non-JSON Content-Type415{"ok":false,"error":"Content-Type 必须是 application/json"}
Invalid / oversized JSON (cap 128 KiB)400{"ok":false,"error":"请求体不是有效 JSON"}
Unknown route404{"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/created the plugin resolves the session's template (sessions[id] ?? defaultTemplate) and registers a prompt-inject section from agent.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/disposed cleans up.
  • Live switchingapplyForSession disposes the old section and registers the new one; section text is 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 the slots system (conversation.input.right + two settings.section entries).

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 protectionHost must be a loopback hostname on the current port (a missing Host falls back to the address check). Applies to GET and POST alike, since /assemble can return sensitive data.
  • Write protection — POST additionally requires a same-origin Origin and Content-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: true persona (e.g. minimal, anchored-standard) lock the system prompt to that single section: injection does not take effect there. /assemble reports this via completeBlocksInjection; 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.