DSH / Atlas
2026-07-10implementedfeaturearchived 2026-07-27

Exact session query service

精确会话查询服务

Session history exists in two places: current `SessionStore` objects and an optional persistence backend. Consumers that need exact inspection would otherwise duplicate live-versus-persisted precedence, persistence lifecycle handling, raw-event surface classification, relationship tracing, and defensive cloning. Durable state can lag the live log between checkpoints, so persistence alone is not a truthful current sou

English

Problem

Session history exists in two places: current SessionStore objects and an optional persistence backend. Consumers that need exact inspection would otherwise duplicate live-versus-persisted precedence, persistence lifecycle handling, raw-event surface classification, relationship tracing, and defensive cloning. Durable state can lag the live log between checkpoints, so persistence alone is not a truthful current source.

Full-text search is related but materially larger. Putting provider coordination, synchronization, invalidation, ranking, and cursor state into the exact-read service would create a second state machine beside the concrete database owner.

Decision

@deepseek-ai/dsh-session-query owns the single abstract ctx.sessionQuery service over one logical corpus. It concretely implements listSessions(), provider-independent filterSessions(filters), listEvents(sessionId), filterEvents(sessionId, filters), bounded readEvent(request), traceSession(sessionId), and traceEvent(request), while concrete backends implement its two full-text methods. The unified service decision owns that topology, the SQLite search decision owns search behavior, and the tracing decision owns lineage and event-relationship semantics.

The service observes the optional ctx.sessionPersistence binding dynamically but retains no persisted cache or invalidation listener. Each cross-corpus list asks the active backend for authoritative metadata, then overlays a fresh live-store list. Matching ids become one SessionRecord: the live header wins and live/persisted independently report source availability. Immutable header disagreement is SESSION_QUERY_SOURCE_CONFLICT.

An exact target read first checks the live store and snapshots the live header and event log. This path never consults persistence, so a failing durable backend cannot make known live history unreadable. With no live target, the service lists current persistence metadata, proves the id exists, loads it, and rejects a list/load header mismatch. All returned headers and events cross one structured-clone boundary.

Surface semantics

dsh-session exports foldSurface(events), and SurfaceManager uses the same transition functions for its incremental cache. The fold returns detached current event sequences and each replacement's actual removed seqs. listEvents() and traceEvent() use that result to classify every raw event, so inspection cannot disagree with model-history derivation about positional replacement semantics.

readEvent() returns the complete target plus raw neighbors by contiguous seq. before and after default to zero and are independently bounded by readWindowMax, default 50. The result carries a cloned SessionHeader, not a source-availability record, because determining a live target's persisted flag would violate the guarantee that live exact reads do not depend on persistence health.

Security boundary

The service is context-wide trusted infrastructure, not an authorization layer. A future model-facing history tool or human UI applies explicit caller/session scope. The service adds no model-facing tool and changes no transcript or snapshot surface.

Alternatives considered

  • Put logical-corpus resolution directly in every consumer — rejected because source precedence, conflicts, optional-service lifecycle, cloning, and surface classification are shared correctness rules.
  • Query only persistence — rejected because checkpoints can lag the current live log.
  • Cache persisted metadata and listen for writes/removals — rejected because exact reads can ask the authoritative sources directly, while cache invalidation adds lifecycle and concurrency state before scale requires it.
  • Put provider registration into the exact-read service — rejected because the SQLite package owns one reconciliation/transaction lifecycle; a registry would split that state without a second provider to justify it.

Consequences

The inherited exact-read implementation has one source-resolution state variable: the currently mounted persistence service. It has no provider queues, fingerprints, extractor registries, observation generations, or derived index updates; a concrete backend owns its full-text state separately. Exact reads, semantic scans, and event traces remain usable in live-only deployments and deterministic when persistence is present.

Cross-corpus listing, lineage tracing, and persisted event operations perform backend I/O on each call. That is deliberate: correctness comes from current authoritative state, while scale-oriented full-text methods use the concrete backend's SQLite derived index.

中文

问题

会话历史存在于两处:当前的 SessionStore 对象与可选的持久化后端。需要精确检查的消费方若无统一服务,就不得不各自重复实现活跃/持久化优先级判定、持久化生命周期处理、原始事件的 surface 分类、关系追踪以及防御性克隆。在检查点之间,持久化状态可能落后于活跃日志,因此仅靠持久化并非当前状态的可靠来源。

全文搜索与此相关,但规模大得多。将提供方协调、同步、失效、排序和游标状态放入精确读取服务,会在具体数据库拥有方旁边再创建一个状态机。

决策

@deepseek-ai/dsh-session-query 拥有面向单一逻辑语料库的唯一抽象 ctx.sessionQuery 服务。它具体实现 listSessions()、提供方无关的 filterSessions(filters)listEvents(sessionId)filterEvents(sessionId, filters)、有界的 readEvent(request)traceSession(sessionId)traceEvent(request),而具体后端实现其两个全文搜索方法。统一服务决策拥有这一拓扑,SQLite 搜索决策拥有搜索行为,追踪决策拥有血缘与事件关系语义。

该服务动态观察可选的 ctx.sessionPersistence 绑定,但不保留持久化缓存或失效监听器。每次跨语料库列表操作向活跃后端请求权威元数据,然后叠加一份新鲜的活跃 store 列表。id 匹配的条目合并为一条 SessionRecord:活跃 header 优先,live/persisted 各自独立报告来源可用性。不可变 header 不一致时产生 SESSION_QUERY_SOURCE_CONFLICT

精确目标读取首先检查活跃 store,快照活跃 header 与事件日志。此路径从不查询持久化,因此持久化后端故障不会导致已知的活跃历史不可读。若活跃 store 中无目标,服务列出当前持久化元数据、证明该 id 存在、加载它,并在列表/加载 header 不一致时拒绝。所有返回的 header 与事件都经过一次 structured-clone 边界。

Surface 语义

dsh-session 导出 foldSurface(events)SurfaceManager 使用相同的转换函数维护其增量缓存。fold 返回分离的当前事件 seq 以及每次替换实际移除的 seq。listEvents()traceEvent() 利用该结果为每个原始事件分类,使检查结果不会在位置替换语义上与 model-history 推导产生分歧。

readEvent() 返回完整的目标加上按连续 seq 排列的原始相邻事件。beforeafter 默认为零,各自受 readWindowMax(默认 50)约束。结果携带克隆的 SessionHeader 而非来源可用性记录,因为判断活跃目标的 persisted 标志会违反「活跃精确读取不依赖持久化健康状态」这一保证。

安全边界

该服务是上下文级别的受信任基础设施,而非授权层。未来面向模型的历史工具或人类 UI 将施加显式的调用方/会话范围。该服务不添加面向模型的工具,也不改变 transcript(文本记录)或快照的 surface。

曾考虑的替代方案

  • 将逻辑语料库解析直接放在每个消费方中:否决。来源优先级、冲突处理、可选服务生命周期、克隆与 surface 分类是共享的正确性规则。
  • 仅查询持久化:否决。检查点可能落后于当前活跃日志。
  • 缓存持久化元数据并监听写入/删除:否决。精确读取可以直接询问权威来源,而缓存失效在规模尚未要求时就引入了生命周期与并发状态。
  • 将提供方注册放入精确读取服务:否决。SQLite 包拥有一套对账/事务生命周期;若没有第二个提供方证明其必要性,注册表只会拆分该状态。

后果

继承的精确读取实现只有一个来源解析状态变量:当前挂载的持久化服务。它没有提供方队列、指纹、提取器注册表、观察代次或派生索引更新;具体后端单独拥有其全文搜索状态。精确读取、语义扫描和事件追踪在纯活跃部署中仍然可用,在持久化存在时具有确定性。

跨语料库列表、血缘追踪和持久化事件操作在每次调用时执行后端 I/O。这是有意为之:正确性来自当前权威状态,而面向规模的全文搜索方法使用具体后端的 SQLite 派生索引。