Skip to content

session query 提供什么

session-query 家族提供独立于压缩之外、对实时与持久会话日志的授权检索。它回答代理或人类可能想要的两类事情:

  • 搜索——通过全文搜索按会话(跨语料)或按事件(会话内)查找先前的工作。
  • 追踪与读取——跟随谱系(某会话的父级与后代)、检查确切事件、读取 surface,并导出原始日志。

这一切的事实来源是每个会话的会话日志,由 packages/session/session-persistence-jsonl 持久化为持久的原始历史。查询是这条日志的消费者ctx.sessions 实时加上动态挂载的 ctx.sessionPersistence);它从不写入日志。

包家族

角色ctx key
session-queryService Definition:可信读取、关系查询、过滤、搜索契约ctx.sessionQuery
session-query-sqlite具体 provider:SQLite FTS5 全文搜索、对账、分页ctx.sessionQuery
tool-session-query面向模型、受工作区授权的搜索/追踪/读取工具注册到 ctx.tools
session-log-exportWeb /export 命令、共享下载状态、结果弹窗ctx.sessionLogDownload
session/session-persistence-jsonl事实来源的 JSONL 持久日志后端ctx.sessionPersistence

服务契约(dsh-session-query

SessionQueryEngine 是组合后的抽象 ctx.sessionQuery 契约。它实现精确的会话历史检索、关系追踪与 provider 无关的过滤;具体后端实现它的两个全文方法。匹配的 id 产生一条记录——实时事件取胜——而 livepersisted 同时报告两种来源的可用性;冲突的不可变 header 以 SESSION_QUERY_SOURCE_CONFLICT 失败。

读取操作

读取用途
listSessions(signal?)克隆合并后的逻辑语料,最新在前。
readSession(sessionId)一条完整的分离原始日志,采用与 resume 相同的核心重放校验;绝不进入实时存储。
filterSessions(filters, signal?)对克隆语料应用会话元数据谓词。
filterEvents(sessionId, filters)提取第一方语义文档并按升序 seq 进行过滤。
listEvents(sessionId)把每个事件分类为 currentshadowedlog-only
readSurface(sessionId)克隆 header + 以模型历史顺序折叠完成的当前 surface。
readEvent(request, signal?)克隆 header + 完整目标事件 + 有界的原始 seq 窗口。
traceSession(sessionId)从内到外的祖先加上确定性的递归后代树。
traceEvent(request)克隆 source header,带直接的位置替换与被引的 source 链接。
readTitleSnapshots(sessionIds) / readTitle(sessionId)面向 UI 的逐会话标题观测。

listSessions() 保持轻量——它不加载日志也不索引标题。持久化是可选的,可以动态挂载/卸载;针对已知实时会话的标题/事件/trace 读取不会咨询持久化,因此持久后端的健康问题不会让当前内存状态不可读。

过滤器

SessionResultFilter 覆盖 id、可空 cwd、创建时间范围、可空父级与来源可用性。SessionEventResultFilter 覆盖 seq/时间范围、事件类型、surface 与语义文本。过滤器数组内部按 AND 合并;单一条款内的值按 OR 合并;空列表值不匹配任何东西;格式错误的范围以 SESSION_QUERY_INVALID_FILTER 失败。文本条款刻意是一种字面语义文本扫描,而非全文查询:调用者文本被转义成一个 Unicode、大小写不敏感的正则,其中每个空白段匹配一个或多个空白字符。

两个抽象搜索方法

searchSessions(request, exec?) 按最强匹配事件对逻辑语料分组;searchEvents(request, exec?) 搜索单个逻辑会话。两者都返回其续接为一个自有的 branded SessionSearchCursor 的页,接受可选取消,并暴露不带 provider 专属数字分数的片段。搜索请求只接受元数据事件过滤器。

SessionQueryError.code 是一个封闭联合:SESSION_QUERY_ABORTED…_CORRUPT_SESSION…_EVENT_NOT_FOUND…_INDEX_FAILED…_INVALID_CONFIG…_INVALID_CURSOR…_INVALID_FILTER…_INVALID_LIMIT…_INVALID_QUERY…_INVALID_LINEAGE…_INVALID_SURFACE…_INVALID_WINDOW…_PERSISTENCE_FAILED…_SEARCH_DISABLED…_SESSION_NOT_FOUND…_STALE_CURSOR…_SOURCE_CONFLICT

配置默认契约
readWindowMax50before/after 原始事件计数上限。
persistedInspectConcurrency4一次批量读取中并发持久日志检查的上限。

SQLite provider(dsh-session-query-sqlite

SqliteSessionQueryEngine 继承确切的读取、追踪与过滤器,并以 SQLite FTS5 实现两个全文方法。查询是必填、经 trim、空白归一化的字面短语——ORNEAR* 等 FTS5 语法被当作数据,而非可执行的 MATCH 语法。元数据过滤器是在排序之前应用的参数化 SQL 谓词。

相关性在持久表与 TEMP 表之间可比较:实际 FTS5 高亮匹配跨度数降序,然后是存储文档码点长度升序;事件时间、会话 id 与 seq 打破平局。片段是空白归一化的纯文本,以 Unicode 码点限界(snippetChars,默认 240)。游标绑定到归一化后的请求与服务实例,并在相关代变化时失败。

一个串行化的状态机比较带来源限定的轻量持久快照修订,非变更性地只检查新增/已变日志,提取共享语义文档,事务性地对账,然后执行查询。持久 FTS 行位于专用派生数据库中;连接本地 TEMP 表持有实时行,为同一会话遮蔽持久基表。

配置默认契约
path必填专用派生索引 SQLite 路径;支持 :memory:
openAtstartupstartup / first-search / never
journalModewalwal / delete / truncate / persist
defaultLimit20请求省略 limit 时的页大小。
maxLimit100接受的最大请求页大小。
snippetChars240片段的码点长度上限。
readWindowMax50 / persistedInspectConcurrency4——继承的读取配置。

分词器是 FTS5 unicode61:这是token 识别(token recall),不是任意子串识别——AI 不匹配 token BRAID;需要字面扫描时请用带 text 条款的 filterEvents()。Node 的同步 DatabaseSync 在 MATCH 执行期间会阻塞 JavaScript 线程,且无法中断正在运行的语句。

面向模型的工具(dsh-tool-session-query

这个可选包注册 session_searchsession_event_searchsession_tracesession_event_tracesession_event_read。它默认挂载到发货的宿主组合中。

配置默认含义
maxSearchResults100跨内部 provider 页收集的授权非自身命中上限。
searchTimeoutMs30000附加到两个全文搜索工具的协作式截止时间。

调用者只来自 ToolExecution.exec.agent跨会话访问要求目标会话与调用者会话的 cwd 值精确相等;没有 cwd 的调用者只能检查自身。搜索从不暴露 provider 游标或模型可控的限制。session_search 总是省略调用者会话;当前会话的 session_event_search 在调用它的 step 之前停止,因此活跃的 assistant 输出与被记录的 tool call 不能匹配自身。谱系输出会把未经授权的祖先/后代边界替换为不含任何隐藏会话 id 的标记。该包刻意不做任何字节或字符截断,也不导入 spill 后端;需要约束内联输出的部署自行挂载 @deepseek-ai/dsh-spill-policy

日志导出(dsh-session-log-export

Web /export 命令与 Session log 头部动作通过宿主流式端点 GET /api/session.export?sessionId=<id>&includeDescendants=true 把原始会话日志下载为 ZIP。宿主半部(在 packages/host/apiproxy 中)拥有 ZIP 生成、原始 JSONL/zstd 读取、后代、附件、背压与 HTTP 错误语义;这个 Web 包拥有按钮、一个下载控制器、共享弹窗以及触发浏览器下载的 command/executed 确认。

/export 记录一条人类命令生命周期;/export <path> 返回错误(浏览器下载通过浏览器普通的下载行为选择目的地)。宿主端点在 readRaw 之前刷新一个实时的根会话,因此由斜杠命令触发的 ZIP 包含其确认触发下载的那一对 command/run + command/done

JSONL 事实来源(session-persistence-jsonl

每个会话拥有的持久日志默认是一个 Zstandard 压缩的 JSONL 文件,布局为 <root>/--<normalized-cwd>--/<encoded-id>/session.jsonl.zstd。第一逻辑行是不可变的 SessionHeader(带 type: 'session',且 seq 保持连续——events[i].seq === i);每个后续行是一条存储记录,或针对一段 ≥3 个同块 assistant/chunk 增量的打包分块行packChunks 默认 true)。存储是追加式的且具备崩溃修复:不完整的尾部帧会被截断,并以合成的 tool/step/turn 闭合符重新开放。查询引擎通过一个 SessionPersistence 接缝读取同一持久语料,因此搜索、追踪与导出一致地看到同一事实来源。

已知限制

  • 服务内部无调用者授权——ctx.sessionQuery 是可信的上下文级基础设施;只有工具exec.agent + cwd 相等)与 Web 动作限制访问。
  • token 识别,而非子串——字面扫描请用 filterEvents()
  • 单一拥有者的派生索引——每个 SQLite path 必须只由某个服务/进程拥有。
  • 搜索限制在部署上限——没有续接 token;工具会请模型收窄查询。

延伸阅读

  • 子代理工作流与 Ralph——兄弟家族;session_query 工具让它们的代理恢复先前的工作。
  • 术语表——turnstepround 界定了 query 事件的含义。
  • 仓库内的子系统参考:docs/subsystems/session-query.md
  • README:packages/session-query/session-query/README.mdpackages/session-query/session-query-sqlite/README.mdpackages/session-query/tool-session-query/README.mdpackages/session-query/session-log-export/README.md
  • 持久日志格式:packages/session/session-persistence-jsonl/README.mdsrc/format.ts
  • Agent Note:.agents/notes/implemented/feature/2026-07-10-sqlite-session-query-provider.md2026-07-13-session-query-tracing.md