Files
deepseek-harness/.agents/notes/implemented/architecture/2026-08-13-session-content-search-opt-in.md
T
Hypatia May b6b6a72df7 feat(session-query): ship full-text session search opt-in via openAt never
The shipped bundles keep ctx.sessionQuery mounted but set the new
session-query-sqlite `openAt: never` phase: searchSessions/searchEvents
fail with the typed SESSION_QUERY_SEARCH_DISABLED code before any request
normalization, node:sqlite is never imported or opened, and no source
observation or reconciliation runs. Every inherited exact read, filter,
and trace — session export descendants, subagent-fork Workspace
inheritance, title reads — keeps working, and the Web sidebar search
degrades to its designed local title/workspace matching. Enabling content
search is a one-line openAt override in a later patch layer; the web e2e
scaffold keeps it enabled as the assembled opt-in coverage.
2026-08-13 11:46:37 +08:00

4.1 KiB

Agent Note: Session content search ships opt-in through openAt never

Status: implemented

English | 中文

Problem

The shipped bundles mounted the SQLite session-query provider with the full-text index live (openAt: first-search), so every default deployment carried a derived FTS index and the Web sidebar offered content search. Whether a deployment wants that index — its node:sqlite import, per-search source reconciliation, and derived storage — is a deployment choice, and the product default is to ship without it; the model-facing search tools were already opt-in and unmounted (the not-shipped-default decision).

Turning the capability off by unmounting the plugin row is not viable. ApiProxyService declares sessionQuery as a required injection, so without the provider the whole host API gateway stays unloaded and the Web GUI never boots. Session-log export traces subagent descendants through ctx.sessionQuery.traceSession, and a subagent fork resolves its Workspace through the same lineage trace — both would need optional-service guards plus a replacement lineage source, roughly tripling the change surface while losing exact reads everywhere.

Decision

Content search is enforced off at the provider. openAt: 'never' is a third opening phase on @deepseek-ai/dsh-session-query-sqlite: searchSessions and searchEvents fail with the typed SESSION_QUERY_SEARCH_DISABLED code before any request normalization, node:sqlite is never imported or opened, and no source observation or reconciliation runs. Every inherited ctx.sessionQuery exact read, filter, and trace keeps working, so session export, fork Workspace inheritance, and title reads are unaffected.

SESSION_QUERY_SEARCH_DISABLED joins the closed SessionQueryErrorCode taxonomy, and the tool-session-query service boundary maps it to the model-safe message session search is disabled in this deployment.

The base bundle sets openAt: never on the session-query-sqlite row and the web bundle's restatement keeps it; enabling content search is a one-line openAt override (first-search or startup) in a later patch layer, typically with a durable path. The host session.search endpoint reports the provider failure through its existing error path, and the Web sidebar keeps its designed degradation: local title/workspace matching plus the content-search-unavailable notice. The CLI compat spec pins the shipped openAt: never rows, while the web e2e scaffold keeps content search enabled — its seeded-session scenarios navigate by content search, and those runs are the assembled coverage for the opt-in path.

Alternatives considered

  • Unmount the plugin row (disabled: true in the base patch): rejected — the api-gateway's required sessionQuery injection keeps the whole host API unloaded, and making that injection optional forces guards plus a header-walk lineage fallback in session export and fork resolution.
  • Disable at the consumers (the host session.search endpoint or the sidebar): rejected — enforcement belongs to the operation that makes the decision; opt-in model tools or any other consumer would still reach the index.
  • A separate boolean beside openAt: rejected — the opening phase already owns when SQLite starts; never extends the same axis instead of adding a second knob that can contradict it.

Consequences

  • Default deployments run no derived index: no node:sqlite import or experimental-SQLite startup warning, no reconciliation work, no derived database on disk. Sidebar search matches session titles and workspace names only.
  • Search failures under the default are typed and stable rather than incidental, so callers distinguish a deployment choice from an index fault (SESSION_QUERY_INDEX_FAILED).
  • Re-enabling content search is per-deployment configuration, not a code change, and restores the full FTS behavior unchanged.
  • Compositions that mount the search tools without overriding openAt get the model-safe disabled message on every search call; enabling the tools implies enabling the index.