@deepseek-ai/dsh-session-query-sqlite 0.1.1-rc.1 → 0.1.2-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +128 -25
- package/README.zh.md +134 -31
- package/package.json +15 -14
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/session-query/session-query-sqlite/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 5c839ebca39c6646122d1061592a9ac75b468e67
|
|
6
|
+
README.zh.md: e3eaa2196b32f4ca553e6278e5598e92730b2ea5
|
package/README.md
CHANGED
|
@@ -1,49 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
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."
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-session-query-sqlite
|
|
2
7
|
|
|
3
8
|
English | [中文](README.zh.md)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
`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.
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Use this package](#use-this-package)
|
|
17
|
+
- [Understand the implementation](#understand-the-implementation)
|
|
18
|
+
- [Further Exploration](#further-exploration)
|
|
19
|
+
- [Model Experience](#model-experience)
|
|
20
|
+
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
|
|
21
|
+
- [Dev Note](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## Use this package
|
|
27
|
+
|
|
28
|
+
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.
|
|
29
|
+
|
|
30
|
+
### When to choose it
|
|
31
|
+
|
|
32
|
+
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.
|
|
33
|
+
|
|
34
|
+
### Minimal configuration
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
- name: '@deepseek-ai/dsh-session'
|
|
38
|
+
- name: '@deepseek-ai/dsh-session-query-sqlite'
|
|
39
|
+
config:
|
|
40
|
+
path: /absolute/path/to/session-search.db
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| Field | Default | Meaning |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `path` | required | Dedicated derived-index SQLite path, or `:memory:`; missing paths are created owner-only on POSIX |
|
|
46
|
+
| `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 |
|
|
47
|
+
| `journalMode` | `wal` | `wal`, `delete`, `truncate`, or `persist` |
|
|
48
|
+
| `defaultLimit` | `20` | Page size when a request omits `limit` |
|
|
49
|
+
| `maxLimit` | `100` | Largest accepted request page size |
|
|
50
|
+
| `snippetChars` | `240` | Maximum snippet length in Unicode code points |
|
|
51
|
+
| `readWindowMax` | `50` | Maximum `before`/`after` raw events for the inherited `readEvent()` |
|
|
52
|
+
| `persistedInspectConcurrency` | `4` | Concurrent persisted-log inspections for inherited batch reads |
|
|
53
|
+
|
|
54
|
+
The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-session-query-sqlite) is the exhaustive source for every accepted field and its JSDoc.
|
|
55
|
+
|
|
56
|
+
### Search behavior
|
|
57
|
+
|
|
58
|
+
`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.
|
|
59
|
+
|
|
60
|
+
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.
|
|
61
|
+
|
|
62
|
+
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.
|
|
63
|
+
|
|
64
|
+
### When to defer or disable search
|
|
6
65
|
|
|
7
|
-
|
|
66
|
+
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.
|
|
8
67
|
|
|
9
|
-
|
|
68
|
+
### Failures and recovery
|
|
10
69
|
|
|
11
|
-
|
|
70
|
+
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.
|
|
12
71
|
|
|
13
|
-
|
|
72
|
+
-----
|
|
14
73
|
|
|
15
|
-
|
|
74
|
+
<a id="understand-the-implementation"></a>
|
|
75
|
+
## Understand the implementation
|
|
16
76
|
|
|
17
|
-
|
|
77
|
+
<details>
|
|
78
|
+
<summary>Implementation internals — click to expand</summary>
|
|
18
79
|
|
|
19
|
-
|
|
80
|
+
This section explains the design decisions behind the backend and points at the code that realizes them; the observable behavior is fully covered in [Use this package](#use-this-package).
|
|
20
81
|
|
|
21
|
-
|
|
82
|
+
### Design philosophy
|
|
22
83
|
|
|
23
|
-
The
|
|
84
|
+
The backend is built on one separation and three commitments:
|
|
24
85
|
|
|
25
|
-
|
|
86
|
+
- **Derived index, never the source store.** The FTS rows live in a dedicated disposable database; the session-persistence database is never opened here.
|
|
87
|
+
- **Live-preferred observation.** One serialized state machine compares persistence snapshot revisions, inspects only new or changed logs, and reconciles in one transaction, so a search reflects the newest stable state.
|
|
88
|
+
- **Generation-bound cursors.** Every corpus change bumps a generation; cursors carry the generation they were created under and fail stale rather than returning a shifted page.
|
|
89
|
+
- **Literal phrases as data.** Caller query text is quoted into one FTS5 phrase so query syntax stays inert, and reserved highlight markers are stripped from documents before indexing.
|
|
26
90
|
|
|
27
|
-
|
|
28
|
-
|---|---:|---|
|
|
29
|
-
| `path` | required | Dedicated derived-index SQLite path; `:memory:` is supported. Missing filesystem paths are created owner-only on POSIX filesystems. |
|
|
30
|
-
| `openAt` | `startup` | `startup` opens before service activation completes; `first-search` defers the SQLite module and handle until search; `never` disables full-text search (typed `SESSION_QUERY_SEARCH_DISABLED` failures) while inherited reads stay available. |
|
|
31
|
-
| `journalMode` | `wal` | `wal`, `delete`, `truncate`, or `persist`. |
|
|
32
|
-
| `defaultLimit` | `20` | Page size when a request omits `limit`; at most `Number.MAX_SAFE_INTEGER - 1`. |
|
|
33
|
-
| `maxLimit` | `100` | Largest accepted request page size; at most `Number.MAX_SAFE_INTEGER - 1`. |
|
|
34
|
-
| `snippetChars` | `240` | Maximum snippet length in Unicode code points. |
|
|
35
|
-
| `readWindowMax` | `50` | Maximum `before` or `after` raw-event count for inherited `readEvent()`. |
|
|
36
|
-
| `persistedInspectConcurrency` | `4` | Maximum concurrent persisted-log inspections for inherited batch reads; must be a positive safe integer. |
|
|
91
|
+
The design history lives in the [SQLite FTS5 session search note](../../../.agents/notes/implemented/feature/2026-07-10-sqlite-session-query-provider.md) and the [unified service decision](../../../.agents/notes/archived/architecture/2026-07-23-unified-session-query-service.md).
|
|
37
92
|
|
|
38
|
-
|
|
93
|
+
### Source map
|
|
39
94
|
|
|
40
|
-
|
|
95
|
+
| File | Role |
|
|
96
|
+
|---|---|
|
|
97
|
+
| [`src/index.ts`](src/index.ts) | Service: config, openAt lifecycle, serialized reconciliation, query execution, cursors |
|
|
98
|
+
| [`src/query.ts`](src/query.ts) | Request normalization, parameterized predicates, snippets, predicate and binding budgets |
|
|
99
|
+
| [`src/schema.ts`](src/schema.ts) | Database schema, application-id ownership, in-place reset, owner-only file creation |
|
|
100
|
+
| [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant; boundaries are validated per serialized query) |
|
|
41
101
|
|
|
42
|
-
|
|
102
|
+
### Index lifecycle
|
|
43
103
|
|
|
104
|
+
Persisted FTS rows live in a dedicated derived database and survive restarts; live sessions use connection-local TEMP tables that shadow the durable base for the same session and reveal it again when the live owner detaches. Each search runs one serialized observation: list persistence snapshots, compare per-session revisions with the indexed rows, inspect only new or changed logs, extract semantic documents, and commit the reconciliation in one transaction before running the query. Repeated queries and unchanged reopens inspect nothing; switching stores or observing new, changed, deleted, or externally repaired sources reconciles on the next stable observation. Source or transaction failure commits nothing and the next search retries.
|
|
105
|
+
|
|
106
|
+
### Schema ownership
|
|
107
|
+
|
|
108
|
+
The database carries an application id and schema version 8. Opening refuses a file owned by another application or a canonical database, rejects unknown user tables, and only a recognized incompatible derived schema resets in place — so an unrelated or session-persistence database is never touched. On POSIX filesystems, missing directories and database files are created owner-only (`0700` and `0600` before the process umask). Exactly one service in one process owns a derived-index path; generations and TEMP shadow state are connection-owned.
|
|
109
|
+
|
|
110
|
+
</details>
|
|
111
|
+
|
|
112
|
+
-----
|
|
113
|
+
|
|
114
|
+
<a id="further-exploration"></a>
|
|
115
|
+
## Further Exploration
|
|
116
|
+
|
|
117
|
+
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.
|
|
118
|
+
|
|
119
|
+
- [Session Query subsystem reference](../../../docs/subsystems/session-query.md) — the full type-level contract this backend implements.
|
|
120
|
+
- [dsh-session-query](../session-query/README.md) — the service definition: exact reads, filters, and traces this backend inherits.
|
|
121
|
+
- [dsh-tool-session-query](../tool-session-query/README.md) — the model-facing consumer that calls these search methods.
|
|
122
|
+
- [SQLite FTS5 session search](../../../.agents/notes/implemented/feature/2026-07-10-sqlite-session-query-provider.md) — search semantics, reconciliation, and the tokenizer decision.
|
|
123
|
+
- [SQLite session persistence](../../../packages/session/session-persistence-sqlite/README.md) — the sibling persistence backend; never point this package's `path` at its database.
|
|
124
|
+
|
|
125
|
+
-----
|
|
126
|
+
|
|
127
|
+
<a id="model-experience"></a>
|
|
44
128
|
## Model Experience
|
|
45
129
|
|
|
46
|
-
None, as
|
|
130
|
+
None, as the search backend returns hits only to callers and registers nothing model-facing.
|
|
47
131
|
|
|
48
132
|
#### KV Cache effect
|
|
49
133
|
|
|
@@ -51,7 +135,26 @@ None; this package neither assembles nor sends a provider request.
|
|
|
51
135
|
|
|
52
136
|
## Known Limitations and Deferred Work
|
|
53
137
|
|
|
138
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
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.
|
|
142
|
+
|
|
54
143
|
- **No caller authorization** — this is a trusted context-wide service; a model tool or UI must enforce its own access policy.
|
|
55
144
|
- **Synchronous query execution** — `DatabaseSync` blocks the JavaScript thread during MATCH execution and cannot interrupt a statement already running.
|
|
56
145
|
- **Token recall, not arbitrary substrings** — the `unicode61` tokenizer does not match substrings inside a larger token; use `filterEvents()` for literal scans.
|
|
57
146
|
- **Single-owner derived index** — one service in one process must own each index path; external writers and multi-process sharing are unsupported.
|
|
147
|
+
|
|
148
|
+
<a id="dev-note"></a>
|
|
149
|
+
### Dev Note
|
|
150
|
+
|
|
151
|
+
<details>
|
|
152
|
+
<summary>Working context for maintainers — click to expand</summary>
|
|
153
|
+
|
|
154
|
+
This Dev Note is working context for maintainers: open design questions and directions that are not decided. It is explicitly non-authoritative — shipped behavior, limits, and accepted rationale live in the sections above, the package code, and the linked Agent Notes.
|
|
155
|
+
|
|
156
|
+
#### Future: alternate tokenizers and search providers
|
|
157
|
+
|
|
158
|
+
The `unicode61` tokenizer choice trades substring recall for index size and two-character token support; the trigram alternative was measured and rejected. Switching tokenizers or adding another search backend would change indexed recall and require its own reconciliation and generation story.
|
|
159
|
+
|
|
160
|
+
</details>
|
package/README.zh.md
CHANGED
|
@@ -1,57 +1,160 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "面向部署方与维护者的 SQLite FTS5 会话历史全文搜索后端,用于选择、配置或排查查询服务之上的全文搜索。"
|
|
3
|
+
kind: "package-reference"
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# @deepseek-ai/dsh-session-query-sqlite
|
|
2
7
|
|
|
3
8
|
[English](README.md) | 中文
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
## 概述
|
|
11
|
+
|
|
12
|
+
`dsh-session-query-sqlite` 用 SQLite FTS5 索引搜索会话历史,返回按会话分组或会话内排序、游标分页的结果。与 `dsh-session-query` 一起挂载,即可同时获得全文搜索与完整查询表面——精确读取、过滤与追踪。实时会话从内存索引,持久化会话从专用派生索引数据库索引,因此结果始终反映最新状态,且不触碰会话持久化存储。搜索是可选能力,已发布组合默认关闭:`openAt` 决定索引在启动时、首次搜索时打开,还是永不打开。设置与用法在前;实现内部细节放在下方可折叠的开发者章节中。
|
|
13
|
+
|
|
14
|
+
## 目录
|
|
15
|
+
|
|
16
|
+
- [使用本包](#use-this-package)
|
|
17
|
+
- [理解实现](#understand-the-implementation)
|
|
18
|
+
- [进一步探索](#further-exploration)
|
|
19
|
+
- [模型体验](#model-experience)
|
|
20
|
+
- [已知限制与延期工作](#known-limitations-and-deferred-work)
|
|
21
|
+
- [开发备注](#dev-note)
|
|
22
|
+
|
|
23
|
+
-----
|
|
24
|
+
|
|
25
|
+
<a id="use-this-package"></a>
|
|
26
|
+
## 使用本包
|
|
27
|
+
|
|
28
|
+
当组合需要对会话历史进行排序后的全文搜索时——例如 Web 内容搜索或 `/resume` 既往工作检索——挂载本包。常用路径是显式的:挂载插件、给它一个专用数据库路径,然后从代码调用 `ctx.sessionQuery.searchSessions` 或 `searchEvents`。
|
|
29
|
+
|
|
30
|
+
### 何时选择
|
|
31
|
+
|
|
32
|
+
当你想对既往会话进行带排序与分页的全文召回时选择它。它与 `dsh-session-query` 和会话服务一起使用;持久化后端可选但建议挂载,这样重启后持久化历史仍可搜索。不要把 `path` 指向 session-persistence 数据库——本包拥有独立的派生索引。
|
|
33
|
+
|
|
34
|
+
### 最小配置
|
|
35
|
+
|
|
36
|
+
```yaml
|
|
37
|
+
- name: '@deepseek-ai/dsh-session'
|
|
38
|
+
- name: '@deepseek-ai/dsh-session-query-sqlite'
|
|
39
|
+
config:
|
|
40
|
+
path: /absolute/path/to/session-search.db
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
| 字段 | 默认值 | 含义 |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `path` | 必填 | 专用派生索引 SQLite 路径,或 `:memory:`;POSIX 上缺失的路径会以仅所有者可访问的方式创建 |
|
|
46
|
+
| `openAt` | `startup` | `startup` 在激活时打开;`first-search` 把 SQLite 模块推迟到首次搜索;`never` 关闭全文搜索,继承的读取保持可用 |
|
|
47
|
+
| `journalMode` | `wal` | `wal`、`delete`、`truncate` 或 `persist` |
|
|
48
|
+
| `defaultLimit` | `20` | 请求省略 `limit` 时的分页大小 |
|
|
49
|
+
| `maxLimit` | `100` | 接受的最大请求分页大小 |
|
|
50
|
+
| `snippetChars` | `240` | 按 Unicode 码点计算的最大 snippet 长度 |
|
|
51
|
+
| `readWindowMax` | `50` | 继承的 `readEvent()` 的 `before`/`after` 原始事件数上限 |
|
|
52
|
+
| `persistedInspectConcurrency` | `4` | 继承批量读取的并发持久化日志检查数 |
|
|
53
|
+
|
|
54
|
+
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-session-query-sqlite)是每个受支持字段及其 JSDoc 的穷尽式真源。
|
|
55
|
+
|
|
56
|
+
### 搜索行为
|
|
57
|
+
|
|
58
|
+
`searchSessions` 搜索整个语料库,并按每个会话匹配最强的事件分组结果;`searchEvents` 搜索一个逻辑会话。查询是字面短语:首尾空白会被移除、内部空白会被规范化,引号、`OR`、`NEAR` 和 `*` 等 FTS5 语法被视为数据,绝不作为可执行查询语法。元数据过滤器(会话 id、cwd、创建时间、父级、可用性、事件 seq/时间/类型/表层)在排序前缩小结果。默认搜索全部 `current`、`shadowed` 与 `log-only` 事件;传入表层过滤器可缩小范围。
|
|
59
|
+
|
|
60
|
+
排序是确定性的:实际 FTS5 高亮匹配 span 更多的在前,然后文档更短的在前,事件时间、会话 id 与 seq 打破平局。结果携带按 `snippetChars` 个 Unicode 码点截断的纯文本摘录,没有提供方专用数值分数。分页通过不透明 `SessionSearchCursor` 延续,游标绑定到规范化后的确切请求;相关语料库变化时游标变为陈旧(`SESSION_QUERY_STALE_CURSOR`),会话内游标可在不相关会话变化后延续,跨会话游标则不能。
|
|
61
|
+
|
|
62
|
+
`unicode61` tokenizer 匹配 token 与短语,而非任意子字符串:`AI` 不匹配 token `BRAID`。需要执行字面、空白灵活的字符串子串扫描时,使用带 `text` 子句的 `ctx.sessionQuery.filterEvents()`。
|
|
63
|
+
|
|
64
|
+
### 何时推迟或关闭搜索
|
|
6
65
|
|
|
7
|
-
|
|
66
|
+
使用 `openAt: first-search` 时,服务在不导入 `node:sqlite`、不打开索引的情况下激活,把 SQLite 的实验性警告推迟到首次实际搜索;无效数据库让首次搜索失败,而不是服务激活失败。使用 `openAt: never` 时,全文搜索对该部署关闭:`searchSessions` 与 `searchEvents` 在任何请求规范化之前就以 `SESSION_QUERY_SEARCH_DISABLED` 失败,而继承的全部精确读取、过滤与追踪保持可用。请求超过编译谓词预算(跨会话 14 个组合谓词、会话内 13 个)或 SQLite 可移植的 32,766 绑定上限时,会在准备语句前以 `SESSION_QUERY_INVALID_FILTER` 失败。
|
|
8
67
|
|
|
9
|
-
|
|
68
|
+
### 失败与恢复
|
|
10
69
|
|
|
11
|
-
|
|
70
|
+
带类型的 `SessionQueryError` 失败携带稳定代码:搜索配置为关闭时 `SESSION_QUERY_SEARCH_DISABLED`;索引无法打开或对账时 `SESSION_QUERY_INDEX_FAILED`;搜索目标不存在时 `SESSION_QUERY_SESSION_NOT_FOUND`;语料库在分页之间变化时 `SESSION_QUERY_STALE_CURSOR`——请重试完整的搜索调用;游标不属于该请求时 `SESSION_QUERY_INVALID_CURSOR`。取消在同步 SQLite 调用之间被尊重;已在 JavaScript 线程上执行的语句无法被中断。
|
|
12
71
|
|
|
13
|
-
|
|
72
|
+
-----
|
|
14
73
|
|
|
15
|
-
|
|
74
|
+
<a id="understand-the-implementation"></a>
|
|
75
|
+
## 理解实现
|
|
16
76
|
|
|
17
|
-
|
|
77
|
+
<details>
|
|
78
|
+
<summary>实现细节——点击展开</summary>
|
|
18
79
|
|
|
19
|
-
|
|
80
|
+
本节解释后端背后的设计决策,并指出实现它们的代码位置;可观察行为已在[使用本包](#use-this-package)中完整说明。
|
|
20
81
|
|
|
21
|
-
|
|
82
|
+
### 设计理念
|
|
22
83
|
|
|
23
|
-
|
|
84
|
+
本后端建立在一个分离与三项承诺之上:
|
|
24
85
|
|
|
25
|
-
|
|
86
|
+
- **派生索引,绝不动源存储。** FTS 行存放在专用可丢弃数据库中;这里的代码从不打开 session-persistence 数据库。
|
|
87
|
+
- **实时优先的观察。** 一个串行化状态机比较持久化快照修订,只检查新增或已更改日志,并在一个事务中对账,因此搜索反映最新的稳定状态。
|
|
88
|
+
- **世代绑定的游标。** 每次语料库变化都会递增世代;游标携带其创建时的世代,宁可陈旧失败也不返回偏移后的页面。
|
|
89
|
+
- **字面短语即数据。** 调用方查询文本被引成一个 FTS5 短语,查询语法保持惰性;保留高亮标记在索引前从文档中剥离。
|
|
26
90
|
|
|
27
|
-
|
|
28
|
-
|---|---:|---|
|
|
29
|
-
| `path` | 必填 | 专用派生索引 SQLite 路径;支持 `:memory:`。在 POSIX 文件系统上,缺失的文件系统路径会以仅所有者可访问的方式创建。 |
|
|
30
|
-
| `openAt` | `startup` | `startup` 会在服务激活完成前打开;`first-search` 把 SQLite 模块与句柄推迟到搜索时再加载和打开;`never` 关闭全文搜索(以类型化的 `SESSION_QUERY_SEARCH_DISABLED` 失败),继承的读取保持可用。 |
|
|
31
|
-
| `journalMode` | `wal` | `wal`、`delete`、`truncate` 或 `persist`。 |
|
|
32
|
-
| `defaultLimit` | `20` | 请求省略 `limit` 时的分页大小;最多为 `Number.MAX_SAFE_INTEGER - 1`。 |
|
|
33
|
-
| `maxLimit` | `100` | 接受的最大请求分页大小;最多为 `Number.MAX_SAFE_INTEGER - 1`。 |
|
|
34
|
-
| `snippetChars` | `240` | 按 Unicode 码点计算的最大 snippet 长度。 |
|
|
35
|
-
| `readWindowMax` | `50` | `before` 或 `after` 的最大原始事件数,用于继承的 `readEvent()`。 |
|
|
36
|
-
| `persistedInspectConcurrency` | `4` | 继承批量读取的最大并发持久化日志检查数;必须是正安全整数。 |
|
|
91
|
+
设计历史记录在 [SQLite FTS5 会话搜索笔记](../../../.agents/notes/implemented/feature/2026-07-10-sqlite-session-query-provider.zh.md)与[统一服务决策](../../../.agents/notes/archived/architecture/2026-07-23-unified-session-query-service.md)中。
|
|
37
92
|
|
|
38
|
-
|
|
93
|
+
### 源码地图
|
|
39
94
|
|
|
40
|
-
|
|
95
|
+
| 文件 | 职责 |
|
|
96
|
+
|---|---|
|
|
97
|
+
| [`src/index.ts`](src/index.ts) | 服务:配置、openAt 生命周期、串行化对账、查询执行、游标 |
|
|
98
|
+
| [`src/query.ts`](src/query.ts) | 请求规范化、参数化谓词、摘录、谓词与绑定预算 |
|
|
99
|
+
| [`src/schema.ts`](src/schema.ts) | 数据库 schema、application id 归属、原地重置、仅所有者文件创建 |
|
|
100
|
+
| [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式;边界在每次串行化查询时校验) |
|
|
41
101
|
|
|
42
|
-
|
|
102
|
+
### 索引生命周期
|
|
43
103
|
|
|
104
|
+
持久化 FTS 行存放在专用派生数据库中并跨重启保留;实时会话使用连接本地 TEMP 表,遮蔽同一会话的持久化基库,并在实时所有者脱离后再次显示基库。每次搜索执行一次串行化观察:列出持久化快照、把逐会话修订与已索引行比较、只检查新增或已更改日志、提取语义文档,并在运行查询前于一个事务中提交对账。重复查询与不变的重新打开不会检查任何内容;切换存储或观察到新增、已更改、已删除或经外部修复的来源时,会在下次稳定观察时对账。来源或事务失败不提交任何内容,下一次搜索重试。
|
|
105
|
+
|
|
106
|
+
### Schema 归属
|
|
107
|
+
|
|
108
|
+
数据库携带 application id 与 schema 版本 8。打开时拒绝其他应用程序拥有的文件或规范数据库,拒绝未知用户表;只有已识别的不兼容派生 schema 才会原地重置——因此不相关或 session-persistence 数据库绝不会被触碰。在 POSIX 文件系统上,缺失的目录与数据库文件以仅所有者可访问的方式创建(进程 umask 前为 `0700` 与 `0600`)。每个派生索引路径在一个进程中只能由一个服务拥有;世代与 TEMP 遮蔽状态由连接持有。
|
|
109
|
+
|
|
110
|
+
</details>
|
|
111
|
+
|
|
112
|
+
-----
|
|
113
|
+
|
|
114
|
+
<a id="further-exploration"></a>
|
|
115
|
+
## 进一步探索
|
|
116
|
+
|
|
117
|
+
当包级约定不够用时阅读以下页面。它们从共享查询服务逐步进入类型级约定与设计证据。
|
|
118
|
+
|
|
119
|
+
- [会话查询子系统参考](../../../docs/subsystems/session-query.zh.md)——本后端实现的完整类型级约定。
|
|
120
|
+
- [dsh-session-query](../session-query/README.zh.md)——服务定义:本后端继承的精确读取、过滤与追踪。
|
|
121
|
+
- [dsh-tool-session-query](../tool-session-query/README.zh.md)——调用这些搜索方法的面向模型消费方。
|
|
122
|
+
- [SQLite FTS5 会话搜索](../../../.agents/notes/implemented/feature/2026-07-10-sqlite-session-query-provider.zh.md)——搜索语义、对账与 tokenizer 决策。
|
|
123
|
+
- [SQLite 会话持久化](../../../packages/session/session-persistence-sqlite/README.zh.md)——兄弟持久化后端;切勿把本包的 `path` 指向其数据库。
|
|
124
|
+
|
|
125
|
+
-----
|
|
126
|
+
|
|
127
|
+
<a id="model-experience"></a>
|
|
44
128
|
## 模型体验
|
|
45
129
|
|
|
46
|
-
|
|
130
|
+
无,因为该搜索后端只向调用方返回命中,且不注册任何面向模型的内容。
|
|
47
131
|
|
|
48
132
|
#### KV Cache 影响
|
|
49
133
|
|
|
50
|
-
|
|
134
|
+
无;本包既不组装也不发送提供方请求。
|
|
135
|
+
|
|
136
|
+
## 已知限制与延期工作
|
|
137
|
+
|
|
138
|
+
<a id="known-limitations-and-deferred-work"></a>
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
这些限制说明本包何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是通用 SQLite 对比或任务积压。
|
|
142
|
+
|
|
143
|
+
- **无调用方授权**——这是上下文范围内的可信服务;模型工具或 UI 必须强制执行自己的访问策略。
|
|
144
|
+
- **同步查询执行**——`DatabaseSync` 在 MATCH 执行期间会阻塞 JavaScript 线程,且无法中断已运行的语句。
|
|
145
|
+
- **Token 召回,而非任意子字符串**——`unicode61` tokenizer 不会匹配更大 token 中的子字符串;对字面扫描使用 `filterEvents()`。
|
|
146
|
+
- **单一所有者的派生索引**——每个索引路径必须仅归一个进程中的一个服务所有;不支持外部写入者与多进程共享。
|
|
147
|
+
|
|
148
|
+
<a id="dev-note"></a>
|
|
149
|
+
### 开发备注
|
|
150
|
+
|
|
151
|
+
<details>
|
|
152
|
+
<summary>维护者的工作上下文——点击展开</summary>
|
|
153
|
+
|
|
154
|
+
本开发备注是维护者的工作上下文:开放设计问题与尚未决定的探索方向。它明确不具权威性——已交付的行为、限制与既定理由以上文、包代码和相关 Agent Note 为准。
|
|
155
|
+
|
|
156
|
+
#### 未来:其他 tokenizer 与搜索提供方
|
|
51
157
|
|
|
52
|
-
|
|
158
|
+
`unicode61` tokenizer 的选择以索引体积与双字符 token 支持换取子字符串召回;trigram 备选方案曾被测量并否决。切换 tokenizer 或增加另一个搜索后端会改变索引召回,并需要各自的对账与世代方案。
|
|
53
159
|
|
|
54
|
-
|
|
55
|
-
- **同步查询执行**:`DatabaseSync` 在 MATCH 执行期间会阻塞 JavaScript 线程,且无法中断已运行的语句。
|
|
56
|
-
- **Token 召回,而非任意子字符串**:`unicode61` tokenizer 不会匹配更大 token 中的子字符串;对字面扫描使用 `filterEvents()`。
|
|
57
|
-
- **单一所有者的派生索引**:每个索引路径必须仅归一个进程中的一个服务所有;不支持外部写入者和多进程共享。
|
|
160
|
+
</details>
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-session-query-sqlite",
|
|
3
3
|
"description": "Concrete ctx.sessionQuery backend with SQLite FTS5 search",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.2-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
],
|
|
33
33
|
"license": "MIT",
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
36
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
37
|
-
"@deepseek-ai/dsh-session-
|
|
38
|
-
"@deepseek-ai/
|
|
39
|
-
"@deepseek-ai/
|
|
35
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
36
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.2",
|
|
37
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.2",
|
|
38
|
+
"@deepseek-ai/dsh-session-query": "^0.1.2-alpha.2",
|
|
39
|
+
"@deepseek-ai/cordis": "^4.0.2"
|
|
40
40
|
},
|
|
41
41
|
"peerDependenciesMeta": {
|
|
42
42
|
"@deepseek-ai/dsh-session-persistence": {
|
|
@@ -44,15 +44,16 @@
|
|
|
44
44
|
}
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
47
|
+
"@deepseek-ai/schemastery": "^3.18.2"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@deepseek-ai/
|
|
51
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.
|
|
52
|
-
"@deepseek-ai/
|
|
53
|
-
"@deepseek-ai/
|
|
54
|
-
"@deepseek-ai/
|
|
55
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
56
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
50
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.2-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-session-persistence-sqlite": "^0.1.2-alpha.2",
|
|
52
|
+
"@deepseek-ai/cordis-plugin-loader": "^1.0.3",
|
|
53
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
54
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.2-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-session-query": "^0.1.2-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-session": "^0.1.2-alpha.2"
|
|
57
58
|
}
|
|
58
59
|
}
|