dsh-session-tools
Đã xác minh@ryuu-64/dsh-session-tools · v0.6.0 · MIT · Giao diện web
Let the agent start a new chat session, send a message to another one, or wait for it to finish.
Cài đặt
dsh plugin add @ryuu-64/dsh-session-tools Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.
Mã nguồn
Thẻ
Tác giả
Readme
dsh-session-tools
让对话里的 AI 能新建会话、给别的会话发消息、看看有哪些会话,以及等别的会话干完活。
四个工具,session_create、session_send、list_sessions、session_wait。新会话会出现在左侧会话列表里,你可以打开并继续对话;想让它归到某个工作区下面也可以。
典型用法:你说"帮我开一个会话去查一下这件事"——AI 直接建好,不用你自己新建再复制粘贴。或者你已经有一个会话在跑,说"等它弄完把结果告诉我",AI 把活派过去,等它做完了再回来接着讲。
session_create:新建会话
| 参数 | 是否必填 | 说明 |
|---|---|---|
prompt |
必填 | 新会话要做的第一件事。新会话看不到当前这个会话,所以要把背景写全。 |
workspacePath |
可选 | 工作区的完整路径。填了,新会话就归到这个工作区下面;不填就是未分组。 |
title |
可选 | 创建时固定的会话标题。优先使用非空标题;省略或只含空白时,从首条任务截取简短标题。 |
wait |
可选 | 默认不等。只有一种情况需要等:你要在新建的那个会话跑完第一轮之后才继续。 |
timeoutMs |
可选 | wait=true 的等待预算(毫秒),默认 60000,最多 300000。 |
做完返回新会话的名字、sessionId 和首条消息的 messageId 回执,你在左侧列表里就能看到它。投递成功后,等待超时、取消或读取错误都不会删除或停止独立目标会话。
新建或首次消息投递失败时,插件会尝试清理:如果已经确认附加到工作区,就调用 detachSession 解除归属;如果已经取得 Agent 句柄,就调用 dispose 释放它。清理中的异常会被忽略,工具仍返回最初的错误。
DSH 0.1.5-rc.2 的 dispose 尝试停止 Agent、关闭持久化句柄并移除运行时注册;关闭句柄负责完成待写入数据并释放写所有权,不是删除持久化记录。因此,失败后不能保证完全回滚或不留记录,也不表示每种失败都会留下记录。插件不会为此额外删除会话数据。
缺省标题使用宿主公开的 fallbackSessionTitle 规范化和截取规则:清理控制字符、合并空白,再取前 5 个以空白分隔的词,最多 40 个 UTF-8 字节,不截断 Unicode 码点(中文同样按字节上限截取)。这两个上限来自官方 [email protected] 标准配置。显式标题仍由宿主规范化,并受宿主的标题长度上限约束;若选定标题清理后没有可见内容,创建失败,不投递消息。
标题在首条消息投递前通过公开的重命名接口写入会话日志并固定,不额外调用模型;首条消息仍标记为 source.kind=plugin。后续对话不会自动改名,即使宿主启用了自动标题提供器。用户仍可手动改名;显式刷新标题则遵循宿主自己的刷新规则。
session_send:给别的会话发消息
| 参数 | 是否必填 | 说明 |
|---|---|---|
sessionId |
必填 | 目标会话的 id,形如 session-9311a0fb-65c7-4b04-b142-1444642a627e。 |
message |
必填 | 要发的内容。对方看不到当前这个会话,所以要写清楚。 |
wait |
可选 | 默认不等:发出去就返回。填 true 就等对方把这条消息处理完,并把它的回答带回来。 |
timeoutMs |
可选 | 等多久(毫秒)。默认 60000(一分钟),最多 300000(五分钟)。等超了返回等待超时和同一回执,不猜测任务是否成功。 |
默认发出即返回 sessionId 与 messageId;messageStatus 区分仍在排队和已被轮次领取。它不表示已经执行成功。消息来源标记为插件,不会被当成是你本人打的字。目标会话如果没开着,会被自动打开再把消息排进去。
填了 wait 就观察这条消息的回执:根据宿主公开的持久 inbox 事件、消息接纳记录和 turn/end 关联到具体轮次,只读取那一轮的输出。A、B 分别进入两轮时,各自保留自己的回答;同一轮接纳多条消息时,共用该轮结果。会话空闲、消息离开队列、出现新的回答,都不能单独证明本条消息成功。
session_wait:等别的会话干完
如果消息已经发出去了,或者那个会话本来就在忙,用这个等它收尾。
| 参数 | 是否必填 | 说明 |
|---|---|---|
sessionId |
必填 | 回执中的目标会话 id。 |
messageId |
可选 | session_create 或 session_send 返回的消息 id。要查询本次消息结果时必须一起传入。 |
timeoutMs |
可选 | 等多久(毫秒),默认 60000,最多 300000。 |
它只负责等和看:不会恢复会话、重发消息或打断目标。传入 sessionId 和 messageId 时返回 scope=message;目标关闭时从 JSONL 重建回执。如果保存的记录还只有排队或领取状态,立即如实返回,不替你启动目标。只传 sessionId 时返回 scope=sessionOverview,明确标为会话概况;其中的最新回答不是任何特定消息的结果。
等待结果与取消
三个等待入口统一默认 60 秒、最多 300 秒;省略或非正数使用默认值;类型不符及非有限数由参数校验拒绝。正的小数至少为 1 毫秒。预算从开始观察起计算,包括只读查询和结果读取,不包括审批或创建阶段。
waitStatus=completed:本次观察/读取已返回。还要看messageStatus,它不等于消息成功waitStatus=timedOut:等待预算耗尽waitStatus=callerCancelled:调用方取消本次等待;不是目标轮次被取消- 后两种情况
completed=false,保留sessionId和messageId,不自动重发、不取消目标,也不返回可能过时的答案。稍后把这两个字段一起传给session_wait
消息回执的 messageStatus:
| 状态 | 含义 |
|---|---|
queued |
消息仍在收件队列中 |
delivered |
已被具体轮次领取,尚未记录该轮的终态;不保证模型已经接纳 |
turnCompleted |
同一消息已被模型接纳,且其所属轮次以 completed 结束;此时 completed=true |
discarded |
消息在队列中被删除、替换或清空,没有执行完成 |
cancelled |
所属轮次以 aborted 结束 |
blocked / failed |
所属轮次被阻止 / 执行出错 |
interrupted / incomplete |
宿主已记录中断修复 / 输出达到 token 上限 |
unknown |
缺少可靠归属证据、消息被改写而未接纳、身份复用或日志不能可靠解码 |
turnCompleted 仅证明宿主轮次正常结束,不证明业务任务成功;例如测试是否通过仍需阅读结果。空文本可以是有效的空结果。输出采用 rc.2 的选择规则:所属轮次最后一个非空 assistant 内容;没有时才回退到该轮持久化 message/attempt 的流式文本,不读取后续轮次或旧答案。
回执投影通过公开 sessionProjections 注册,宿主可以保存其检查点;缓存不是新的事实来源。冷读始终从完整持久事件重建。宿主尚未 flush 就崩溃时可能丢失日志尾部;缺少记录只能返回 unknown,不能保证凭回执恢复尚未落盘的事实。插件不创造私有 message → result API,也不会为尚未闭合的崩溃日志猜一个成功结局:在宿主正式恢复并记录 interrupted 前,只能报告已有领取状态。session_send 的回执同时保存在工具结果元数据中;创建回执写入持久结果文本,现有创建卡片仍使用原有的会话身份元数据。
每次等待结束都会清理自己的计时器和取消监听器。真实 rc.2 持久化冷读使用只读句柄并传入取消信号,结束时关闭句柄;仅提供旧查询服务的兼容宿主,其不可取消读取由宿主自行管理,但不会延长本次等待预算。按消息等待使用可取消观察;会话概况仍使用 rc.2 的 whenIdle()(不接受取消信号),只停止观察,不对目标调用 cancel 或 dispose。
list_sessions:先看看有哪些会话
不用记 id。当你要说的是"发给那个讨论发布的会话",AI 可以先列出会话(名字、目录、哪个是当前会话、现在在干什么),你说清是哪一个,它再发。
| 参数 | 是否必填 | 说明 |
|---|---|---|
limit |
可选 | 最多列几个,默认 20,按最近活动排序。 |
最近活动取会话自身日志中以下记录的最大时间:
- 新消息加入收件箱,以及
user/message用户消息 assistant/message模型输出,以及assistant/attempt模型尝试turn/start轮次开始、step/start步骤开始、tool/call工具调用开始turn/end的completed、aborted、blocked、error、max-tokens结束状态;正常完成、用户取消、被阻止和执行失败都计入
单独改标题、读取或重新打开会话不算新活动;分叉继承的父会话记录也不算子会话的新活动。没有以上记录时使用创建时间。先对所有普通会话排序,再取 limit;时间相同时保留宿主原来的顺序。
step/end、tool/result 和 turn/end interrupted 不独立推进活动时间。宿主恢复崩溃日志时也会补写这些结束记录;其中步骤结束记录可能与真正执行产生的记录完全相同,旧日志无法证明其来源。这里不尝试凭时间或字段猜测来源。因此,工具或步骤刚结束、但还没有下一条上述明确记录时,排序时间暂不更新;后续模型输出、新执行开始或真实轮次结束会正常更新时间。单独改名或恢复不会改变排序,保存后和重启后的读取使用同一定义。
开着的会话读取内存日志,关闭的会话只读持久日志,不会为了排序恢复 Agent 或执行任务。同一个工具实例最多同时做 4 次活动读取;按内存日志长度或持久化服务的日志版本缓存活动时间,不保存完整日志,每次关闭读句柄。宿主未提供版本信息时不复用冷读结果;日志读取失败则报告错误,不把“读不到”当成“没有活动”。
首次调用(或插件重载后)需要扫描所有普通会话的旧日志,因此 limit=1 也不能省略这些读取。开销随总日志量增长;后续调用仍需要列出会话、检查日志版本和读取选中会话的标题,但未改变的日志不重复扫描活动。真实 rc.2 JSONL 合成回归中,24 个会话、9600 条事件、约 258 KB 存储的在 Linux、Node 24.19.0 测试环境中的单次测量为首次约 151 ms、再次约 33 ms;活动扫描分别读取 24 和 0 个日志,另各读 1 次选中标题,最多 4 个读句柄。这里只是小规模测试样本,不是用户大日志库的耗时保证。
每条会带一个状态,就是侧边栏上显示的那个东西:
| 状态 | 意思 |
|---|---|
running |
进行中,正忙着。 |
idle |
空闲,没在干活(开着或者躺着都算)。 |
archived |
已归档,你把它从侧边栏收起来了。 |
子代理会话不会列出来(那是 AI 自己派出去的活)。已归档的会话会列出来并标 archived——因为"已归档"只是你把它从侧边栏收起来了,它本身还在;AI 看到这个标记就知道那是你收起来的会话,可以据此决定要不要往里发消息。
limit 填得不合理会按合理值处理:填 0 或负数按 1 算,填超过 100 按 100 算,小数取整。
动手前会先问你
新建会话、或者往别的会话发消息,通常会先请求确认,写清它要干什么,比如:
允许一次:在工作区
E:\Users\Ryuu\Desktop新建会话,内容是「查一下某件事」
只有本次明确返回“允许一次”才放行;拒绝、取消、没应答、服务缺失或无法判断权限时停止。仅当宿主公开权限接口明确返回 sandbox=danger-full-access 且 approval=never 时免确认。never 单独表示审批请求自动拒绝;例如 workspace-write + never 不会放行。宿主在工具执行前已有的拒绝规则仍然生效。
目标会话没开着时,确认前只读查询并检查目标;获准后重新检查,再恢复会话、发送消息。审批拒绝或取消不会激活目标。
只是等一个会话做完、或者列一下有哪些会话,不会弹卡:这两件事不往任何地方写东西。
建好的会话,卡片上点一下就能过去
做完之后,对话里那张卡片会变成「已创建会话」加一个按钮——点一下直接跳到新建的那个会话,不用去左侧列表里找。
(这一部分由插件的浏览器端负责渲染,所以旧的对话记录也会跟着变成可点。)
0.6.0 升级说明
从 0.5.4 升级时,请检查以下兼容边界:
- Node 支持范围为
^22.19.0 || ^24.0.0,宿主回归基线仍是 DSH0.1.5-rc.2 session_create/session_send返回消息回执。要继续查询同一次消息,请保存并一起传入sessionId和messageId;只传会话 id 的session_wait返回会话概况- 判断消息结果时同时检查
waitStatus和messageStatus。turnCompleted只表示所属轮次正常结束,业务是否成功仍需阅读结果;超时或调用方取消后不要自动重发 - 未指定的会话标题现在由首条任务生成并固定,不额外调用模型,后续对话不会自动改名
此版本包含审批、消息结果归属、等待取消与资源清理、会话卡片、标题、最近活动排序及文档修正。测试范围和已知限制见下方对应章节。
安装
下面以 desktop profile(宿主配置)为例,将插件安装到该配置中:
dsh plugin --profile desktop add @ryuu-64/dsh-session-tools
从源码装:
git clone https://github.com/Ryuu-64/dsh-session-tools.git
dsh plugin --profile desktop add link:C:\path\to\dsh-session-tools
装完后重启对应的 DSH 宿主。插件本身没有额外配置项,但宿主仍须满足下列服务条件。用 link: 方式装的,改完源码要重启应用才生效。
宿主要求与验收范围
本节以 DSH 0.1.5-rc.2 为基线。能否加载取决于宿主提供的服务,不能只按“桌面版”或“无界面”判断。
- 加载必需服务:
tools、agents、sessionTitle、workspaceRegistry、agentDefaultModel,见插件的注入声明。即使不填workspacePath,也不能省略workspaceRegistry - 执行时还需要的能力:新建和发送会话消息需要
approval、sandboxPolicy的公开权限接口;缺失时停止。列出会话和查找已关闭的发送目标需要sessionQuery;关闭会话的日志读取使用sessionPersistence,或兼容宿主提供的sessionQuery.readSession - 官方配置证据:同版本的 Web 组合加载 workspace,Desktop 复用 Web 组合。workspace 并非桌面版独有。缺少该服务的 headless 配置无法加载本插件;不能据此宣布所有 headless 配置可用
- 已运行的宿主回归:使用仓库的临时 Loader 配置和可重启 JSONL 配置,加载真实 rc.2 宿主组件;最近活动排序测试另加载真实
sessionQuery。这些是测试专用配置,模型请求与人工审批答复使用脚本替代,不是完整的官方 Web 或 Desktop profile 验收
服务出现在官方配置中,与本插件已在该完整配置中通过验收,是两件事。下方测试结果不覆盖完整 Web 浏览器或 Desktop UI 的端到端使用,也不构成其他宿主配置或 DSH 0.2 的兼容承诺。
使用限制
- 不会顺手新建工作区。
workspacePath必须是已经存在的那个工作区的路径;写错了它会报错,并把现有的工作区列出来给你挑。 - 不能给子代理发消息。目标必须是普通会话;AI 派出去的子代理要用
send_message。也不能给当前这个会话自己发。 - 关掉的会话要有工作目录才能唤醒。发消息时如果目标会话没开着,插件会把它重新打开;但如果那条会话没记录工作目录,就打不开、发不进去,会明确告诉你原因。
- 重启期间收不到。新建会话是立刻生效的;发出去的消息要等对方会话被打开处理,所以 DSH 得开着。等待也一样,DSH 关了就没法等。
- 归属定了就不能改。会话归到哪个工作区是记在账上的,建完之后没法在工作区之间搬。
开发与回归检查
测试基线固定为 DSH 0.1.5-rc.2。使用 Node 22.19.0(22 系列最低支持版本)或 Node 24:
npm ci --ignore-scripts --no-audit --no-fund
npm run check
package-lock.json 固定完整依赖图;开发依赖和 overrides 把宿主组件固定在同一 rc.2 基线,避免上游宽松 peer 范围混入其他预发布版本。安装无需生命周期脚本,仓库 .npmrc 也将其禁用。CI 在 Node 22.19.0 和 24 上各自执行干净安装、完整测试和语法检查,某一版本失败不会取消另一版本。
消息回执增加真实收件箱、轮次终态、JSONL 冷读、投影检查点、宿主重新加载和恢复修复回归;模型内容仍是脚本边界。
会话排序回归覆盖真实 sessionQuery 的创建时间顺序与对话时间顺序不同、仅改标题、稳定同值排序、limit=1、崩溃恢复与真实取消、重启冷读、日志追加/缩短/替换/不完整尾部、缓存失效和读句柄清理。node --test test/session-activity*.test.mjs 可单独运行;旧日志开销测试会输出本次环境的实际耗时和读取次数。
测试分层:
- 策略和边界单元测试:用小型服务替身覆盖输入校验、权限组合、并发和取消时序
- 真实宿主集成:临时配置经官方 Cordis Loader 加载真实工具运行时、AgentLoop、审批、工作区和 JSONL 持久化;仅模型请求和人工审批答复使用脚本边界,不发送真实网络请求或用户消息
- 发布物回归:运行
npm pack,从解包后的入口经 Loader 加载插件;读取磁盘中的创建结果,用打包的客户端、官方 SlotCore 与真实 React 验证卡片文字和导航回调。这是无浏览器渲染测试,不宣称覆盖完整桌面 UI - 资源清理:测试拥有并关闭临时目录、Agent、读句柄和计时器;默认等待预算还通过独立子进程正常退出回归,不能依赖强制结束进程来通过
首次消息投递前的取消回归用服务替身检查 detachSession / dispose 调用;真实宿主还覆盖了标题清理后没有可见内容、因而创建失败且未投递消息的路径。这些测试没有覆盖真实持久化写入或关闭失败的故障注入,不能证明所有失败都会完全回滚。
目前没有把上述结果外推为 DSH 0.2 兼容承诺;升级宿主基线需要另行验收。