DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

Zylog82568 /

Zylog82568/dsh-persona-slot

Verified

Per-session persona slot for DeepSeek Harness: one persona per session, a global persona every session inherits, a /persona command and an Installed-page panel.

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

dsh-persona-slot

Per-session persona slot for DeepSeek Harness. Every session holds exactly one persona — writing a new one replaces the old instead of layering — and one global persona covers every session that has no persona of its own. Persona length is unbounded. Edit either from the conversation with /persona, or from the panel under Plugins → Installed.

中文说明

Why this exists

@deepseek-ai/dsh-persona gives one persona to a whole preset. That is the right tool when every session of a preset should be the same character. It cannot answer the other two questions:

  • This session should be a cat, and that one should be a lawyer — at the same time, in the same window.
  • The persona should be changeable while the conversation is already running.

This plugin answers both. It resolves a persona per session from three tiers and applies it through one agent-scoped prompt hook, so two sessions never see each other's choice and a change takes effect on the next model step.

One persona per session

That is a guarantee, not a convention:

  • A session's store record is single-valued; set replaces, so a third write overwrites the second and never concatenates.
  • Resolution returns at the first tier that holds a record, so an exact-session persona and the global persona never both reach one prompt.
  • one persona per session and a session never resolves two personas at once pin both rules in the unit suite.

Resolution order

The first tier that holds a record wins.

Order Tier Where it lives
1 exact session id config.sessions["<id>"] or the runtime store
2 wildcard "*" config.sessions["*"] or the runtime store
3 global config.global or the runtime store

A record is either text — used as the persona — or an explicit blank, which mutes that tier. With blankOverrides: true (the default) a blank stops the lookup, so a session can silence itself without deleting the deployment default. With blankOverrides: false a blank falls through to the next tier.

Install

dsh plugin --profile <profile> add dsh-persona-slot

Local development:

dsh plugin --profile <profile> add link:/absolute/path/to/dsh-persona-slot

The bundle patch mounts one row, persona-slot.

Configure

- id: persona-slot
  config:
    # Persona every session inherits.
    global: "You are a terse systems engineer. Answer in short commands."
    # Per-session personas; "*" is the wildcard tier.
    sessions:
      "<session-id>": "You are a cat. Every sentence ends with 喵."
      "*": "Answer in Simplified Chinese."
    enabled: true
    # append: add the persona after the deployment persona.
    # replace: take the deployment persona's place.
    mode: append
    # Optional wrapper around the persona text.
    prefix: ""
    suffix: ""
    blankOverrides: true
    includeSubagents: true
Field Default Meaning
global "" Persona every session inherits
sessions {} Per-session personas; "*" is the wildcard tier
enabled true Master switch; false injects nothing and keeps stored text
mode append append adds a section, replace rewrites the deployment persona
prefix "" Text written above the persona
suffix "" Text written below the persona
blankOverrides true Whether a blank record mutes its tier instead of falling through
includeSubagents true Whether subagent sessions also resolve a persona

mode can also be changed at runtime with /persona mode.

The Installed-page panel

In the Web GUI open Plugins → Installed and find dsh-persona-slot. Its persona-slot row is now expandable: the arrow opens this plugin's own page, which owns:

  • Global persona (总人设) — a multi-line editor; saving it applies to every session without its own persona from the next model step.
  • Wildcard persona — for sessions that have no persona of their own.
  • Placement — append after the deployment persona, replace it, or follow profile config.
  • Live sessions — each live session's resolved persona, where it came from, and a Clear control for sessions holding their own.

The panel refreshes every ten seconds and never overwrites text you have typed but not saved.

It talks to the plugin's own /persona-slot/state and /persona-slot/update routes instead of the settings form, because that form's value field is single-line and cannot hold a character sheet. A panel save and a /persona set in a session write the same document, so the two never disagree.

The /persona command

/persona                             show this session's resolved persona
/persona show [this|global|all]      show one target
/persona set <text>                  set this session's persona
/persona set global <text>           set the persona every session inherits
/persona set all <text>              set the wildcard persona
/persona set for <id> <text>         set one named session's persona
/persona clear [target]              delete the record so the target falls through
/persona mute [target]               record a blank that stops the fallback
/persona list                        list runtime overrides
/persona mode append|replace|config  choose how the persona joins the prompt
/persona reset                       drop every runtime override

Text is taken verbatim, multi-line included. A session id is only recognised after for, so /persona set 你是猫娘 sets the persona to "你是猫娘" instead of treating it as a target.

Where the runtime state lives

$DSH_HOME/persona-slot/personas.json

Profile configuration is a deployment default; this file is what the user typed. Keeping them apart means a plugin upgrade, a profile rebuild, or a reinstall never erases a persona written in a session. A malformed file is moved aside as personas.json.corrupt and the plugin starts empty instead of failing.

How it hooks in

Each live agent gets one system-prompt/assemble listener registered through agent.ctx. That registration is scope-filtered to that agent and disposed with it, so:

  • two sessions assemble two different prompts, and neither can read the other's persona;
  • the listener resolves the store at every model step, so /persona set applies on the next step with no re-registration and no restart;
  • unloading the plugin or disposing an agent removes exactly its own hook.

In append mode the persona becomes a persona-slot:session section placed right after the deployment persona. In replace mode the deployment persona section itself is rewritten. Persona text is marked interpolate: false, so a literal {{anything}} in a persona survives prompt assembly instead of being treated as an unresolved variable.

Model Experience

What the model sees

One additional prompt section (append) or a rewritten deployment persona (replace) holding the operator's persona text. The section is stable across steps until the persona changes, so provider prompt caching keeps working between edits.

Token effect

The persona text plus the section separator (a blank line). Nothing else is added.

KV Cache effect

An unchanged persona keeps the prefix stable. A /persona set changes the prefix, which invalidates the cache from that section onward; that is the intended cost of changing an identity mid-conversation.

Known limitations

  • Not a security boundary. Personas are prompt text the operator writes. They do not restrict tools, sandbox mode, or approval policy — use those for enforcement.
  • The panel needs an HTTP surface. The routes are registered on ctx.webServer, so a headless or TUI composition has only the /persona command.
  • No persona-length ceiling. A very long persona is sent on every request and consumes context; that cost is the operator's to budget.
  • Session ids, not titles. Overrides key on the session id. A resumed or forked session keeps the id, so its persona follows it; a title change does not move a persona.
  • One persona per session. Two personas cannot be layered on one session; combine them into one text.
  • replace needs a deployment persona slot. It rewrites deployment:persona-prefix. If a composition mounted dsh-persona inside a preset without that section, replace still inserts the section at the front of the assembled prompt, so the session is never left without one.

Supported DSH versions

0.2.1-rc.2 through 0.2.1-rc.5, declared as >=0.2.0-rc.2 <0.2.1-0 || >=0.2.1-rc.1 <=0.2.1-rc.5.

The range is written out in explicit prerelease branches on purpose. node-semver only lets a prerelease version satisfy a range when some comparator in that range sits on the same major.minor.patch tuple and carries a prerelease tag of its own, so a broad-looking >=0.2.0-rc.2 <0.3.0-0 silently excludes every 0.2.1-* build — including the ones this plugin is tested on. The explicit branches keep 0.2.1-rc.1 … rc.5 inside and leave 0.2.1 itself, the alpha builds, and 0.3.0-* outside.

Developed and tested against 0.2.1-rc.2; the release builds are the same host packages with patch-level fixes, and the plugin only uses the stable seams (agents, commands, systemPrompt, webServer).

Development

npm test          # unit tests + real-runtime integration test
npm run check     # syntax check every module

The integration test boots a real Cordis composition (dsh-system-prompt, dsh-agent, dsh-agent-loop, dsh-commands, dsh-session, dsh-tools) and asserts that two sessions assemble different prompts. Set DSH_HOST_ROOT to the @deepseek-ai host package directory to run it; the suite skips rather than fails when that runtime is absent.

—/ 5

No ratings yet

Verified DSH bundle

Commit bdef802edceb

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