会话
一个会话就是你和某个工作区的一段对话:问了什么、答了什么、跑了哪些工具。ZettCode 边跑边写, 正常退出不会删除已经保存的会话。
存在哪里
所有工作区共用一个存储根目录,先按工作区分组:
~/.zettcode/sessions/<工作区键>/
├── metadata.jsonl 索引:标题和活跃时间
└── <会话 id>/
└── data.jsonl 对话本身<工作区键> 是工作区绝对路径的 URL-safe base64,所以同一个项目的两份 checkout 不会共用一个 目录,路径也不会跑出存储根之外。
data.jsonl 是只追加的 —— 每条记录都写着它的父节点,于是对话是一棵树而不是一个列表。 metadata.jsonl 是会话列表读的小索引,所以列会话列表从来不需要解析对话内容。崩溃可能截断最后 一行,那一行会被忽略;而其他位置损坏的行会报错,不会被悄悄丢掉。
为什么是树
唯一的写操作就是追加,所以保存一轮对话不会重写历史。它还顺带带来两个你能感知到的好处: 恢复就是从最新一条往前走到根,压缩是往树里加一条摘要、让后续请求在那里截断,而不删掉 任何东西。
离开与回来
退出时会打印回来的路:
resume this session: zettcode --resume 01a10b75 --workspace ~/projects/api在程序里,/resume 会弹出这个工作区最近的会话(新的在前,带标题和上次变动距今多久); /resume <id> 跳过面板直接打开。你当前在哪个会话,状态行里写着。
/new 开一个新会话,当前对话留在磁盘上。/clear 只是清屏,会话继续,模型仍然记得之前的轮次。
例子:接着昨天的工作做
在同一个工作区启动,然后选择之前的会话:
zettcode -w ~/projects/api在输入框中输入 /resume,用 ↑ / ↓ 选中目标对话,再按 Enter。 之前的消息会替换当前 transcript,接着就可以说“从上次失败的测试继续”。
已经知道 id 时,可以跳过列表:
zettcode --workspace ~/projects/api --resume 01a10b75把路径和 id 换成自己的。直接运行 zettcode 默认开始新会话,不会自动恢复上一条。 如果找不到 id,先检查工作区:即使文件相同,两份 checkout 的会话列表也是分开的。
标题
第一次回答之后,代理会用一次很小的模型调用生成一个四五个词的标题,追加到 metadata.jsonl。 会话面板和状态行显示的就是它。
/title users endpoint 可以自己命名,而且这个名字会保留:自动命名永远不会覆盖已经有标题的 会话,所以你在第一条消息之前起的名字不会被冲掉。
例如,输入 /title 修复 users 分页。不带参数的 /title 会报告当前标题。 没有标题的会话显示为 New session,及时起名能让较长的列表更容易查找。
导出
/export report.html 会把整个会话 —— 每一轮、工具调用、耗时、压缩摘要,以及当前的上下文 —— 写进一个独立的 HTML 文件,可以直接在浏览器里读、作为记录保留、或贴进 review。不带路径时, 文件落在工作区里,名字是 zettcode-<会话 id>.html,并告诉你位置。
页面左侧是一列轮次,右侧是选中轮次里的事件:每一条都带着当初写入的原文、写入时间,以及它的 开销。长内容(系统提示、成片的工具输出)默认折叠,展开后在自己的区块里滚动,不会把页面撑长。 侧栏的最后一项是当前上下文,所以这个文件同时回答了“说过什么”和“模型现在带着什么”; 压缩过的会话会在那里显示 checkpoint,重复出现的系统指令只做指向而不再重复打印。
导出的页面不需要网络也不需要服务器:样式和脚本都内联在里面。
例子:保存并分享报告
在 ZettCode 内输入 /export review.html,再从文件管理器或浏览器打开提示中的文件。 相对路径以工作区为基准;绝对路径可以保存到其他目录,必要时会创建父目录。
/btw 交流不会进入导出的历史或模型上下文,但恢复会话时仍会显示在 transcript 中。 HTML 是当时的快照,之后继续对话不会自动更新它,也不能用 /resume 把 HTML 导入成会话。
分享前先检查
导出可能包含源码、提示词、文件路径和带有私人信息的工具输出,发给别人前应先检查。 如果想备份可恢复的会话,应复制这个工作区对应的会话存储目录。