WM-CODER/custom-first-control-prompt
DeepSeek Harness plugin for insert custom prompt
编辑导读Editor's Brief
## 核心特性
- 通过 harness system-prompt assembly 注册有序的 system-prompt sections,包含必填的 name、order 和 text 字段,以及可选的 enabled 标志。
- 在 llm/stream 拦截期间,将配置的 user/assistant 参考对话作为真实交替消息注入到普通对话请求的开头。
- 仅在请求路径中保留注入的参考内容,避免写入 session log,并使真实轮次编号仍从 1 开始。
- 在激活时一次性构建参考消息,作为深度冻结的共享对象,并在通过 ctx.llm.stream 重新分发前克隆请求。
- 提供面板配置编辑器,将按 id 定位的覆盖项写入 profile cordis.patch.yml,同时保留其他条目。
## 使用场景
- 为整个部署添加必须在每个普通对话请求中的 persona 之前渲染的通用规则或稳定指令。
- 提供固定参考对话以塑造模型行为,而不在 session history 中持久化种子消息。
- 使用 web profile panel 检查注入的 LLM 请求,并通过新会话提问验证配置内容。
## 技术细节
- 该插件可从 GitHub、npm 或本地目录安装到 DSH web profile,安装或配置更改后需要重启 web app。
- section 条目通过 ctx.systemPrompt.section() 注册,参与变量插值、作用域遮蔽和 assembly waterfall。
- 监听器会跳过带有辅助 purpose 标记的调用、没有 sessionId 的手动构建请求,以及来自 subagent 的会话,除非 includeSubagents 为 true。
- 单个条目的 section 或 history 问题会降级为跳过该条目并输出警告,而不是导致插件树失败。
## 注意事项
- 聊天 UI 不会显示注入的参考历史,因此模型可见内容必须从 session log 和部署配置中重建。
- 种子文本是模型可见的 prompt 材料,不应被视为可信通道。
- 配置更改需要重启 web 才能作用于新请求,并且不支持会话中途编辑。
## Core Features
- Registers ordered system-prompt sections through the harness system-prompt assembly, with required name, order, and text fields and optional enabled flag.
- Injects configured user/assistant reference exchanges as real alternating messages at the head of ordinary conversation requests during llm/stream interception.
- Keeps injected reference content on the request path only, avoiding session-log writes and leaving real turn numbering starting at 1.
- Builds reference messages once at activation as deep-frozen shared objects and clones requests before redispatching them through ctx.llm.stream.
- Provides a panel configuration editor that writes an id-targeted override into profile cordis.patch.yml while preserving other entries.
## Use Cases
- Adds deployment-wide house rules or stable instructions that must render before the persona in every ordinary conversation request.
- Supplies fixed reference exchanges that shape model behavior without persisting seed messages in session history.
- Uses the web profile panel to inspect injected LLM requests and verify configured content through a fresh-session question.
## Technical Details
- The plugin installs into the DSH web profile from GitHub, npm, or a local directory and requires a web app restart after installation or configuration changes.
- Section entries register via ctx.systemPrompt.section(), participating in variable interpolation, scope shadowing, and the assembly waterfall.
- The listener skips auxiliary purpose-stamped calls, hand-built requests without sessionId, and subagent-origin sessions unless includeSubagents is true.
- Per-entry section or history problems degrade to skipped entries with warnings instead of failing the plugin tree.
## Notes
- The chat UI does not display injected reference history, so model-visible content must be reconstructed from session log plus deployment config.
- Seed text is model-visible prompt material and should not be treated as a trusted channel.
- Configuration changes require a web restart for new requests, and mid-session editing is not supported.
安装Install
dsh plugin --profile web add @wm-coders/dsh-custom-first-control-prompt
把 WM-CODER/custom-first-control-prompt 加入你的 DSH 配置(web profile)即可启用。
READMEREADME
@wm-coders/dsh-custom-first-control-prompt
English | 中文
Deployment-configured prompt prefix. Ordered system-prompt sections render ahead of the deployment persona, and configured reference user/assistant exchanges are injected into every ordinary conversation request — as real alternating messages prepended on the request path (llm/stream interception, zero session-log writes). Static content renders byte-identically on every request, preserving prefix-cache reuse.
Installation
# From GitHub (recommended — built artifacts committed, no build approval needed)
dsh plugin --profile web add github:WM-CODER/custom-first-control-prompt
# From npm
dsh plugin --profile web add @wm-coders/dsh-custom-first-control-prompt
# From local directory (development)
dsh plugin --profile web add ./path/to/custom-first-control-prompt
After installation, restart the web app (dsh --profile web or run restart-web.ps1 / restart-web.sh).
To uninstall:
dsh plugin --profile web remove @wm-coders/dsh-custom-first-control-prompt
Install / deployment / debugging obstacles and test methods: DEBUG-NOTES.zh.md (in Chinese; web fail-loud root causes, duplicate-id inserts, API verification chains — all paths scrubbed). One-command install: INSTALL.md. Full cross-machine walkthrough: INSTALL-FULL.zh.md (Chinese).
Configuration
- id: custom-first-control-prompt
name: '@wm-coders/dsh-custom-first-control-prompt'
config:
sections:
- name: house-rules
order: -50
text: |
…stable system text…
history:
- user: …
assistant: …
includeSubagents: false
| Key | Default | Meaning |
|---|---|---|
sections |
— | Ordered system-prompt fragments; absent or empty registers nothing. |
sections[].name |
required | Entry name; the registry sees custom-first-control-prompt:<name>. |
sections[].order |
required | Render position among all sections. Factory convention: harness identity −100, persona 0, tool guidance 100–199; values below 0 prepend ahead of the persona. |
sections[].enabled |
true |
false keeps the entry in configuration without registering it. |
sections[].text |
required | Static section text; keep it free of volatile values — any change breaks prefix reuse from the first changed token. |
history |
— | Ordered user/assistant reference exchanges injected ahead of every ordinary conversation request; both texts must be non-empty and free of reserved tags (case-insensitive: <user>, <assistant>, <exchange>, <custom-history and all closing tags). Absent or empty injects nothing. |
includeSubagents |
false |
false skips sessions whose header meta marks subagent origin. |
Invalid configuration fails plugin load naming the offending entry: empty text, duplicate section names, or a non-finite order. Per-entry section problems (blank/duplicate name, bad order, empty text) and per-pair history problems (empty text, embedded reserved tags) degrade to skipping that entry with a warning instead of failing the plugin tree.
Injection mechanism
The reference exchanges are built once at plugin activation into alternating real Message objects (deep-frozen, shared by reference across requests), then prepended onto every ordinary conversation request by an llm/stream waterfall listener:
- Clone and redispatch: loop-built requests are deep-frozen and marker-tagged (
markAgentLoopRequest(deepFreeze(...))) and never mutated; the listener clones the request, prepends the seed messages, and redispatches throughctx.llm.stream. The clone carries no loop marker, the agent-loop log-reconstruction invariant does not apply to it, and the discarded original is a purederiveMessages()projection. - Zero log writes: seed messages live only on the request path — real turn numbering starts at 1 with no collisions, forks are ordinary copies, and compaction cannot shadow the reference history (every request re-injects it).
- Scope filtering: auxiliary calls (
purpose-stamped, e.g. session-title, compaction) and hand-built requests (nosessionId) pass straight through; subagent-origin sessions are skipped by default (includeSubagents: trueopts in). - Panel verification: not seeing the seed messages in the chat transcript is expected; use the panel's LLM listener to inspect the injected real request (Settings → "Custom first control prompt" → LLM listening, or the dock strip above the composer).
Verifying the injection works: in a fresh session ask a question only the injected history can answer (e.g. "repeat our earliest user message") — the model quoting the configured content proves it. session.history shows no seed messages (a clean log is a feature, not a failure).
Panel save semantics: the panel's config editor writes an id-targeted override into the profile cordis.patch.yml (never an insert, so it cannot collide with the bundle layer's row), updating only this plugin's core row (custom-first-control-prompt) and preserving everything else — other entries, comments, and legacy rows. While the profile patch carries no row, the editor shows the composed config (bundle-layer defaults); saving then creates the override.
System sections
Each enabled entry registers via ctx.systemPrompt.section() at plugin load, so it participates in every assembly exactly like the factory sections: variable interpolation, scope shadowing, and the assembly waterfall all apply. Static configured text renders identically in every assembly, which is what keeps the request prefix reusable.
Model experience
Deployment system sections
What the model sees
Configured section text renders at its configured order position — by default ahead of the persona — alongside the factory sections from dsh-system-prompt.
Token impact
Every section repeats on every request; cost scales with rendered length.
KV-cache impact
Prefixes stay stable while section text, order, and the enabled set render identically. Any change can break reuse from the first changed system-prompt token.
Reference conversation history
What the model sees
Real alternating messages at the head of every ordinary conversation request — one user message per configured user text, one assistant message per configured assistant text:
[user] configured user text 1
[assistant] configured assistant text 1
[user] configured user text 2
[assistant] configured assistant text 2
[user] the real prompt…
Token impact
A fixed single copy of the reference history per request (it never accumulates across turns, and compaction changes nothing — every request re-prepends the same frozen message sequence).
KV-cache impact
The reference history leads the message sequence byte-stably, keeping request prefixes reusable.
Known limitations and deferred work
- The chat UI never shows the reference history — the seeds live only on the request path; neither alternating messages nor framework rows appear in the conversation UI. Reconstructing model-visible content requires the session log plus the deployment config (the framework exposes no plugin event-type registry and
Session.appendcannot carry anignorableenvelope) — a deliberate, declared deviation from the harness log-reconstruction default. - Seed text is model-visible reference material — treat it as prompt text the model reads, not a trusted channel.
- No mid-session edits — configuration changes take effect for new requests after a web restart; a compliant "edit while quiescent" surface-replacement event carrying a source seq reference is deferred.
FAQ: how do the plugin and panel rows enter the composition?
The core package declares dsh.bundle (in-package cordis.patch.yml), and the
reconciliation inside dsh plugin add activates that bundle layer — both the
core row custom-first-control-prompt (server logic: system sections,
reference-history injection) and the panel row ui-custom-first-control-prompt
(browser UI: settings page, dock, LLM listener) appear together, with no
hand-written patch rows. dsh plugin remove drops the dependencies and the
bundle layer together.
- Customizing: never copy a
- insert:row. Write an id-targeted (non-insert) patch in the profilecordis.patch.ymlto override the bundle row's config (last write wins) — sample incordis.patch.yml.template; the panel's config editor saves exactly this form. - Offline junction installs skip reconciliation, so the bundle layer never
activates —
install.ps1 -Offlinewrites the same two rows into the profile patch instead. - Duplicate-id warning: once the bundle layer carries the rows, any leftover
- insert:row with the same id in the profile patch (from a manual-era install) = root-list duplicate → the web fails to boot;uninstall.ps1strips both shapes surgically.
amruthpillai/reactive-resume
tt-a1i/archify
zhu1090093659/dsh-web-ui
strukto-ai/mirage
liustack/modlens
omdsh-dev/DSH-better-sidebar
ccch1mneyyy/dsh-TUI