结论:能接着上一轮说。但会话 id 不能自己起,第一轮得用 --json 从首行事件里捞;它还认目录,换了目录就续不上。

前两期分别写过它怎么装、怎么把结果接走。这期只回答一个问题:无头模式每次跑完就退出,第二次调用还记不记得第一次说过什么。

本次实测

环境:WSL2(Ubuntu)、Node 22.23.2、dsh 0.2.0-rc.2。沙箱 /tmp/dsh_20261016/,每条命令都套 timeout 280。

# 测试项 命令要点 结果 耗时
1 自己给会话起名字 --session-id myjob exit 1,报 session "myjob" does not exist 2.2 s
2 跑一轮并取出 id --json,新会话 exit 0,stdout 首行 {"type":"session","sessionId":"session-3337…"} 9.5 s
3 埋一个暗号 --json,不碰任何文件 exit 0,末行 final = 「记住了」 3.8 s
4 同 id 再调一次 --session-id session-6e45… exit 0,事件里 "turn":2,答出「青竹七号」,全程没读文件 3.9 s
5 同上,去掉 --json 普通输出 exit 0,stdout 13 字节,就是暗号 5.8 s
6 换目录续跑 cd /tmp 后同 id exit 1,报 was recorded in "/tmp/dsh_20261016", not "/tmp" 2.6 s

第 3、4 步是这期的关键。第 3 步只让它记住一个自己编的暗号,不写任何文件;第 4 步用同一个 id 再调一次,问暗号是什么。它答对了。答案不在磁盘上,只可能在上一轮的会话里。

会话记录落在 ~/.dsh/sessions/ 下面。一级目录是工作目录转义出来的名字(/tmp/dsh_20261016 → --tmp-dsh_20261016--),再往下一层,每个会话一个文件夹,里面是 session.v4.jsonl.zstd 加一把 session.lock。上面那个会话跑了两轮,文件 26 KB。第 4 步的事件里还带着用量:219 个新输入 token、5376 个缓存读取 token。

会话也不自己清理。~/.dsh/sessions 下还留着 10 月 9 日那次测试的沙箱目录,整个目录现在 376 KB,session.lock 是 0 字节。

第 2 步那次会话比第 3 步慢了一倍多(9.5 秒对 3.8 秒),差在动作上:事件流里它先发了一次 bash 去确认目标文件不存在,第二次才走 write 落盘;第 3 步只回一句话,没有工具调用。

一条坑:id 得捞,目录得对

现象:想给会话起个固定名字方便脚本引用,--session-id myjob 直接退 1,提示这个 id 不存在。它只认已经存在的会话。

复现:

sh dsh --profile headless --session-id myjob "随便说一句"

解法:第一轮照常跑,加上 --json,从 stdout 第一行把 id 抠出来存着。我拿第 3 步的输出验过这一行能正确取到 session-6e45…:

sh sid=$(timeout 280 dsh --profile headless --json "$TASK" | head -1 | sed 's/.*"sessionId":"\([^"]*\)".*/\1/') timeout 280 dsh --profile headless --session-id "$sid" "继续"

不带 --json 时 stdout 只有答案(13 字节),stderr 里只有推理文本,两边都不给 id,所以这个 id 只能从事件流里拿。

第二件事同样会绊人:id 对了、目录错了也不行(第 6 步)。会话按工作目录归档,脚本中途 cd 过,就得 cd 回来再续。

一句话总评

4.3 / 5,只评「跨进程记住上下文」这一项。 续跑确实省掉复述:第二次调用用 219 个新输入 token 就接上了上一轮的暗号,比把上下文重贴一遍划算。扣分全在 id 上,--help 只写了 "an unknown id is an error",没说 id 从哪来,也没提会话绑目录,这两件事都得撞一次才知道。

测试环境:WSL2(Ubuntu)、Node 22.23.2、dsh 0.2.0-rc.2。命令、输出片段、耗时、退出码均为本机实测。

文 / 程实