dsh-plugin-persona
No description
- Stars
- 0
- Language
- JavaScript
- Created
- Oct 16, 2026
- Updated
- Oct 16, 2026
Introduction
dsh-plugin-persona — DSH 人格管理器
给当前 DSH 客户端提供一套「模型人格(人设 / 人格提示词)」的完整管理:多套自定义人设、每个会话独立绑定、默认人格、随时切换(可选择立即生效或下个会话生效),并把同一套能力作为工具暴露给模型自己调用。
- Host 半(
lib/index.js):在会话自己的 agent scope 里覆盖deployment:persona-prefix/deployment:persona-suffix,注册 HTTP JSON 桥,注册 3 个模型工具。 - Web 客户端半(
lib/client.js):设置页「模型人格」+ 输入框左侧的人设切换芯片。
功能
设置页(设置 → 模型人格,settings.section#persona-manager,order 25)
- 人设库:新建 / 编辑(名称、emoji、描述、前缀、后缀)/ 删除,
complete(独占整个 system prompt)与suppressRuntimeContext(抑制运行时上下文)两个开关。 - 设为默认 / 取消默认(默认人格只决定「没有绑定的会话」用什么)。
- 导入 / 导出 JSON;显示存储路径;显示模型工具注册状态(Host 半如果注册失败,这里会直接给出错误文本)。
输入框芯片(conversation.input.left#persona-manager.switcher,order 120)
- 一眼看到当前会话生效的人格(emoji + 名字,
跟随默认/临时文本也会显示出来)。 - 下拉即可切换:跟随默认、任选人设、
临时文本…(只作用于本会话的一次性人格)、仅对下一个会话生效(只记账、不重注入,因此不动当前 KV 前缀)、立即重注入。
模型工具(Host 半注册,模型可自行调用)
persona_list— 列出所有人设,并说明本会话当前生效的是哪个。persona_switch— 切换本会话人格;参数id/custom(临时文本)/deferred(true = 下次会话生效)。persona_create— 新建并保存一个可复用的人设。
HTTP JSON 桥(客户端半使用,也可以脚本直接调)
GET /dsh-persona/api/state?sessionId=<id>→ 完整视图(人设库、默认、本会话绑定、生效结果、active、tools注册状态、storePath)。POST /dsh-persona/api/call,body{"method": "...", "params": {...}},方法:statecreateupdateremovesetDefaultswitchactivateimportexport。失败返回 4xx +{"ok":false,"error":"..."}。
安装
推荐走 DSH 插件管理(需要 danger-full-access 或逐次批准):
plugin_manager action: install_bundle target: <本目录的绝对路径>
它会 pnpm link 进 profile 并把包名写进 profile package.json 的 dsh.profile.bundles,同时激活 cordis.patch.yml 里的 Loader 行 persona-manager。
也可以手动安装:把本目录放进(或链接到)profile 的 node_modules/dsh-plugin-persona,再在 dsh.profile.bundles 里加入 "dsh-plugin-persona"。
生效时机与 KV 缓存(重要)
- 只绑定 / 立即生效:
switch默认只写绑定(下个会话或下个 agent 才生效);activate或界面上的「立即重注入」才会改写当前会话正在用的提示词。 - DSH 的 system prompt 走
systemPromptUpdate: 'in-history':提示词变化会追加在已缓存历史之后,所以「立即生效」不会破坏已有前缀的缓存复用;代价是旧人格文本会留在历史里,直到 compaction 把它压掉。 - 重新加载:客户端半(
lib/client.js)的改动由 client-hmr 轮询自动热更新,刷新页面即可看到;Host 半(lib/index.js等)的改动必须重启 DSH 进程(Node ESM 缓存,加载器不带 cache-busting 查询串)。
数据
- 默认
$DSH_HOME/persona/personas.json,可用环境变量DSH_PERSONA_STORE覆盖。 - 结构:
{version: 1, defaultId: string|null, personas: Persona[], bindings: {[sessionId]: {personaId?: string, custom?: string}}}。 - 上限:200 条人设、名称 64 字符、单段文本 64 KiB、请求体 1 MiB。
- 写入是原子的(临时文件 + fsync + rename),读取永不抛(坏文件/缺失都当空库处理)。
- 首次运行会种入 3 个内置人设:精简架构师 / 严格评审员 / 耐心的讲解者。
实现要点(为什么这样做)
- DSH 的
systemPrompt没有「改文本」接口,只有section()/context()注册,而且注册是 per-context 的分层覆盖(ScopedLayers)。因此插件把人格注册进该会话自己的agent.ctx,用它覆盖 preset 带来的人格 section;同一 scope 内重复注册同名 section 会抛错,所以每次切换都先 dispose 旧的再注册新的。 - 不能全局注册 persona section:会与 prompt registry 自带的全局 section 冲突并直接报错。
agent id === session id,所以会话绑定直接用 sessionId 当键;新会话在agent/created时套用(绑定 → 默认 → 什么都不做,跟随 preset),插件被重新加载时也会给已存在的会话补注入一次。- 不用自定义 session 事件(
SessionEventMap是仓库自有的,未知类型在读取时会被拒),也不依赖存储服务,插件自带原子 JSON 存储。 - 客户端半是手写的 lazy-CJS bundle(
window.__ModuleLoader__.load),只require平台 seed 表里的react/react/jsx-runtime;bundle 模型里没有ctx.styles之类的服务,CSS 用原始<style>元素在 factory 阶段插入(由 client-modules 的claimStyles认领,HMR 时能正确回收)。 - 模型工具不 import
@deepseek-ai/dsh-tools(第三方插件解析不到 app.asar 里的包,会静默失败),改为手写等价对象:标准 JSON Schema 的parameters+output: {schema, render}。
开发与测试
node test/check-persona.js # 语法 + store/runtime 语义(原子写、resolvePersona 优先级、覆盖注册、释放、后缀 order 10200)
node test/verify-host.js # 驱动真实 HTTP 路由 + 三个模型工具(含 deferred 不改写当前提示词的回归断言)
node test/verify-client.js # 在假 window/document 里加载 bundle,真的渲染并点按钮,断言发出 switch 调用
npm test # 三个一起跑
三个 harness 都是纯 Node、无子进程(当前沙箱禁止管道),失败会打印 FAIL … 并以非 0 退出。
目录
lib/index.js Host 半:HTTP 路由 + 3 个模型工具 + agent 生命周期 + 视图
lib/store.js 原子 JSON 存储、规范化与限额
lib/runtime.js 在 agent scope 内注入/释放 persona section
lib/client.js Web 客户端半:设置页 + 输入框芯片(手写 bundle)
cordis.patch.yml 注入 Loader 行 persona-manager
test/ check-persona.js / verify-host.js / verify-client.js