🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI
广告☁️ 云服务器特惠阿里云首购 8 折 · 腾讯云合作特惠
DeepSeek Harness Hub
← 返回列表

Han-Yao94/dsh-session-toolkit

DeepSeek Harnessspec-screened扫描:中风险在 GitHub 查看 ↗
✓ 可直接安装

会话身份、会话自动上线、会话日志按钮、会话间通信 + 全局提示词/重启服务这类工作台工具

自动检查通过:npm 包已发布且 engines 声明满足基线;该结论来自程序自动检查,未经人工实机验证。 · 最近上游提交 2026/9/24 · 已提供中文文档
综合分
32.2
GitHub 分
32.2
用户评分
—
★ Stars
3
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add dsh-session-toolkit
npm 包 dsh-session-toolkit 已校验归属本仓库,走 npm 安装最省事
信任档位:已验证本站已于 3 天前真实安装成功(L4 · 真实安装)
是什么
dsh 原生插件 · tool
装得上吗
本站已真实安装成功(L4 · 真实安装,非静态推断)
安全吗
本站尚未对该插件做风险分级(暂未覆盖,不等同于无风险)
还在维护吗
活跃:最近一次提交在 1 天前

档位由下列信号合成:本站实装验证(真实安装,当前最高到 L4)· 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。下方各区块是它的证据明细。 验证判据与等级说明 →

🟢实装验证通过· 2026/9/22
由本站实装验证器在真实 dsh 环境安装成功,非静态推断。
数据截至 2026/9/25(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查✓ 自动检查通过

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-session-toolkit @ 0.1.8
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/20 11:28:51

依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-home-paths@deepseek-ai/dsh-tools@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/dsh-client-locale@deepseek-ai/dsh-client-store@deepseek-ai/dsh-client-ui-primitives
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

由 DeepSeek 最新模型翻译生成
dsh-session-toolkit

DeepSeek Harness 的整合插件工具箱。将先前 6 个独立的本地插件——会话身份、全局提示词、会话自动恢复、Web 重启服务、Session log 按钮平移、会话间消息——合并为单个可安装包(官方 bundle 形态,dsh.bundle.patch),通过 dsh plugin add 安装;另含提示词去重(Prompt Dedup)功能。

当前版本:0.1.12,已对照 DeepSeek Harness dsh-v0.1.7-alpha.1 验证——它同时是最低支持版本——并在 dsh-v0.1.7-rc.1 上重新验证过(契约面 + 全套门,非完整功能回归):0.1.6 及更早会响亮失败而非静默降级(见兼容性)。

功能

会话身份(Session Identity)
每会话人设提示词注入该会话系统提示词(独立段 session-identity,order 40,每次组装按 agent 求值),支持默认身份与每会话覆盖。UI:身份浮层(启用开关、4000 字符软上限、保存/重置、编辑默认身份、继承默认身份)及双入口状态按钮:conversation.session.header.actions(id session-identity,order 40)与 conversation.input.left(id session-identity-input,order 40)。浮层卡片可按标题行拖动:位移每次移动都被钳制在视口内、窗口缩放时重新钳制;卡片比视口大时每个轴都仍可移动,四个边都能拖到。位置不持久化,浮层关闭即复位。

全局提示词(Global Prompt)
设置页(settings.section,id global-prompt,order 30),以 Tabs(全局 / 按工作区) 渲染。全局 Tab 注入一段作用于所有会话系统提示词的文本(段 global-prompt,order 50);按工作区 Tab 注入按工作区提示词(段 workspace-prompt,order 60)。两个段都以 interpolate: false 注册:提示词文本与引用文件里的 {{...}} 一律按字面保留,用户内容永不被改写,未注册的 {{name}} 也不可能让组装失败。0.1.6 之前的内核没有分段的 interpolate 开关,由 lib/prompt-literal.js 在组装结果上退化为 { 连续串空格化。

工作区提示词(Workspace Prompt)
为 cwd 前缀匹配到已配置工作区目录(该目录及子目录)的会话注入按工作区提示词。工作区列表由活跃会话的 cwd 聚合而来(ctx.agents.roots(),去重并按会话数计数)。当多个已启用工作区前缀命中某会话的 cwd 时,取最具体(路径最深/最长)者。removed 记录用户已移除的路径,使活跃工作区同步不重新补回。工作区行的启用开关 即时保存(live-save);「保存」按钮仅持久化提示词内容 + 引用文件。

引用文件(Referenced Files)
全局提示词与工作区提示词均可引用文件列表。每次组装重新读取每个引用文件(UTF-8;按 mtimeMs + 大小缓存,未变化的文件不重复读盘),注入到提示词文本之后。有字节预算(globalPrompt.maxFileBytes / maxTotalBytes,默认 256 KiB / 1 MiB):超限文件跳过而不是阻塞组装。读取失败同样跳过,两种情况都在 UI 中显示具体原因。支持纯文本/markdown。每个文件的读取状态是 host 的运行时投影,经只读路由 GET /api/session-toolkit/state 送到 UI(ok:N 字符 / fail:原因 / 未读取),浏览器半只在设置页打开期间轮询它;状态不写入任何配置文件,内容未变时不重建投影。

会话自动恢复(Session Auto-Resume)
开启开关的会话在 GUI 重启后自动恢复,优先走官方恢复链路(ctx.sessionController.resolveAgent)——它除了 mount preset,还会通过 installSelection 恢复会话自己的模型选择,并做 subagent 归属校验与并发恢复去重;0.1.6 之前没有该服务的内核回落为 ctx.agents.resume + 手工 mount preset,并携带 agentDefaultModel 的默认模型。开启某会话即立即恢复(false→true 边沿)。过滤:开关开启、仅顶层(无 subagent origin、无 delegationDepth > 0、无 parentSession)、非空白(快照形状的 eventCount !== 0)。并发受限(CONCURRENCY = 3),逐项失败隔离 + 在途集合防重复恢复。

Web 重启(Web Restart)
General 设置中的「重启服务」入口(settings.general.item,id web-restart,order 90),重启 GUI 服务器并显示全屏进度覆盖层(探针驱动进度、重载前填充动画、90 秒超时回退到手动刷新)。两条平台链路都独立于将要退出的服务器进程:

- Windows(windows-script):wscript.exe 执行 launcher VBS(隐藏控制台),由它运行 /autostart/dsh-web-restart.cmd;spawn 继承服务器进程 token,提权分支(唯一 UAC 来源)不可达。
- macOS / Linux(posix-relaunch;配置了 webRestart.scriptPath 时为 posix-script):无需任何配置即可自重启——host 生成一次性 /bin/sh 脚本:SIGTERM 当前 PID → 最多等 10 秒(超时 SIGKILL)→ cd 回原工作目录 → 以原命令(process.execPath + process.argv.slice(1))重新执行,输出追加到 /autostart/dsh-web-restart.log。若服务器由 supervisor 之类托管,可把 webRestart.scriptPath 指向自己的 .sh 接管。

client 挂载时探测 GET /api/restart,host 回报 available: false(不支持的平台)时直接隐藏入口,该平台 POST 返回 501。路由:GET /api/restart(健康探针,恒 200 + available/mode/platform)与 POST /api/restart(触发;重启在途 409,配置的脚本不存在或无法自重启 500 且带原因,可继续 202 + 500ms 缓冲后 spawn)。client 只在拿到 202 时进入覆盖层——其它状态就地显示错误,不再空转 90 秒。恢复检测采用中断-恢复:覆盖层仅在观察到探针连续失败 restartFailThreshold 次并再次返回 200 后重载;若探针全程可达则报告「未检测到重启」(noRestart)直到超时,提供手动刷新。

同一个 webServer 上另注册只读状态路由 GET /api/session-toolkit/state(活跃工作区 + 引用文件读取状态;非 GET 一律 405):设置页靠它拿这两项运行时投影,因此它们不再占用 settings 命名空间,也不落盘。

Session log 按钮平移(Session-Log Button Relocation)
遮蔽 conversation.session.header.utilities 中的官方条目(同 id session-log-download,priority −1,cell shadowing),并在 conversation.session.header.actions 注册副本(id session-log-download-moved,order 41),复用官方 sessionLogDownload controller(ctx.get('sessionLogDownload')),下载行为与官方一致。副本对齐 0.1.6 的官方形态——「⋯ 更多操作」菜单(单条「下载 Session 日志」)触发共享对话框(文案走本插件自己的 locale 命名空间);它是冻结的复刻件:官方改版必须人工同步,官方条目新增菜单项时也要重新核对遮蔽策略。

会话管理(Session Admin)
host 平面另注册两个工具,与 send_to_session / list_sessions 同平面:

- create_session —— 自主创建一个新的顶层会话(GUI 左侧导航里的一个聊天窗口)。cwd 与 prompt 均必填:cwd 必须是绝对路径(无 cwd 的会话不会进宿主列表),prompt 是新会话的首条消息。创建成功即产生一条真实用户消息(会真实跑一轮模型、消耗一次调用);按内核设计,产生过事件的会话会被持久化,因此本工具不提供「只登记、不说话」的临时会话。可选 title 会立即设定标题并 pin 住。返回体含 sessionId、cwd、status、title 与 notes。
- rename_session —— 修改一个在线(live)会话的标题。改名会 pin 住标题,不再被自动标题生成覆盖。目标必须是顶层会话且当前在线:目标是子会话(origin=subagent 或 delegationDepth>0)时明确拒绝,不静默改写。

两者都以结构化结果返回(工具执行本身不抛未捕获异常):成功 { ok: true, … },失败 { ok: false, error: '', errorText: '' }。错误码:MODEL_UNAVAILABLE / MODEL_SELECTION_FAILED / MODEL_SELECTION_INVALID / EMPTY_CWD / CWD_NOT_ABSOLUTE / EMPTY_PROMPT / PROMPT_TOO_LONG / PRESET_RESOLVE_FAILED / CREATE_FAILED / CREATE_UNAVAILABLE / CREATE_NO_AGENT / EMPTY_TARGET / EMPTY_TITLE / SESSION_UNAVAILABLE / TARGET_IS_SUBAGENT / TITLE_SERVICE_UNAVAILABLE / UNEXPECTED。
(prompt 缺失由内核工具参数校验在框架层拒绝——内核把它转成工具错误结果,该异常不经本插件代码;prompt 传了但纯空白才由本插件返回 EMPTY_PROMPT。两者都不创建会话。)

两处如实声明:
- 可见性未在工具内验证 —— 「带 prompt 建出的会话会出现在左侧导航」取决于内核是否真正开跑一轮(导航按「空白会话」判据过滤,该状态只在 turn/start 时翻转;followup 只是入队并唤醒驱动)。因此返回体不下「已出现在导航」的结论,notes 会写明这一点。
- preset 降级 —— agentPresets 服务存在但默认 preset 解析失败时,工具返回 PRESET_RESOLVE_FAILED,而不是交付一个没有 preset 的残缺会话;服务整体缺失属合法降级,会话照建并带说明性 notes。

会话间消息(Peer Messaging)
host 平面注册 send_to_session / list_sessions 工具(按 id 或工作区路径寻址会话、wakeup 投递),并在 conversation.session.header.actions(id copy-session-id,order 30)与 conversation.input.left(id copy-session-id-input,order 30)各加「复制会话 ID」按钮。发出消息内容在投递前经 toPlainText 转为纯文本,接收方看到整洁文本而非原始 markdown。wakeup 有两条不同的投递路径:wakeup: true(走 steer)时,目标正在执行 ⇒ 消息进当前轮的下一个步边界(既不新开一轮,也不打断当前步);目标空闲 ⇒ 立刻开一轮;wakeup: false(走 inject)只投递、不唤醒任何人。两种情形仍会排队而不是插入:投递时目标空闲(没有步边界可插,消息在下一个轮次边界被收下),以及内核的 wakingAfterAbort 重分类(上一个活动正在被取消时,steer 被静默降级为 next-turn)。因此 steer 不是「消息永不等待」的承诺——判断一条消息是否真的插入,要看三件事同时成立:target 是 next-step、投递时有轮在跑、且投递到消费之间没有新的 turn/start。

提示词去重(Prompt Dedup)
对 身份 / 全局 / 工作区 三段系统提示词(段 session-identity、global-prompt、workspace-prompt,order 40/50/60)做跨段行级去重。promptDedup.enabled 默认开启(仅显式设为 false 时禁用)。按 \n 切分,三段内出现过的完全相同原行只保留"先出现"那一份(全局 seen 贯穿三段,同段内部自重复也收敛),后出现段的重复行被去掉;任何段独有内容一律保留。空行(含只有空白的行)不参与去重——空行是 markdown 的段落/列表分隔,把它当成重复行会让第一段之后的每一段空行都被删掉。不解析 {{name}} 占位符(单行完整组,按行切分不会切断)、不破坏 markdown、不设 complete,绝不动 harness 自带段(harness:identity / deployment:persona / 工具段)。机制:在插件根 ctx 订阅 system-prompt/assemble waterfall,await next() 后对返回结果的 sections 做去重再返回。

兼容性

插件已对照 DeepSeek Harness dsh-v0.1.7-alpha.1 验证,并以它作为最低支持版本,另在 dsh-v0.1.7-rc.1 上重新验证过(契约面 + 全套门,非完整功能回归;声明的依赖区间——harness 包 ^0.1.7-alpha.1、schemastery ^3.18.3——本来就同时覆盖两者):0.1.7 把 settings 从「插件注册命名空间 + ctx.settingsScope.bind」改为「条目 Config 的 volatile 字段 + ctx.configForms」,用户数据面因此整体迁移(见设置字段)。在 0.1.6 及更早内核上,客户端条目会停在 pending (waiting for service: configForms),web boot 报「Failed to load plugins」——这是响亮失败而非静默降级,处置是升级 harness 或卸载本插件。interpolate: false 与 ctx.sessionController.resolveAgent 的既有兜底不变。

- 框架:@deepseek-ai/cordis 4.0.3 与 @deepseek-ai/schemastery 3.18.3(即 dsh-v0.1.7-alpha.1 vendored 的版本;dsh-v0.1.7-rc.1 vendored 的是 cordis 4.0.4 / schemastery 3.18.4)。@deepseek-ai/schemastery 的下限是 ^3.18.3:volatile() 在该版本才存在,更早的 3.18.x 会让 Config 构造直接抛错。插件经 cordis harness 加载,并以 dsh.bundle.patch 注册为 bundle。
- Host 服务(已对照原生源码校验):本条目 Config 的 volatile() 字段 + .get() 实时读取,提交后由 ctx.on('loader/volatile-update', …) 通知(不重挂插件);ctx.get('settings').update('session-toolkit', patch) 作为 host 写回条目 config 的入口(工作区自动补回用);ctx.systemPrompt.section({ name, order, text, interpolate: false });ctx.agents.{ get, resume({ resumeSessionId, agentOptions, setup }), roots, requireInitiator };ctx.sessionController.resolveAgent(sessionId);session.header 字段(cwd、origin、delegationDepth、parentSession、agentPreset;没有 seedLength);用于等待晚到可选服务的 ctx.inject(names, cb);ctx.get('webServer').register({ kind: 'exact', path, handler });@deepseek-ai/dsh-tools 的 defineTool + tools.register();以及 ctx.get('agentDefaultModel')、sessionPersistence、sessionTitle、workspaceRegistry、sessionLogDownload、timer、on、effect。
- Client 服务(已校验):window.__ModuleLoader__.load({ id, factory });ctx.get('slots') → slots.register(meta, render) / slots.inject(name, fn)(低 priority 遮蔽);ctx.get('configForms').get('session-toolkit') → 表单 { getSnapshot()/.value/.status, subscribe, set(field, value), unset(field), mutate(ops, expectedRevision) }(写入经 host 校验后落盘当前 profile 的 cordis.patch.yml);ctx.get('locale') → register(ns, { zh, en }) / bind(ns);只读状态路由 GET /api/session-toolkit/state;以及 timer client 服务(ctx.timeout)。bundle 的运行时 require 均解析自模块表种子词(react、react/jsx-runtime、@deepseek-ai/dsh-client-store、@deepseek-ai/dsh-client-ui-primitives、……)。

配置与设置数据面
插件的 host 侧 Config 在插件加载时即用 schemastery 校验整棵配置树。解析顺序 = schema 默认 → profile patch(用户层);两者都在 host 侧解析完再交给插件。

DSH 0.1.7 起,settings 只投影带 volatile() 的字段,并只用两个事实标识一份设置:被编辑条目的 id(= session-toolkit)与该条目的 Config。因此:

- 用户数据(身份文本、全局/工作区提示词、自动上线开关、UI 旋钮)声明为 volatile 字段,浏览器半经 ctx.get('configForms').get('session-toolkit') 读写同一份条目 Config,写入由 host 校验后落盘当前 profile 的 cordis.patch.yml(不再有 settings.yaml 命名空间,也没有 settingsScope 服务)。
- host 半每次用到时 .get() 实时读取:volatile 值提交后不重挂插件,identity / global-prompt 段在下一次组装就生效,auto-resume 经 loader/volatile-update 立刻恢复新开启的会话。
- 普通字段(顺序、上限、重试、并发等部署参数)保持非 volatile:它们同样能在设置页里看到,但修改走 cordis 配置的正常生命周期。
- 运行时的只读投影(活跃工作区、引用文件读取状态)不属于配置,经 GET /api/session-toolkit/state 直接送给浏览器,既不落盘也不出现在表单里。

架构

- Host 半 —— lib/index.js 组装九个功能模块(identity.js、global-prompt.js、auto-resume.js、web-restart.js、peer-message.js、session-admin.js、log-reposition.js、prompt-dedup.js、prompt-literal.js)。inject 为模块依赖去重并集;每个模块的 apply 在 safe() 守卫内运行,单个模块失败不影响整包。所有贡献均绑定生命周期(提示词段与 HTTP 路由用 ctx.effect,工具随插件 fiber 注册;定时器统一走 timer 服务)。global-prompt.js 拥有 globalPrompt / workspacePrompt 两组 volatile 字段的读取、readPromptFiles 辅助函数(实时 fs.readFileSync 读)、活跃工作区聚合(agents.roots() → GET /api/session-toolkit/state),以及把新出现的工作区路径经 ctx.get('settings').update('session-toolkit', …) 补进条目 config。
- Client 半 —— client/client.js 为单一 window.__ModuleLoader__.load bundle;五个 UI 模块内联在 IIFE 中,在一个 apply 里按序注册全部 slot(逐模块守卫)。所有 UI 用 React.createElement;样式以 data-plugin style 标签注入,使用主题 CSS 变量与深色覆盖;无全局 DOM 操作。global-prompt 模块渲染 Tabs(全局 / 按工作区) 页面,并含可复用 FileRefsPanel(添加/移除引用文件;每文件状态来自 GET /api/session-toolkit/state 的轮询投影)。

注册的 Slots

| Slot | Id | Order / priority | 功能 |
|---|---|---|---|
| settings.section | global-prompt | order 30 | 全局 + 工作区提示词页(Tabs) |
| settings.general.item | web-restart | order 90 | 重启入口 |
| conversation.session.header.actions | copy-session-id | order 30 | 复制会话 ID |
| conversation.session.header.actions | session-identity | order 40 | 身份按钮 |
| conversation.session.header.actions | session-log-download-moved | order 41 | Session log 下载 |
| conversation.input.left | copy-session-id-input | order 30 | 复制会话 ID(工具行) |
| conversation.input.left | session-identity-input | order 40 | 身份按钮(工具行) |
| conversation.session.header.utilities | session-log-download | priority −1(遮蔽) | 隐藏官方按钮 |

配置

设置字段(条目 config 的 volatile 部分)

条目 id 固定为 session-toolkit;下面这些 volatile() 字段就是设置页读写的那份数据,schema 校验后落盘当前 profile 的 cordis.patch.yml。字段名即 config 路径(identity.sessions 等),浏览器半经 configForms.get('session-toolkit') 用同样的路径读写。

| 字段 | Schema | 说明 |
|---|---|---|
| identity.default | {enabled: boolean, text: string} | 默认身份。解析顺序:会话记录 → 默认 → 空。禁用或空文本不注入。 |
| identity.sessions | Record | 每会话身份。身份文本上限 identity.maxText(8000 字符,token 守卫)。 |
| autoResume.sessions | Record | 每会话「重启后自动上线」开关;缺省键视为关闭。 |
| globalPrompt.{enabled,content,files} | {enabled: boolean, content: string, files: string[]} | 启用时注入所有会话。files 为引用文件列表,组装时读取并追加(按 mtime/大小缓存;读取失败或超限的文件跳过)。 |
| workspacePrompt.workspaces | Record | 按工作区提示词。某会话会得到与其 cwd 目录前缀匹配、路径最深(最具体)且启用的工作区提示词。 |
| workspacePrompt.removed | string[] | 用户已移除的路径,使活跃工作区同步不重新补回。 |
| client. | 7 个 UI 旋钮(见下表) | 浏览器半的运行时参数(字符上限、重启超时/轮询/填充、复制反馈)。 |

运行时投影(不落盘、不属于 config):活跃工作区 [{path, sessionCount}] 来自 ctx.agents.roots()(各 agent 的 session.header.cwd 去重计数;不来自本插件作用域不可见的 workspaceRegistry),引用文件读取状态为 Record;两者都经 GET /api/session-toolkit/state 提供给设置页。

插件 Config(cordis)

聚合包导出单一 Config(schemastery schema),按功能分键。默认值 = 现状;可在 cordis.yml / cordis.patch.yml 插件行的 config 字段覆盖,无需改代码。带 .volatile() 的分键(用户数据 + client.)就是设置页读写的那份数据,浏览器半经 configForms.get('session-toolkit') 读到同一份解析结果(见配置与设置数据面)。

- id: session-toolkit
name: 'dsh-session-toolkit'
config:
identity:
maxText: 8000
sectionOrder: 40
default:                # volatile:默认身份
enabled: false
text: ''
sessions: {}            # volatile:Record
globalPrompt:
sectionOrder: 50
workspaceSectionOrder: 60
maxFileBytes: 262144    # 单个引用文件上限;超限文件跳过并在 UI 报 fail
maxTotalBytes: 1048576  # 单个段的全部引用文件合计上限
enabled: false          # volatile:全局提示词开关
content: ''             # volatile:全局提示词正文
files: []               # volatile:引用文件列表
workspacePrompt:          # volatile:按工作区提示词
workspaces: {}          # Record
removed: []             # 用户已移除的路径
autoResume:
concurrency: 3
sessions: {}            # volatile:Record
webRestart:
scriptPath: ''          # 可选;缺省推导为 /autostart/dsh-web-restart.cmd
spawnDelayMs: 500
promptDedup:
enabled: true           # 三段(身份/全局/工作区)跨段行级去重开关;默认 true = 开启(仅显式设为 false 时禁用)
client:
identityCharLimit: 4000 # volatile:以下 7 键都由浏览器半读取
restartTimeoutMs: 90000
restartPollMs: 1000
restartFillMs: 600
restartFailThreshold: 2
restartSettleMs: 8000
copyFeedbackMs: 1600

| 键 | 默认值 | 含义 |
|---|---|---|
| identity.maxText | 8000 | 身份文本截断上限(字符,token 守卫)。UI 软上限为 client.identityCharLimit(4000,编辑区限制),host 硬截断为本值(8000)。 |
| identity.sectionOrder | 40 | 身份段在系统提示词中的顺序。迁移:显式固定 identity.sectionOrder: 55 的用户需改为 40 以保持「身份 → 全局 → 工作区」顺序。 |
| globalPrompt.sectionOrder | 50 | 全局提示词段的顺序。 |
| globalPrompt.workspaceSectionOrder | 60 | 工作区提示词段的顺序(置于最后)。 |
| globalPrompt.maxFileBytes | 262144 | 单个引用文件的字节上限;超限文件跳过(状态 fail)而不是阻塞组装。 |
| globalPrompt.maxTotalBytes | 1048576 | 单个段全部引用文件的合计字节预算。 |
| autoResume.concurrency | 3 | 启动恢复的最大在途 resume 数。 |
| webRestart.scriptPath | 推导 | 重启脚本路径。为空(默认)= Windows 推导 /autostart/dsh-web-restart.cmd、macOS+Linux 推导 …/dsh-web-restart.sh,并在 POSIX 上额外启用自重启(无需脚本)。填路径=交给你自己的脚本:POSIX 下经 /bin/sh 执行,文件不存在时 POST 立即 500,不再让覆盖层空转。 |
| webRestart.spawnDelayMs | 500 | 202 缓冲后 spawn 重启脚本的延迟。 |
| promptDedup.enabled | true | 三段(身份/全局/工作区)系统提示词跨段行级去重开关(默认开启,仅显式设为 false 时禁用)。开启时,三段中出现过的完全相同的非空原行只保留"先出现"一份(全局 seen 贯穿三段),后出现段的重复行被去掉;空行永远保留(它是 markdown 的段落/列表分隔)。任何段独有内容一律保留。不解析 {{name}} 占位符、不破坏 markdown、不设 complete,绝不动 harness 自带段。 |
| identity.default / identity.sessions | 空 | 用户数据(volatile):默认身份与每会话身份。设置页「会话身份」写入;也可直接写 profile patch。 |
| globalPrompt.enabled / .content / .files | off / 空 | 用户数据(volatile):全局提示词开关、正文、引用文件列表。 |
| workspacePrompt.workspaces / .removed | 空 | 用户数据(volatile):按工作区提示词与「已移除路径」。活跃工作区同步会把新出现的路径补进 workspaces(经 ctx.get('settings').update),removed 里的路径不会被补回。 |
| autoResume.sessions | 空 | 用户数据(volatile):每会话「重启后自动上线」。false→true 立即恢复该会话。 |
下列 client. 键由 host 校验;浏览器半经 configForms.get('session-toolkit') 读的就是这几个字段(表单不可用时回落 client/client.js 里的 UI_FALLBACK,值等于历史默认值)。设置页「插件」条目里可以直接改它们。

| 键 | 默认值 | 含义 |
|---|---|---|
| client.identityCharLimit | 4000 | 身份编辑区字符上限(UI 软上限;全局提示词编辑区同用)。 |
| client.restartTimeoutMs | 90000 | 重启覆盖层超时(之后提示手动刷新)。 |
| client.restartPollMs | 1000 | 重启健康轮询间隔(也是进度 tick)。 |
| client.restartFillMs | 600 | 检测到恢复后的进度填充动画时长。 |
| client.restartFailThreshold | 2 | 判定中断前的连续健康轮询失败次数。 |
| client.restartSettleMs | 8000 | 检测到恢复后、自动刷新前的稳定窗口(ms)。DSH 会话标题由 LLM 异步生成、无就绪信号,此值是"重启后首轮 reload 的等待窗口",用于改善标题 fallback(显示为工作区名)。若个别会话标题仍显示工作区名,可手动刷新或调大该键;根治需 DSH 提供"标题就绪"信号(建议向 DSH 反馈)。 |
| client.copyFeedbackMs | 1600 | 复制反馈对勾时长。 |

从旧 settings.yaml 迁移(0.1.6 → 0.1.7)

0.1.6 及更早,本插件的用户数据放在 /settings.yaml 的命名空间里;0.1.7 的 harness 启动时会把该文件改名为 settings.yaml.imported,并只把「节名 == 某个存活条目 id」的节导入该条目——本插件的旧命名空间(session-identity 等)不匹配任何条目 id,因此它们被拒收、原样留在 settings.yaml.imported 里。

迁移映射(旧节 → 新 config 路径),一次搬完即可:

| 旧 settings.yaml 节 | 新 config 路径 |
|---|---|
| session-identity.default / .sessions | identity.default / identity.sessions |
| global-prompt.{enabled,content,files} | globalPrompt.{enabled,content,files} |
| workspace-prompt.{workspaces,removed} | workspacePrompt.{workspaces,removed} |
| session-auto-resume.sessions | autoResume.sessions |
| session-toolkit-ui. | client.(默认值相同,通常无需搬) |
| workspace-registry-active、prompt-file-status | 丢弃:它们是运行时投影,现在由 GET /api/session-toolkit/state 提供 |

两种落地方式,选一种:

1. 设置页:打开「插件」里 dsh-session-toolkit 条目的设置页,把旧值贴进对应字段(表单会写进 profile patch)。
2. 直接写 profile patch(适合批量搬运):在 /profiles//cordis.patch.yml 追加一个 - id: session-toolkit 条目,把上表右侧的路径放进 config:。写之前先用本插件自己的 Config 校验一遍即可避免形状错误:

import plugin from 'dsh-session-toolkit'   // 或直接 import 仓库的 lib/index.js
plugin.Config(migratedConfig)              // 抛错即形状不对

部署

安装到任意 profile(bundle 层;单一来源,无副本):

来自 npm
dsh plugin --profile web add dsh-session-toolkit

来自 GitHub
dsh plugin --profile web add github:Han-Yao94/dsh-session-toolkit

来自本地 checkout / tarball
dsh plugin --profile web add ./dsh-session-toolkit-.tgz

包的 dsh.bundle.patch(cordis.patch.yml)将单一入口(id: session-toolkit,name: 'dsh-session-toolkit')注册为 bundle 层——在 dsh-base / dsh-web-app 之后、profile patch 层之前应用(层序:bundles 依次 → profile patch → home patch → --patch 覆盖)。

卸载:dsh plugin --profile web remove dsh-session-toolkit。

本地开发

迭代源码时可安装 checkout(dsh plugin --profile web add ,使用 pnpm link: 依赖),或手工 junction 到 profile 的 node_modules 并在 profile 的 cordis.patch.yml 显式 - insert: 注册。推荐使用官方 dsh plugin add 流程。

验证门(仅限源码 checkout 内运行——scripts/ 不随发布包分发;无需构建步骤,也无需安装依赖):

pnpm check    # 语法门 —— 对全部随包 JS 跑 node --check
pnpm verify   # 另加打包契约 —— 入口可达、import 声明完整、双语 README 版本一致

pnpm verify 断言「工作区内容 == 包内容」,因此一旦有人给 package.json 加上 prepare/prepack/prepublishOnly 脚本,它会故意报错。升级 harness 时使用的 DSH 集成点清单见 docs/agents/integration-contracts.md。

分享与安装

已发布至 npm(dsh-session-toolkit,最新已发布版本 v0.1.12,MIT)并同步至 GitHub(github.com/Han-Yao94/dsh-session-toolkit)。纯 JS 包——无构建步骤、无 prepare 脚本。files 已白名单 lib/、client/、cordis.patch.yml 与 README。

npm 上就是当前版本。 0.1.12 已发布,因此 dsh plugin --profile web add dsh-session-toolkit 即可得到上文介绍的会话管理工具(create_session / rename_session)及其余全部功能。0.1.9 与 0.1.10 打过 tag 但从未进入 npm;0.1.11 是 0.1.8 之后第一个真正发布的版本。从本 checkout 或 GitHub 安装(dsh plugin --profile web add github:Han-Yao94/dsh-session-toolkit)等价。

- npm:消费者 dsh plugin --profile web add dsh-session-toolkit 安装;新版本通过 npm publish(或 pnpm publish)发布。
- GitHub:dsh plugin --profile web add github:Han-Yao94/dsh-session-toolkit。
- tarball:pnpm pack → dsh plugin --profile web add ./dsh-session-toolkit-.tgz。

运行时依赖(@deepseek-ai/schemastery(下限 ^3.18.3——volatile() 自该版本起才有)、@deepseek-ai/dsh-tools、@deepseek-ai/dsh-home-paths)声明在 dependencies,随安装自动拉取;平台模块(react、@deepseek-ai/cordis、@deepseek-ai/dsh-client-locale、@deepseek-ai/dsh-client-store、@deepseek-ai/dsh-client-ui-primitives)为 peerDependencies,由 DSH 宿主提供。harness 提供的包一律只写一个前置版本代——^0.1.7-alpha.1:caret 区间不跨 minor,而 semver 还额外要求"比较器里必须有一个指明候选人自身 major.minor.patch 的前置版本",这正是当初要写成 ^0.1.2-alpha.5 || ^0.1.6-alpha.2 并集的原因,也正是 §F 偏斜的根因(插件拿到自己的旧副本、宿主在跑新版本)。既然 dsh-v0.1.7-alpha.1 已是最低支持版本,旧的并集成员已删除:今后每次 harness 换代都必须同步抬高这些区间,而 node scripts/dependency-skew.measure.mjs --profile /profiles/web(期望 SKEW_COUNT=0)就是告诉你该抬了的那个检查。区间只在安装期生效——重新安装并重启 GUI 之后再量。@deepseek-ai/dsh-client-ui-slots 刻意不声明:slots 服务由 web shell 播种,npm peer 声明是死重。已验证:打包 tgz 的干净安装可完整解析所有 import(不依赖本地 junction)。另有两条安装侧工具(需要外部 checkout/profile,因此不挂 CI,见契约表 §D/§E/§F):

node scripts/dependency-skew.measure.mjs --profile /profiles/web   # §F:期望 SKEW_COUNT=0
node scripts/dsh-log-ui.drift.mjs --harness           # §E:复刻件漂移

模型体验

系统提示词贡献

模型看到的内容

每次组装贡献三个段,顺序:session-identity(order 40)→ global-prompt(order 50)→ workspace-prompt(order 60),位于部署 persona 之后、工具引导(100–199)之前。身份段在组装时按 agent(AssembleContext.agent)从 session-identity 设置解析,subagent(origin/delegationDepth)跳过。工作区段为 cwd 前缀匹配到配置工作区(取路径最深/最具体且启用者)的会话注入该工作区提示词,否则为空。

全局段与工作区段都会在提示词文本后追加其引用文件内容:每次组装读取 files(UTF-8,按 mtime/大小缓存),按原文拼接(段声明 interpolate: false,内容不被改写)。无法读取或超出字节预算的文件会跳过(其内容不注入),但其读取状态被记录供 UI 显示。空段在渲染时删除。

Token 影响

启用时三个段的文本随每次请求重复。全局提示词作用于所有会话;身份文本仅作用于能解析到它的会话(自身记录或默认);工作区文本仅作用于 cwd 前缀匹配到已启用且已配置工作区(取最具体)的会话。引用文件的完整内容会加入实际提示词,因此消耗额外 token——大引用文件会显著增加每次请求的 token 成本。身份文本上限 8000 字符(token 守卫)。

KV Cache 影响

设置不变时各段渲染文本是请求前缀的固定部分;修改会话身份或全局/工作区提示词(或编辑/新增引用文件)可能从首个变化 token 起使提供方缓存复用失效(与官方 persona 段语义一致)。

工具面

send_to_session、list_sessions、create_session 与 rename_session 在 host 平面注册,所有会话可见(subagent 经常驻 preset 组装继承)。参数与返回均为 JSON 兼容。四个工具都会向模型暴露,因此 create_session 的语义后果(创建即产生一条真实用户消息并消耗一次模型调用)对模型是可见的。

机制与红线

- 身份注入 使用单一全局段、text 提供方按 agent 求值——无逐 agent 注册、无生命周期开销、设置变更实时生效。
- frozen 配置铁律(红线) —— volatile 字段 ref.get() 返回的是 deepFreeze 快照(不可变)。任何要改的地方必须先 { ... }(数组 .slice()) 拷贝成可变对象再提交:host 半把整份新值交给 ctx.get('settings').update(...),浏览器半把新值交给表单的 set / mutate(它们按 config 路径提交,不用整份替换)。直接改冻结对象会抛 object is not extensible(正是此处修复的「工作区列表空」根因)。同一 { ... } 拷贝规则适用于 client 对 workspacePrompt.workspaces 的写入(onWsFilesChange / save / saveWsEnabled / removeWorkspace)。
- 引用文件读取、失败跳过 —— readPromptFiles 在每次组装的 text() 内运行(stat 判定是否重读);读取失败或超限的文件不会中断组装,其状态被记录进进程内投影供 GET /api/session-toolkit/state 与设置页显示,且只在内容变化时替换投影对象。
- 自动上线绝不调用 dispose() —— AgentHandle.dispose() 会从存储移除会话;关闭开关只影响下次重启,绝不下线当前会话。
- 重启零 UAC 是构造性保证 —— spawn 继承服务器进程 token(SYSTEM 或用户),taskkill 目标是同权限进程,脚本提权分支(唯一 UAC 来源)不可达。若 3080 被其他程序占用,仍可能出现提权重试(重启脚本中有说明)。
- 遮蔽基于 cell shadowing —— utilities 条目以更低 priority 重注册官方 session-log-download cell;遮蔽崩溃时官方条目优雅 abdicate 回退。
- 纯文本转换 —— toPlainText(10 条规则、代码围栏状态机、宽松匹配)仅在发送时执行;消息结构与 source: { kind: 'user' } 不变。

已知限制与暂缓事项

- client 半为手工维护的单文件 IIFE 包;新增功能需同步维护 lib/ 与 client/client.js 两处。
- client 半的「标识符作用域」没有任何门覆盖。 调 react.useState 的块必须同时 require('react'):react/jsx-runtime 不提供它,而缺绑定时组件渲染即抛错;slot 渲染器会把那个抛错吞掉并丢掉整条 entry,于是症状是「按钮静默地不见了」,而不是任何人看得见的报错。这一形态从 2026-09-18(3e44642 加了 hooks 调用却没加 require)活到 2026-09-22,穿过了全部的门。
- 收到的跨会话消息在界面上是「收起的一行」,不是可读的正文。 send_to_session 按生产者归属记录投递——source: { kind: 'agent-message', form: 'relay', senderSessionId }——而客户端对所有非人类来源都走它对 turn trigger 的渲染,那一行默认收起,点开才见正文。写成 kind: 'user' 会像人类消息一样 inline 展开,但会把另一个 Agent 的话记成用户说的——而那正是 V4 唯一规定为「生产者拥有」的字段。归属优先;点那一行即可读到正文(正文首行仍自带发件人)。
- 图标名属于集成面。 DSH 0.1.7 把 @deepseek-ai/dsh-client-ui-primitives 的图标从 IconXxxOutline 改名为 IconXxxOutlineRegular / IconXxxOutlineMedium(1 px 与 1.3 px 笔画;artwork 保留旧默认 size),因此 client 半必须使用目标 harness 的名字。不存在的名字求值为 undefined,而 React.createElement(undefined, …) 会抛错,导致该组件子树整片空白、而它的导航行照常出现(注册与渲染是两件事)。这一形态对其余所有门都是静默的——语法门、打包门、锚门当时全绿。用 node scripts/primitives-export.assert.mjs --harness  守它:exit 1 会逐条列出插件引用了、而已装 harness 并未导出的成员。
- 平移的 Session log 入口依赖官方 sessionLogDownload controller 接口,且复刻官方 0.1.6 的「⋯ 更多操作」菜单形态;它是冻结的复刻件:DSH 升级后跑一次 node scripts/dsh-log-ui.drift.mjs --harness ——它按同一组锚点双向审计,漂移即非零退出(§E)。有意的分叉:官方 header 菜单此后多了第二项(feedback),本复刻件只保留 download;这是已裁定的状态、不是待决问题——门把它记成 note 而非失败,正因为"跟随上游新增能力"本身是一个决定,而该决定已于 2026-09-22 作出:不跟随。只有确实想要那个 feedback 入口时才需要重开。
- toPlainText 宽松斜体匹配可能误删非格式位置的成对 (如 a * b * c);对 agent 生成消息可接受,边界收紧为可选优化。
- 聚合 inject 并集会等待所列全部服务;某 profile 缺一服务会拖慢整包 apply(web profile 当前齐备)。
- harness 提供的依赖区间是前置版本并集;改完区间必须重跑 pnpm install,并在装好的 profile 上跑 node scripts/dependency-skew.measure.mjs --profile /profiles/web(期望 SKEW_COUNT=0;DE-INSTANCE 表示同版本不同实例,§F 判定为可接受)。
- ctx.get('agentDefaultModel')、sessionTitle、workspaceRegistry 改为调用时惰性解析,缺失时降级为 cwd/路径寻址;tools 与 webServer 改用 ctx.inject 等待就绪——loader 并发创建条目,apply 时刻的 ctx.get 没有顺序保证,晚到会让功能永久静默消失。
- 重启探测窗口 — 仅在健康探测连续失败 restartFailThreshold × restartPollMs(默认 2 × 1000 ms = 2 s)后恢复时判定为重启。若 relaunch 在该窗口内完成,覆盖层可能误报「未检测到重启」(noRestart);调低 restartFailThreshold 到 1 虽更灵敏,也会让单次瞬时失败被误判为重启中断。
- 引用文件在组装路径预热 —— readPromptFiles 每次组装对每个引用文件做一次 statSync,仅在 mtime/大小变化时读盘;单文件与合计字节预算避免超大文件阻塞组装或撑爆提示词,状态投影也只在变化时写入。client 端 files 即时保存(onWsFilesChange / save)。
- UI 旋钮来自同一条目的 client. —— 浏览器半经 configForms.get('session-toolkit') 读 client. 字段(表单不可用时回落冻结的 UI_FALLBACK)。client 条目本身仍拿不到 cordis 行配置,但设置的读取已不再需要 host 镜像:同一条目 Config 两侧都可见。
- 最低 harness 版本 = dsh-v0.1.7-alpha.1 —— settings 数据面在 0.1.7 改成「条目 Config 的 volatile 字段 + configForms」。0.1.6 及更早没有 configForms,客户端条目会停在 pending,web 客户端报「Failed to load plugins」;这是刻意的响亮失败(硬 inject),不是静默降级。

恢复方法

卸载 bundle:dsh plugin --profile web remove dsh-session-toolkit,然后重启 GUI。要回退到整合前的布局,请重新启用原插件而非安装本包。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

同作者(Han-Yao94)的其他插件

💬 加入社群

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

DPharness QQ 群二维码,QQ 扫码进群
QQ 扫码进群
DPharness 飞书群二维码,飞书扫码进群
飞书扫码进群