@deepseek-ai/dsh-session-query-sqlite 0.1.2-rc.1 → 0.1.3-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 +6 -5
- package/README.zh.md +6 -5
- package/lib/index.js +16 -12
- package/lib/types/index.d.ts +6 -3
- package/package.json +9 -9
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: 81518cb5c902583f91ef3194e7c9311f49ffe229
|
|
6
|
+
README.zh.md: b8a7e268d5d1771d75f8a4fb6cae2e2d33e38169
|
package/README.md
CHANGED
|
@@ -49,7 +49,8 @@ Choose it when you want full-text recall over prior sessions with ranking and pa
|
|
|
49
49
|
| `maxLimit` | `100` | Largest accepted request page size |
|
|
50
50
|
| `snippetChars` | `240` | Maximum snippet length in Unicode code points |
|
|
51
51
|
| `readWindowMax` | `50` | Maximum `before`/`after` raw events for the inherited `readEvent()` |
|
|
52
|
-
| `
|
|
52
|
+
| `persistedReadConcurrency` | `4` | Concurrent persisted-log reads for inherited batch reads |
|
|
53
|
+
| `preparedSessionCacheSize` | `5` | Cold prepared-Session observations the inherited `observeSession` reader retains for reuse |
|
|
53
54
|
|
|
54
55
|
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
|
|
|
@@ -84,11 +85,11 @@ This section explains the design decisions behind the backend and points at the
|
|
|
84
85
|
The backend is built on one separation and three commitments:
|
|
85
86
|
|
|
86
87
|
- **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,
|
|
88
|
+
- **Live-preferred observation.** One serialized state machine compares persistence snapshot revisions, reads only new or changed logs through short-lived read handles, and reconciles in one transaction, so a search reflects the newest stable state.
|
|
88
89
|
- **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
90
|
- **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.
|
|
90
91
|
|
|
91
|
-
The design history lives in the [SQLite FTS5 session search note](../../../.agents/notes/
|
|
92
|
+
The design history lives in the [SQLite FTS5 session search note](../../../.agents/notes/archived/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).
|
|
92
93
|
|
|
93
94
|
### Source map
|
|
94
95
|
|
|
@@ -101,7 +102,7 @@ The design history lives in the [SQLite FTS5 session search note](../../../.agen
|
|
|
101
102
|
|
|
102
103
|
### Index lifecycle
|
|
103
104
|
|
|
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. Both tables retain the exact inherited cut in numeric `seed_length`; reconstructed headers expose only `isSeeded`, while the cut participates in live fingerprints and persisted source revisions. Each search runs one serialized observation: list persistence snapshots, compare per-session revisions with the indexed rows,
|
|
105
|
+
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. Both tables retain the exact inherited cut in numeric `seed_length`; reconstructed headers expose only `isSeeded`, while the cut participates in live fingerprints and persisted source revisions. Each search runs one serialized observation: list persistence snapshots, compare per-session revisions with the indexed rows, read only new or changed logs through a read handle (balancing an interrupted final turn in memory, never writing back), extract semantic documents, and commit the reconciliation in one transaction before running the query. Repeated queries and unchanged reopens read 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
|
|
|
106
107
|
### Schema ownership
|
|
107
108
|
|
|
@@ -119,7 +120,7 @@ Read these pages when the package-level contract is not enough. They move from t
|
|
|
119
120
|
- [Session Query subsystem reference](../../../docs/subsystems/session-query.md) — the full type-level contract this backend implements.
|
|
120
121
|
- [dsh-session-query](../session-query/README.md) — the service definition: exact reads, filters, and traces this backend inherits.
|
|
121
122
|
- [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/
|
|
123
|
+
- [SQLite FTS5 session search](../../../.agents/notes/archived/feature/2026-07-10-sqlite-session-query-provider.md) — search semantics, reconciliation, and the tokenizer decision.
|
|
123
124
|
- [JSONL session persistence](../../session/session-persistence-jsonl/README.md) — the authoritative Session store this disposable index observes; keep its root separate from this package's database path.
|
|
124
125
|
|
|
125
126
|
-----
|
package/README.zh.md
CHANGED
|
@@ -49,7 +49,8 @@ kind: "package-reference"
|
|
|
49
49
|
| `maxLimit` | `100` | 接受的最大请求分页大小 |
|
|
50
50
|
| `snippetChars` | `240` | 按 Unicode 码点计算的最大 snippet 长度 |
|
|
51
51
|
| `readWindowMax` | `50` | 继承的 `readEvent()` 的 `before`/`after` 原始事件数上限 |
|
|
52
|
-
| `
|
|
52
|
+
| `persistedReadConcurrency` | `4` | 继承批量读取的并发持久化日志读取数 |
|
|
53
|
+
| `preparedSessionCacheSize` | `5` | 继承的 `observeSession` 读取器为复用保留的冷 prepared-Session 观察数 |
|
|
53
54
|
|
|
54
55
|
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-session-query-sqlite)是每个受支持字段及其 JSDoc 的穷尽式真源。
|
|
55
56
|
|
|
@@ -84,11 +85,11 @@ kind: "package-reference"
|
|
|
84
85
|
本后端建立在一个分离与三项承诺之上:
|
|
85
86
|
|
|
86
87
|
- **派生索引,绝不动源存储。** FTS 行存放在专用可丢弃数据库中;这里的代码从不打开 session-persistence 数据库。
|
|
87
|
-
- **实时优先的观察。**
|
|
88
|
+
- **实时优先的观察。** 一个串行化状态机比较持久化快照修订,只通过短生命周期读取 handle 读取新增或已更改日志,并在一个事务中对账,因此搜索反映最新的稳定状态。
|
|
88
89
|
- **世代绑定的游标。** 每次语料库变化都会递增世代;游标携带其创建时的世代,宁可陈旧失败也不返回偏移后的页面。
|
|
89
90
|
- **字面短语即数据。** 调用方查询文本被引成一个 FTS5 短语,查询语法保持惰性;保留高亮标记在索引前从文档中剥离。
|
|
90
91
|
|
|
91
|
-
设计历史记录在 [SQLite FTS5 会话搜索笔记](../../../.agents/notes/
|
|
92
|
+
设计历史记录在 [SQLite FTS5 会话搜索笔记](../../../.agents/notes/archived/feature/2026-07-10-sqlite-session-query-provider.md)与[统一服务决策](../../../.agents/notes/archived/architecture/2026-07-23-unified-session-query-service.md)中。
|
|
92
93
|
|
|
93
94
|
### 源码地图
|
|
94
95
|
|
|
@@ -101,7 +102,7 @@ kind: "package-reference"
|
|
|
101
102
|
|
|
102
103
|
### 索引生命周期
|
|
103
104
|
|
|
104
|
-
持久化 FTS 行存放在专用派生数据库中并跨重启保留;实时会话使用连接本地 TEMP 表,遮蔽同一会话的持久化基库,并在实时所有者脱离后再次显示基库。两类表都在数字 `seed_length` 中保留精确继承切点;重建的 header 只公开 `isSeeded`,而切点参与实时 fingerprint
|
|
105
|
+
持久化 FTS 行存放在专用派生数据库中并跨重启保留;实时会话使用连接本地 TEMP 表,遮蔽同一会话的持久化基库,并在实时所有者脱离后再次显示基库。两类表都在数字 `seed_length` 中保留精确继承切点;重建的 header 只公开 `isSeeded`,而切点参与实时 fingerprint 与持久来源修订。每次搜索执行一次串行化观察:列出持久化快照、把逐会话修订与已索引行比较、只通过读取 handle 读取新增或已更改日志(在内存中补齐被中断的末尾轮次,从不写回)、提取语义文档,并在运行查询前于一个事务中提交对账。重复查询与不变的重新打开不读取任何内容;切换存储或观察到新增、已更改、已删除或经外部修复的来源时,会在下次稳定观察时对账。来源或事务失败不提交任何内容,下一次搜索重试。
|
|
105
106
|
|
|
106
107
|
### Schema 归属
|
|
107
108
|
|
|
@@ -119,7 +120,7 @@ kind: "package-reference"
|
|
|
119
120
|
- [会话查询子系统参考](../../../docs/subsystems/session-query.zh.md)——本后端实现的完整类型级约定。
|
|
120
121
|
- [dsh-session-query](../session-query/README.zh.md)——服务定义:本后端继承的精确读取、过滤与追踪。
|
|
121
122
|
- [dsh-tool-session-query](../tool-session-query/README.zh.md)——调用这些搜索方法的面向模型消费方。
|
|
122
|
-
- [SQLite FTS5 会话搜索](../../../.agents/notes/
|
|
123
|
+
- [SQLite FTS5 会话搜索](../../../.agents/notes/archived/feature/2026-07-10-sqlite-session-query-provider.md)——搜索语义、对账与 tokenizer 决策。
|
|
123
124
|
- [JSONL 会话持久化](../../session/session-persistence-jsonl/README.zh.md)——本可丢弃索引观察的权威 Session store;其 root 必须与本包的数据库路径分开。
|
|
124
125
|
|
|
125
126
|
-----
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { SESSION_FORMAT_VERSION, SessionSeq } from "@deepseek-ai/dsh-session";
|
|
2
3
|
import { Service } from "@deepseek-ai/cordis";
|
|
3
4
|
import z from "@deepseek-ai/schemastery";
|
|
4
|
-
import {
|
|
5
|
-
import SessionQueryEngine, { SESSION_QUERY_DEFAULT_PERSISTED_INSPECT_CONCURRENCY, SESSION_QUERY_READ_WINDOW_MAX, SessionQueryError, SessionSearchCursor, assertSessionHeadersCompatible, buildSessionEventSearchDocuments, materializeSessionEventResultFilters, materializeSessionResultFilters } from "@deepseek-ai/dsh-session-query";
|
|
5
|
+
import SessionQueryEngine, { SESSION_QUERY_DEFAULT_PERSISTED_INSPECT_CONCURRENCY, SESSION_QUERY_DEFAULT_PREPARED_SESSION_CACHE_SIZE, SESSION_QUERY_READ_WINDOW_MAX, SessionQueryError, SessionSearchCursor, assertSessionHeadersCompatible, buildSessionEventSearchDocuments, materializeSessionEventResultFilters, materializeSessionResultFilters, readColdSessionLog } from "@deepseek-ai/dsh-session-query";
|
|
6
6
|
import { mkdir, open } from "node:fs/promises";
|
|
7
7
|
import { dirname, resolve } from "node:path";
|
|
8
8
|
//#region lib/types/schema.js
|
|
@@ -492,7 +492,8 @@ var SqliteSessionQueryEngine = class extends SessionQueryEngine {
|
|
|
492
492
|
maxLimit: z.number().step(1).min(1).max(SQLITE_MAX_PAGE_LIMIT).default(100),
|
|
493
493
|
snippetChars: z.number().step(1).min(1).default(240),
|
|
494
494
|
readWindowMax: z.number().step(1).min(0).default(SESSION_QUERY_READ_WINDOW_MAX),
|
|
495
|
-
|
|
495
|
+
persistedReadConcurrency: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(SESSION_QUERY_DEFAULT_PERSISTED_INSPECT_CONCURRENCY),
|
|
496
|
+
preparedSessionCacheSize: z.number().step(1).min(1).max(Number.MAX_SAFE_INTEGER).default(SESSION_QUERY_DEFAULT_PREPARED_SESSION_CACHE_SIZE)
|
|
496
497
|
});
|
|
497
498
|
/** Validated and defaulted backend configuration. */
|
|
498
499
|
config;
|
|
@@ -712,20 +713,21 @@ var SqliteSessionQueryEngine = class extends SessionQueryEngine {
|
|
|
712
713
|
let persisted = /* @__PURE__ */ new Map();
|
|
713
714
|
if (persistence !== void 0) try {
|
|
714
715
|
const canReuseIndexed = this._lastPersistenceIdentity === void 0 || this._lastPersistenceIdentity === persistenceBinding.identity;
|
|
715
|
-
const
|
|
716
|
+
const listOptions = signal === void 0 ? void 0 : { signal };
|
|
717
|
+
const before = await persistence.list(listOptions);
|
|
716
718
|
assertNotAborted(signal);
|
|
717
719
|
persisted = materializePersistenceSnapshots(before);
|
|
718
720
|
for (const entry of persisted.values()) {
|
|
719
721
|
if (canReuseIndexed && indexed.get(entry.header.id)?.revision === entry.revision) continue;
|
|
720
722
|
if (initiallyLive.has(entry.header.id) || this.ctx.sessions.get(entry.header.id) !== void 0) continue;
|
|
721
723
|
assertNotAborted(signal);
|
|
722
|
-
const loaded = await persistence
|
|
724
|
+
const loaded = await readColdSessionLog(persistence, entry.header.id, signal);
|
|
723
725
|
assertNotAborted(signal);
|
|
724
|
-
assertSessionHeadersCompatible(entry.header, loaded.
|
|
725
|
-
entry.loaded = observeSession(loaded.
|
|
726
|
+
assertSessionHeadersCompatible(entry.header, loaded.header);
|
|
727
|
+
entry.loaded = observeSession(loaded.header, loaded.inheritedEventCount, loaded.events);
|
|
726
728
|
}
|
|
727
729
|
assertNotAborted(signal);
|
|
728
|
-
const afterSnapshots = await persistence.
|
|
730
|
+
const afterSnapshots = await persistence.list(listOptions);
|
|
729
731
|
assertNotAborted(signal);
|
|
730
732
|
const after = materializePersistenceSnapshots(afterSnapshots);
|
|
731
733
|
if (!samePersistenceSnapshots(persisted, after)) continue;
|
|
@@ -1034,11 +1036,11 @@ function sameSessionIds(before, after) {
|
|
|
1034
1036
|
return true;
|
|
1035
1037
|
}
|
|
1036
1038
|
function sameHeader(a, b) {
|
|
1037
|
-
return a.
|
|
1039
|
+
return a.id === b.id && a.createdAt === b.createdAt && a.cwd === b.cwd && a.parentSession === b.parentSession && a.isSeeded === b.isSeeded && (a.delegationDepth ?? 0) === (b.delegationDepth ?? 0) && a.agentPreset === b.agentPreset;
|
|
1038
1040
|
}
|
|
1039
1041
|
function rowHeader(row) {
|
|
1040
1042
|
return {
|
|
1041
|
-
version:
|
|
1043
|
+
version: SESSION_FORMAT_VERSION,
|
|
1042
1044
|
id: row.session_id,
|
|
1043
1045
|
createdAt: row.created_at,
|
|
1044
1046
|
...row.cwd === null ? {} : { cwd: row.cwd },
|
|
@@ -1081,7 +1083,8 @@ function resolveConfig(config) {
|
|
|
1081
1083
|
maxLimit: config.maxLimit ?? 100,
|
|
1082
1084
|
snippetChars: config.snippetChars ?? 240,
|
|
1083
1085
|
readWindowMax: config.readWindowMax ?? SESSION_QUERY_READ_WINDOW_MAX,
|
|
1084
|
-
|
|
1086
|
+
persistedReadConcurrency: config.persistedReadConcurrency ?? SESSION_QUERY_DEFAULT_PERSISTED_INSPECT_CONCURRENCY,
|
|
1087
|
+
preparedSessionCacheSize: config.preparedSessionCacheSize ?? SESSION_QUERY_DEFAULT_PREPARED_SESSION_CACHE_SIZE
|
|
1085
1088
|
};
|
|
1086
1089
|
if (typeof resolved.path !== "string" || resolved.path.trim().length === 0) throw invalidConfig("path must not be blank");
|
|
1087
1090
|
if (![
|
|
@@ -1093,7 +1096,8 @@ function resolveConfig(config) {
|
|
|
1093
1096
|
assertPageLimit("maxLimit", resolved.maxLimit);
|
|
1094
1097
|
assertPositiveInteger("snippetChars", resolved.snippetChars);
|
|
1095
1098
|
if (!Number.isInteger(resolved.readWindowMax) || resolved.readWindowMax < 0) throw invalidConfig("readWindowMax must be a non-negative integer");
|
|
1096
|
-
if (!Number.isSafeInteger(resolved.
|
|
1099
|
+
if (!Number.isSafeInteger(resolved.persistedReadConcurrency) || resolved.persistedReadConcurrency < 1) throw invalidConfig("persistedReadConcurrency must be a positive safe integer");
|
|
1100
|
+
if (!Number.isSafeInteger(resolved.preparedSessionCacheSize) || resolved.preparedSessionCacheSize < 1) throw invalidConfig("preparedSessionCacheSize must be a positive safe integer");
|
|
1097
1101
|
if (resolved.defaultLimit > resolved.maxLimit) throw invalidConfig("defaultLimit must be less than or equal to maxLimit");
|
|
1098
1102
|
if (![
|
|
1099
1103
|
"wal",
|
package/lib/types/index.d.ts
CHANGED
|
@@ -49,8 +49,10 @@ export interface Config extends SessionQueryConfig {
|
|
|
49
49
|
maxLimit?: number;
|
|
50
50
|
/** Maximum snippet length in Unicode code points. Defaults to 240. */
|
|
51
51
|
snippetChars?: number;
|
|
52
|
-
/** Maximum concurrent persisted-log
|
|
53
|
-
|
|
52
|
+
/** Maximum concurrent persisted-log reads in one inherited batch read. Defaults to 4. */
|
|
53
|
+
persistedReadConcurrency?: number;
|
|
54
|
+
/** Maximum cold prepared-Session observations the inherited reader retains for reuse. Defaults to 5. */
|
|
55
|
+
preparedSessionCacheSize?: number;
|
|
54
56
|
}
|
|
55
57
|
interface ResolvedConfig {
|
|
56
58
|
path: string;
|
|
@@ -60,7 +62,8 @@ interface ResolvedConfig {
|
|
|
60
62
|
maxLimit: number;
|
|
61
63
|
snippetChars: number;
|
|
62
64
|
readWindowMax: number;
|
|
63
|
-
|
|
65
|
+
persistedReadConcurrency: number;
|
|
66
|
+
preparedSessionCacheSize: number;
|
|
64
67
|
}
|
|
65
68
|
/** Concrete SQLite owner of the combined `ctx.sessionQuery` service. */
|
|
66
69
|
export declare class SqliteSessionQueryEngine extends SessionQueryEngine {
|
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.3-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
31
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.
|
|
32
|
-
"@deepseek-ai/dsh-session-query": "^0.1.
|
|
30
|
+
"@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
|
|
31
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.3-alpha.2",
|
|
32
|
+
"@deepseek-ai/dsh-session-query": "^0.1.3-alpha.2",
|
|
33
33
|
"@deepseek-ai/cordis": "^4.0.2"
|
|
34
34
|
},
|
|
35
35
|
"peerDependenciesMeta": {
|
|
@@ -42,11 +42,11 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@deepseek-ai/cordis-plugin-loader": "^1.0.3",
|
|
45
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
46
|
-
"@deepseek-ai/dsh-session-persistence": "^0.1.
|
|
47
|
-
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.
|
|
48
|
-
"@deepseek-ai/dsh-session-query": "^0.1.2-rc.1",
|
|
45
|
+
"@deepseek-ai/dsh-session": "^0.1.3-alpha.2",
|
|
46
|
+
"@deepseek-ai/dsh-session-persistence": "^0.1.3-alpha.2",
|
|
47
|
+
"@deepseek-ai/dsh-session-persistence-jsonl": "^0.1.3-alpha.2",
|
|
49
48
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
50
|
-
"@deepseek-ai/dsh-session-
|
|
49
|
+
"@deepseek-ai/dsh-session-query": "^0.1.3-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.3-alpha.2"
|
|
51
51
|
}
|
|
52
52
|
}
|