DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Persona Cards — DSH Plugin for DeepSeek Harness
← Plugins
P

dsh-persona-cards

Persona Cards

Persona cards: Write multiple personas in settings, enable them individually, and inject them directly into the system prompt; also includes a global setting that applies to all conversations and is always injected.

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:lyufeng000/dsh-persona-cards#79b5476ddba33e277bcf0f5c1b84b86624dfedd1
READMECompatibilityVersions

Compatibility and provenance

Persona Cards is published as dsh-persona-cards and currently resolves to version 0.2.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
10/16/2026

Versions

0.2.0
stable
10/16/2026

Related plugins

Loading related plugins…

Latest
0.2.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
GitHub
★ 0
Weekly downloads
0
Last push
10/16/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

Related plugins

More verified plugins in memory-context.

Hindsight Coding Agents@vectorize-io/hindsight-coding-agentsReflect-only Hindsight long-term memory for coding agents (harness-pluggable: opencode, …), with automatic background ingestion (no setup CLI).Ab Memorydsh-ab-memory--- description: "The dsh-ab-memory package: a cross-session, file-backed agent memory with five tools (remember / recall / forget / load_skill / distill) and an always-on light injection." kind: "package-reference" ---Memsearch Dsh@zilliz/memsearch-dshMemSearch plugin for DeepSeek Harness: shared markdown memory across agents, with capture, pre-step context injection, memory-recall skill, and a skill-candidate review panel.Archive Manager@michengai/dsh-archive-manager归档、恢复、收藏和永久删除会话,并诊断损坏的会话日志。 · Archive, restore, favorite, and permanently delete sessions, and diagnose damaged session logs.

README

dsh-persona-cards —— 人设卡

在 设置 → 人设卡 里管理人设,在每个会话里选用哪一套。选中的那张会拼进 系统提示词,排在官方部署人设之后、所有工具说明之前。改完下一个模型步骤就生效,不用重启。

默认不注入任何人设 —— 要用哪张,得在会话里显式选。

另有一段全局设置:不挑会话、不挑人设,写了就永远注入(排在人设之前)。 见下面「全局设置」一节。


功能

  • 设置里的独立一页:左侧设置导航新增「人设卡」,与「通用」「模型」「插件」并列。
  • 全局设置(与人设卡并列的一块):一段对所有会话都成立的附加正文, 不管会话有没有选人设都会注入,且排在人设之前;留空即不注入。见下面专节。
  • 多张卡:新建 / 编辑 / 删除,每张卡有名字、正文、启用开关。
  • 卡片态 / 编辑态分离:列表默认是只读卡片(标题 + 正文预览 + 开关), 点「编辑」才展开输入框,点「完成」回到卡片态、点「取消」直接丢弃草稿。
  • 会话级选择:会话输入框左侧有个「人设」选择器。选中只作用于当前会话, 别的会话不受影响;不选就是「不使用人设」。
  • 自动保存:编辑完成时立即落盘;开关 / 删除 / 新建立即落盘;全局设置为 700ms 防抖落盘。
  • 零污染:没选人设、全局设置也留空时,插件对那个会话的提示词完全不存在 (空段落会被 DSH 自动丢弃)。

全局设置:唯一一段"无条件的"注入

项值
在哪写设置 → 人设卡 → 页面顶部「全局设置」框
何时注入永远 —— 不挑会话、不挑人设、不挑启用状态
注入位置官方部署人设(0) 之后、选中的人设卡(100) 之前 → order = 90
段落名persona-cards:global
存哪settings.json(与人设卡 cards.json 分开)
空值语义全空白 = 没写 = 整段被 DSH 丢弃,插件对这个会话不产生任何提示词

排序上刻意取 90:它是官方 SECTION_ORDERS 里 DEPLOYMENT_PERSONA_PREFIX(0) 与 本插件人设段 (100) 之间的空位,不会和任何官方段撞号。语义是「全局总纲 → 具体身份」: 对所有会话一致的要求先立,会话选的人设后置覆盖。

为什么单独一段,而不是拼进人设段:两段的生效条件完全不同(人设段"没选就空"、 全局段"写了就在")。合成一段就得在渲染时拼字符串,还要额外处理"只有全局没有人设" 的边界;分开之后 DSH 的 renderPrompt() 空段丢弃规则天然把两种情况都处理干净。

为什么单独一个文件:卡片与全局设置的自动保存是各自独立触发的,共用一个文件 就意味着每次落盘都写全量,一边的写入会把另一边刚改的内容按旧值覆盖回去。 分开之后两个文件各自「临时文件 + rename」,互不干扰。

{{model}} / {{cwd}} / {{provider}} 的引用规则与人设完全一致:落盘前过同一道 白名单闸门(normalizeBody()),其余引用一律拒绝保存并说明原因。

语义:设置页管仓库,会话管这次用哪张

层存哪作用
卡片cards.json设置页维护
启用开关同上只决定这张卡进不进备选池,不替任何会话做决定
会话 → 卡片 绑定session-cards.json会话界面的选择器写入
全局设置settings.json设置页维护,无条件注入,与会话/人设无关
注入什么——全局设置(若有)+「绑了就用绑的那张;没绑就什么都不注入」

这个设计解决了一个实际问题:把几张卡全启用,新会话不会把几个人格叠加成 互相打架的身份指令 —— 因为「启用」不等于「生效」。

会话 id 来自段落 text 函数收到的组装上下文 —— @deepseek-ai/dsh-agent 的 assembleContextFor() 返回的是 { agent, scope: agent, signal? }, 所以 context.agent.id 就是当前会话。拿不到 agent(或绑的卡被停用/清空/删除)时 一律按「不注入」处理,不会抛错、也不会误注入别的卡。

⚠️ 为什么选择器不在「新建会话」界面

试过了,DSH 的插槽体系里没有干净的位置:

  1. 新建会话的空状态(hero)里,所有会话级控件都被官方显式跳过 —— dsh-client-ui-conversation 里写死了 input === undefined || sessionId === undefined ? null : renderSlot("conversation.input.left", {})。 新建会话时还没有 sessionId,所以这些槽一律不渲染。
  2. hero 区唯一能挂东西的是 conversation.hero.agentPreset,但它是 single 槽且已被官方占用 (replaceRisk: shadows-shipped-ui)—— 用它就得顶掉官方的 agent 预设选择器。

所以选择器放在 composer 工具行左侧(conversation.input.left,list 槽、空着、replaceRisk: none)。 效果是:发出第一条消息后选择器出现,之后随时可切;新会话的第一条消息用设置里的默认规则。

安装

包已链接进 desktop profile。手工装的三步(本机开发用):

  1. 包目录 junction 到 <profiles>/node_modules/dsh-persona-cards
  2. <profile>/package.json 的 dependencies 加 "dsh-persona-cards": "0.2.0"
  3. <profile>/cordis.patch.yml 追加 cordis.patch.yml 里的 insert 块

官方装法:dsh plugin --profile <p> add dsh-persona-cards

⚠️ 改完 Host 代码必须完全重启 DSH

DSH 的 Loader 复用 ESM 模块缓存:禁用/启用插件、改配置文件、新建会话都不会 重新 import lib/index.js。

两半的更新方式不一样,会错配:

半边什么时候重新读盘
lib/client.jsdsh-client-modules 对这个文件挂了 watch —— 改完刷新页面就是新的
lib/index.js只在 DSH 进程启动时加载一次

所以「刷新页面」之后很容易出现界面是新的、Host 是旧的:新界面按新协议调 saveGlobal,旧 Host 的 switch 里没有这个 case,回一句 未知接口:saveGlobal,界面上就是「保存失败:未知接口:saveGlobal」。

现在这种错配会被显式认出来(快照里没有 global 字段 = Host 是旧版本), 设置页直接给出警告并禁用输入框,不会再让人对着"能打字却存不下去"猜。 修法只有一个:完全退出 DSH 再重新打开。

数据

项位置
卡片文件$DSH_HOME/persona-cards/cards.json
全局设置$DSH_HOME/persona-cards/settings.json
会话绑定$DSH_HOME/persona-cards/session-cards.json
诊断输出$DSH_HOME/persona-cards/diag.json(排查注册问题用,可删)

$DSH_HOME 解析顺序:环境变量 DSH_HOME → ~/.dsh。

默认即 %USERPROFILE%\.dsh\persona-cards\cards.json。同目录下已有的 .md 人设文件不会被读写或修改。

注入位置

两个提示词段落,order 取自 @deepseek-ai/dsh-system-prompt 的 SECTION_ORDERS:

HARNESS_IDENTITY(-1000) → DEPLOYMENT_PERSONA_PREFIX(0) → ★persona-cards:global(90)
  → ★persona-cards:user(100) → PLAN_POLICY(500) → TEAM_POLICY(600) → 各工具说明(1000+)
段落名order内容
persona-cards:global90全局设置,非空就注入,与上下文无关
persona-cards:user100当前会话选中的那张人设卡,没选就是空串(整段被丢弃)

DSH 0.2.0 兼容性(重要)

DSH 从 0.1.7-rc.2 升到 0.2.0-rc.1 后,cordis 收紧了 ctx 代理: 没声明 inject 的服务属性访问会直接抛错 —— cannot get property "X" without inject(cordis/lib/index.js 的 ReflectService.handler.get)。

connection.rpc.handle() 内部是 register(owner = this.ctx, …) → owner.webServer.register(route),而 this.ctx 解析成调用方 fiber 的 ctx (官方注释明写:"channel registrations belong to the caller fiber")。 本插件刻意不声明 inject(声明会把 apply 推迟,见 whenService 注释), 于是官方通道整条挂掉,表现就是设置页报:

读取失败:transport failure for /persona-cards/state: HTTP 405

(405 = 请求落到了 fallback,说明路由压根没注册。)

修法:官方通道优先,失败自动降级。

降级路径用 ctx.get('webServer') —— 官方给的服务可选访问形式,不受 inject 约束 —— 自己挂 {kind:'prefix'} 路由,信任校验复用 connection.admit(req): 它是实例方法,只读 trustedHosts / browserAuth / operator 三个实例属性, 完全不碰 ctx。

已在真实运行环境实测过(建了个独立探针包,新包名没有 ESM 缓存所以当场加载):

检查项结果
ctx.get('webServer')object
webServer.registerfunction
ctx.get('connection') / .admitobject / function
路由注册成功
未认证请求打到该路由401(不是 404/405),且确实进了处理器

401 是预期的:命令行的裸请求没有浏览器认证 cookie,被信任栅栏拦下; 页面内发起的同源请求会带 cookie,正常通过。

两个保命设计(别改)

  1. {{...}} 闸门。DSH 的 renderPrompt() 会对每个段落做变量插值,而 interpolate() 在三种情况下直接抛异常:引用了没注册的名字 (unknown prompt variable)、引用了已注册但本次组装没有值的变量、以及写法 不合法(malformed prompt variable reference)。用户在人设里顺手写个 {{随手写的}} 就能把整个会话搞崩。

    做法是开启插值 + 落盘前白名单校验:

    • 开启插值,让 DSH 官方注册的变量真正替换成当前值;
    • 保存时用 scanVariableReferences() 扫正文(顺序逐字复刻官方 interpolate(),包括「没有配对的单个 {{ 当普通文本放行」这条), 只放行 {{model}} / {{cwd}} / {{provider}},其余引用拒绝保存并说明原因。

    全库扫过 DSH 的 289 个包:官方一共只注册了这三个变量(都在 dsh-agent-loop, 取值形如 context.agent?.options.model),而且官方自己没有任何内置段落 引用它们 —— 这三个就是留给插件/组合层用的。

  2. 不能用 deployment:persona-prefix 这个段名。它归 @deepseek-ai/dsh-persona 所有,而官方注释明写那个插件是 scope-only 的: 全局挂载会与注册表自身的注册相撞并 fail loud。所以本插件用自己的段名。

实现要点

dsh-persona-cards/
├── package.json       # dsh.bundle.patch + dsh.client(平台 web)
├── cordis.patch.yml   # 挂载声明
└── lib/
    ├── index.js       # Host:持久化(卡片 / 绑定 / 全局设置)+ 两个提示词段落 + RPC 通道
    └── client.js      # Client:设置页 UI + 会话界面的人设选择器

Host ↔ Client 通信走 ctx.connection.rpc.handle(channel, handler)。 通道内部已经过 admit(req)(Host/Origin 信任栅栏 + 浏览器鉴权),被拒即 401/403 —— 所以本插件不需要自己写路由守卫,这也是没走 ctx.webServer.register() 野路子的原因。

客户端侧对应 ctx.connection.rpc.call(channel, endpoint, payload), 返回 {ok:true,value} / {ok:false,error},业务失败不抛异常,必须自己解包。

service 获取刻意不用依赖注入:apply 用 whenService() 轮询取 systemPrompt / connection。本机实测踩过一次 ctx.inject([...], cb) 静默挂起(fiber 显示 active,但回调始终没跑,RPC 通道就这么没了)。

dsh.client.inject 填的是包名,不是 cordis 服务名(package.json 里):

"client": {
  "platform": "web",
  "inject": [
    "@deepseek-ai/dsh-client-ui-settings",
    "@deepseek-ai/dsh-client-locale",
    "@deepseek-ai/dsh-client-connection"
  ]
}

它是客户端插件之间的加载顺序依赖,让提供方先于消费方加载;cordis 服务依赖 写在 client.js 的 exports.inject 里(['slots','locale','connection'])。 这里一开始写成了服务名,虽然被宽容处理也能跑,但不合规范 —— 对齐参照物是官方 @deepseek-ai/dsh-client-ui-settings-general(同样注册独立设置页,用的正是这三个包)。

这个声明只在客户端启动时解析一次:改完它必须重启客户端,改 client.js 都不够。

已知限制

  • 正文上限 100000 字符、单卡名 100 字符、最多 50 张卡;超限拒绝保存并给出原因, 不做静默截断。全局设置共用这个正文上限。
  • 卡片是全局的,不分会话/工作区;全局设置同样只有一段,不能按会话覆盖 (要按会话区分就用人设卡)。
  • 没有排序、导入导出、md 文件导入。