description: "The SQLite FTS5 full-text search backend for session history, for deployments and maintainers choosing, configuring, or debugging full-text search over the query service."
English | 中文
dsh-session-query-sqlite searches session history with a SQLite FTS5 index and returns ranked, cursor-paginated results grouped by session or within one session. Mount it together with dsh-session-query and you get full-text search plus the whole query surface — exact reads, filters, and traces — at once. Live sessions are indexed from memory and persisted sessions from a dedicated derived-index database, so results always reflect the newest state without touching the session-persistence store. Search is opt-in and off by default in shipped compositions: openAt decides whether the index opens at startup, at the first search, or never. Setup and usage come first; the implementation internals live in a collapsible developer section below.
Mount this package when a composition needs ranked full-text search over session history — for example the Web content search or /resume prior-work retrieval. The common path is explicit: mount the plugin, give it a dedicated database path, and call ctx.sessionQuery.searchSessions or searchEvents from code.
Choose it when you want full-text recall over prior sessions with ranking and paging. Choose it together with dsh-session-query and the session service; a persistence backend is optional but recommended so persisted history is searchable after restarts. Avoid pointing path at the session-persistence database — this package owns a separate derived index.
- name: '@deepseek-ai/dsh-session'
- name: '@deepseek-ai/dsh-session-query-sqlite'
config:
path: /absolute/path/to/session-search.db
| Field | Default | Meaning |
|---|---|---|
path |
required | Dedicated derived-index SQLite path, or :memory:; missing paths are created owner-only on POSIX |
openAt |
startup |
startup opens at activation; first-search defers the SQLite module until the first search; never disables full-text search while inherited reads stay available |
journalMode |
wal |
wal, delete, truncate, or persist |
defaultLimit |
20 |
Page size when a request omits limit |
maxLimit |
100 |
Largest accepted request page size |
snippetChars |
240 |
Maximum snippet length in Unicode code points |
readWindowMax |
50 |
Maximum before/after raw events for the inherited readEvent() |
persistedReadConcurrency |
4 |
Concurrent persisted-log reads for inherited batch reads |
preparedSessionCacheSize |
5 |
Cold prepared-Session observations the inherited observeSession reader retains for reuse |
The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.
searchSessions searches the whole corpus and groups results by each session's strongest matching event; searchEvents searches one logical session. Queries are literal phrases: they are trimmed and whitespace-normalized, and FTS5 syntax such as quotes, OR, NEAR, and * is treated as data, never as executable query syntax. Metadata filters (session id, cwd, created-at, parent, availability, event seq/time/type/surface) narrow results before ranking. All current, shadowed, and log-only events are searchable by default; pass a surface filter to narrow.
Ranking is deterministic: more actual FTS5 highlighted-match spans first, then shorter documents, with event time, session id, and seq breaking ties. Results carry plain-text snippets bounded by snippetChars Unicode code points, with no provider-specific numeric score. Pages continue through an opaque SessionSearchCursor bound to the exact normalized request; a cursor becomes stale when its relevant corpus changes (SESSION_QUERY_STALE_CURSOR), and a within-session cursor survives changes to unrelated sessions while a cross-session cursor does not.
The unicode61 tokenizer matches tokens and phrases, not arbitrary substrings: AI does not match the token BRAID. Use ctx.sessionQuery.filterEvents() with a text clause when a literal whitespace-flexible substring scan is required.
With openAt: first-search, the service activates without importing node:sqlite or opening the index, deferring SQLite's experimental warning until the first actual search; an invalid database fails that first search instead of service activation. With openAt: never, full-text search is off for the deployment: searchSessions and searchEvents fail with SESSION_QUERY_SEARCH_DISABLED before any request normalization, while every inherited exact read, filter, and trace keeps working. Requests that exceed the compiled-predicate budget (14 combined predicates across sessions, 13 within a session) or SQLite's portable 32,766-binding limit fail with SESSION_QUERY_INVALID_FILTER before statement preparation.
Typed SessionQueryError failures carry stable codes: SESSION_QUERY_SEARCH_DISABLED when search is configured off; SESSION_QUERY_INDEX_FAILED when the index cannot open or reconcile; SESSION_QUERY_SESSION_NOT_FOUND when a search target is absent; SESSION_QUERY_STALE_CURSOR when the corpus changed between pages — retry the complete search call; and SESSION_QUERY_INVALID_CURSOR for a cursor that does not belong to this request. Cancellation is honored between synchronous SQLite calls; a statement already executing on the JavaScript thread cannot be interrupted.
Read these pages when the package-level contract is not enough. They move from the shared query service to the type-level contract and the design evidence.
None, as the search backend returns hits only to callers and registers nothing model-facing.
None; this package neither assembles nor sends a provider request.
These limits define when this package is a poor fit or needs special operational care. They are current package constraints, not a general SQLite comparison or a task backlog.
DatabaseSync blocks the JavaScript thread during MATCH execution and cannot interrupt a statement already running.unicode61 tokenizer does not match substrings inside a larger token; use filterEvents() for literal scans.