DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

sunlei1214 /

sunlei1214/dsh-session-requirement

Verified

Per-conversation requirement / constraint box for DeepSeek Harness - title-row field, @-skill picker, injected into every message of that conversation.

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@48e0cb21

dsh-session-requirement

Per-conversation session requirement / constraint box for DeepSeek Harness. It lives in the conversation title row and lets you write one standing requirement for this conversation, plus pick skills with @, and have all of it attached to every message you send.

The box is off until you switch it on. The switch lives in Settings → General → 本对话的整体要求; flip it and the box appears in the title row immediately — no reload, no restart. Flip it back and the box is gone, and nothing is attached to your messages either.

Collapsed, it is one line at the right end of the title row:

… 标准模式                              ┌────────────────────────────────────────┐
                                        │ 输入本对话的限制、约束或整体要求(可选)(●)│
                                        └────────────────────────────────────────┘

Focused, it lifts out of the row as a floating card — the header never grows:

                                        ┌──────────────────────────────────────┐
                                        │ @office-docx ×  @office-xlsx ×       │
                                        │ 只输出中文,表格必须带单位              │
                                        │ …                                    │
                                        │  ┌────────────────────────────────┐  │
                                        │  │ @office-docx   Word documents  │  │
                                        │  │ @office-xlsx   Excel workbooks │  │
                                        │  └────────────────────────────────┘  │
                                        └──────────────────────────────────────┘

Behaviour

| Rule | Detail | | --- | --- || GUI switch | one toggle in Settings → General, stored globally in the state file; off on a fresh install | | Show/hide | off renders nothing at all — no empty gap and no slot in the title row; on mounts the field in every conversation at once | | Placement | conversation.session.header.utilities, order: 0 — the right-aligned utility area of the Session title row (the 创建/标准模式 line), left of the corner controls | | Size | a fixed 380px × 28px slot, so the title row never reflows. On focus the field lifts out of flow as a 470px floating card (position: absolute) over the tab row, because the header must not grow | | Blank conversations | the shipped header hides its whole title-row chrome while a conversation is still blank, so the same field also registers in conversation.input.dock (order: -100) and shows there until the title row appears. A module-level seat registry keeps exactly one copy on screen; the dock copy is the full-width, in-flow variant | | Collapsed | one line; expands to four lines on focus; collapses on blur without losing text | | Scope | text, chips, and the per-conversation switch are stored per conversation; a new conversation starts empty and off | | Effective | requirementText.trim().length > 0 \|\| skillIds.length > 0 | | Switch | grey while there is no content and clicking it focuses the input; turns on by itself on the first effective content; turning it off keeps text and skills but stops attaching them; clearing everything greys it again | | Attach | only when the GUI switch is on, the per-conversation switch is on, and the content is effective | | @ picker | opens on @ at line start or after whitespace; filters by name, description and whenToUse; ↑/↓ move, Enter picks, Esc closes, click picks; empty result and load failure show an inline message instead of breaking the input | | Chips | a picked skill becomes a non-editable @name × chip; × removes it; Backspace in an empty input removes the last one; duplicates are ignored; the cap is maxSkills (default 10) | | Skills source | the live harness skill registry through ctx.remote.skills.list — nothing is hardcoded. Subagent sessions have no catalog and say so instead of showing a fake list | | Skills execution | none. This plugin only selects, stores and forwards skills; the model receives the skill body as context and decides what to do |

How attachment works

conversation.send() on the client is a pure pass-through — there is no outgoing-prompt transform to hook. The durable, model-visible seam is the host's agent/pre-step; when the claimed message batch contains a real user message (a fresh send, not a tool-loop follow-up) this row appends one extra user/message:

<session_requirement>
The user set a standing requirement for this whole conversation. Apply it to every reply until the user changes it.
<requirement>…</requirement>
<selected_skills>office-docx, office-xlsx</selected_skills>
</session_requirement>

<skill_content name="office-docx">…</skill_content>

It is committed through the harness's own surfaceOp: "append" path with source.kind = "session-requirement", so it lands in the transcript like the built-in /skill invocation injection — it never impersonates a user gesture.

HTTP surface

Route Purpose
GET /api/session-requirement?sessionId=… read one conversation's state (also reports visible)
GET /api/session-requirement?mode=settings read the GUI switch alone, with no conversation
POST /api/session-requirement write one conversation's state: { sessionId, requirementText, requirementEnabled, skillIds }
POST /api/session-requirement { visible } flip the GUI switch alone

State lives in <DSH_HOME>/session-requirement.json, written atomically (.tmp + rename):

{
  "version": 1,
  "settings": { "visible": false },
  "sessions": {
    "session-…": { "conversationId": "session-…", "requirementText": "", "requirementEnabled": false, "skillIds": [], "updatedAt": 0 }
  }
}

Requests are rejected with 400 rather than silently corrected when sessionId is malformed, the text exceeds maxTextLength, or skillIds is not a deduplicated list of at most maxSkills skill names.

Command

/requirement prints the current conversation's state; /requirement clear clears and disables it.

Requirements

  • DeepSeek Harness 0.2.x (desktop or web), Cordis v4.
  • No npm dependencies. The host half imports only Node built-ins; the browser half is a hand-written CJS bundle that takes react from the harness loader.

Install

git clone https://github.com/sunlei1214/dsh-session-requirement.git

Point your profile at the checkout:

// <DSH_HOME>/profiles/<profile>/package.json
"dependencies": { "dsh-session-requirement": "link:C:/path/to/dsh-session-requirement" },
"dsh": { "profile": { "bundles": [ …, "dsh-session-requirement" ] } }

Then reinstall the profile and restart DeepSeek Harness — the browser half ships as a static client bundle, so a page refresh alone is not enough.

Use link:, not file:. pnpm copies a file: directory dependency and caches it, so later edits never reach the profile. link: creates a junction (Windows) or symlink, so the profile always reads the current source.

Uninstall

Remove the dependency and the bundle entry, reinstall, restart. The host row drops its route, its agent/pre-step listener, and flushes pending state on dispose; the browser half removes its <style data-plugin="dsh-session-requirement"> node, its document listeners, its debounce timers, its caches, and aborts in-flight requests. Deleting <DSH_HOME>/session-requirement.json is optional.

Configuration

Option Default Meaning
maxSkills 10 maximum chips per conversation
maxTextLength 8000 maximum requirement length

Tests

node test/host-check.mjs
node test/client-check.mjs

(npm test runs both; if the PowerShell execution policy blocks npm.ps1, run the two files directly.)

Two offline harnesses run against the real source files, with no harness install required:

  • test/host-check.mjs — a fake context, web server and skill registry. Covers both routes, the GUI switch (default off, flip, persist, and that a hidden row never attaches), every validation refusal, the atomic JSON sidecar, the pre-step injection (fresh send vs. tool-loop follow-up, disabled, empty, text-only, unknown and non-user-invocable skills, aborted signal), the /requirement command, and route removal on dispose.
  • test/client-check.mjs — a minimal React hook runtime plus a fake DOM, driving the actual components: the GUI switch gating the row (absent while off, appearing live when flipped), collapsed/expanded rows, the grey toggle focusing instead of enabling, auto-enable on first content, manual off/on, chips, the @ picker (filtering, arrows, Enter, Esc, click, empty state, load failure), dedupe, the cap, Backspace, per-conversation isolation, a late read not clobbering typed text, and a teardown that must leave no style node, listener, or pending write.

test/verify-live.mjs checks a running app after installing and restarting:

node test/verify-live.mjs           # read-only
node test/verify-live.mjs --write   # round-trip a write, then restore

It reports the state route, the GUI switch route, the boot manifest entry, and the validation refusals. A 401 means the host half did not load — restart the app.

License

MIT © 2026 sunlei1214

Not affiliated with DeepSeek. "DeepSeek Harness" and "DSH" refer to the host application this plugin extends.

—/ 5

No ratings yet

Verified DSH bundle

Commit 48e0cb212356

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout