@deepseek-ai/dsh-storage-sqlite 0.1.1-rc.2 → 0.1.2-alpha.3

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 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/storage/storage-sqlite/README.md
5
- README.md: 654ae004986240ae6c7c8f74f80e45089270a0ab
6
- README.zh.md: 9f31c55a3281a02ece793d2810480de4e9d8f339
5
+ README.md: 243c4bb93e50e81c417634645b611484ca65430e
6
+ README.zh.md: ea7e886332e8b7663f1b381991ee1e6c7f09a6e7
package/README.md CHANGED
@@ -1,31 +1,115 @@
1
+ ---
2
+ description: "SQLite storage backend for hosts and maintainers choosing, configuring, or debugging document-per-row KV storage in one database file."
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-storage-sqlite
2
7
 
3
8
  English | [中文](README.zh.md)
4
9
 
5
- SQLite backend for the [storage hub](../storage/README.md): registers as backend `sqlite`, serving the `kv` facet over one `node:sqlite` database file (or `:memory:`). Design and trade-offs: [domain KV storage Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md).
10
+ ## Summary
11
+
12
+ `dsh-storage-sqlite` is a storage backend that hosts every routed unit in one SQLite database file, storing each record as one JSON document per row, registered as backend `sqlite`. A single record update touches exactly one row, which is what makes this the right medium for high-frequency, point-sized writes. Choose it when a domain's data changes often or the deployment prefers one queryable database; choose the JSON backend when the data should be readable as plain files. The backend is host-side only: it contributes no prompt, tool, or schema, so the model and the agent loop never see it.
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
+ -----
6
24
 
7
- ## Storage model
25
+ <a id="use-this-package"></a>
26
+ ## Use this package
8
27
 
9
- Document-per-row: each unit table becomes a physical `"u_<unit>_<table>" (key TEXT PRIMARY KEY, value TEXT)` STRICT table whose `value` is the record's JSON text, so one key updates one row (the reason to route a high-churn domain here instead of the JSON backend). Unit identity lives in two metadata tables — `units` stamps each unit's format version at first open and rejects a differing descriptor with `version-mismatch`; `unit_globals` holds each unit's global singleton row. The physical layout version lives in `PRAGMA user_version`; any other stamped value rejects (unreleased format, no migrations). Unit and table names are validated against the hub's `UNIT_NAME_RE` before they reach DDL, so no external input is ever interpolated into SQL identifiers.
28
+ Use this package when a composition keeps frequently updated domain data in one database: route the relevant domains to this backend and each unit materializes as tables in the configured database file.
10
29
 
11
- Every write primitive is a single prepared statement — SQLite's per-statement atomicity satisfies the KV contract without explicit transactions, and write ordering stays the caller's responsibility (the domain layer's write chain). Missing directories and database files are created owner-only (`0o700`/`0o600`), matching the session-persistence SQLite backend.
30
+ ### When to choose it
12
31
 
13
- ## Configuration (schemastery)
32
+ Choose it when writes are frequent and point-sized — each key maps to exactly one row, so updating one record touches one row instead of rewriting a whole file. Choose the JSON backend when humans inspect or edit the stored data as plain files. The synchronous `node:sqlite` driver blocks the JavaScript thread for the duration of each single-statement call, which is fine at domain-data scale but worth accounting for at high write rates.
14
33
 
15
- ```ts
16
- interface Config {
17
- path: string // SQLite database file path, or ':memory:' for an in-process DB
18
- journalMode?: 'wal' | 'delete' | 'truncate' | 'persist' // journal_mode pragma; default 'wal'
19
- }
34
+ ### Configuration
35
+
36
+ Two fields: the database path and the journal mode. `:memory:` opens an in-process database whose contents disappear with the process.
37
+
38
+ ```yaml
39
+ - name: '@deepseek-ai/dsh-storage'
40
+ - name: '@deepseek-ai/dsh-storage-sqlite'
41
+ config:
42
+ path: /var/lib/dsh/data.db
43
+ - name: '@deepseek-ai/dsh-storage-domain'
44
+ config:
45
+ backend: sqlite
20
46
  ```
21
47
 
48
+ | Field | Default | Meaning |
49
+ |---|---|---|
50
+ | `path` | required | SQLite database file path, or `:memory:` |
51
+ | `journalMode` | `wal` | Journal mode: `wal`, `delete`, `truncate`, or `persist` |
52
+
53
+ `wal` suits local disks; a rollback-journal mode (`delete`/`truncate`/`persist`) fits filesystems where WAL's shared-memory files do not work, such as network mounts. The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-storage-sqlite) is the exhaustive source for every accepted field and its JSDoc.
54
+
55
+ ### Observable behavior
56
+
57
+ Missing directories and database files are created owner-only (`0o700`/`0o600`); an existing database keeps its modes. A unit whose stored format version differs from its descriptor rejects `version-mismatch`, and a database stamped with a physical layout version other than the current one rejects outright — no migration, pre-release stance. Failures carry stable `StorageError` codes, and writes are durable once resolved.
58
+
59
+ -----
60
+
61
+ <a id="understand-the-implementation"></a>
62
+ ## Understand the implementation
63
+
64
+ <details>
65
+ <summary>Implementation internals — click to expand</summary>
66
+
67
+ The backend is a document-per-row layout over one `node:sqlite` connection, designed so a per-key update is a single prepared statement.
68
+
69
+ ### Design concept
70
+
71
+ - **Document per row.** Each unit table becomes a physical STRICT table `u_<unit>_<table> (key TEXT PRIMARY KEY, value TEXT)` whose `value` column holds the record's JSON text; the global singleton lives in a shared `unit_globals` table. One key update touches exactly one row — the reason to route a high-churn domain here.
72
+ - **Single-statement atomicity.** Every write primitive is one prepared statement, so SQLite's per-statement atomicity satisfies the KV contract without explicit transactions; write ordering stays the caller's responsibility (the domain layer's write chain).
73
+ - **Names validated before DDL.** Unit and table names must match `UNIT_NAME_RE` before they reach DDL, so no external input is ever interpolated into SQL identifiers.
74
+ - **Versions fail loud.** The physical layout version lives in `PRAGMA user_version` (fresh databases stamp it last); unit format versions live in the `units` table. Any other stamped value rejects — no migrations.
75
+
76
+ ### Open sequence
77
+
78
+ Opening the database creates the parent as `0o700`, exclusively creates a missing file as `0o600`, applies `PRAGMA foreign_keys = ON` and the journal mode, checks `user_version`, creates the `units` and `unit_globals` metadata tables, and stamps fresh databases last so a failure leaves the medium unstamped.
79
+
80
+ ### Source map
81
+
82
+ | File | Role |
83
+ |---|---|
84
+ | [`src/index.ts`](src/index.ts) | Plugin entry: backend registration, `path`/`journalMode` config, unit table |
85
+ | [`src/schema.ts`](src/schema.ts) | Open sequence, physical layout version, metadata tables, record table naming |
86
+ | [`src/unit.ts`](src/unit.ts) | One opened unit: prepared statements, JSON value parse, close |
87
+ | [`src/invariant.ts`](src/invariant.ts) | Invariant companion (no runtime invariant: versions are open-time checks) |
88
+
89
+ </details>
90
+
91
+ -----
92
+
93
+ <a id="further-exploration"></a>
94
+ ## Further Exploration
95
+
96
+ Read these pages when this backend's view is not enough: the subsystem reference is the authoritative contract, and the sibling backend shows the alternative medium.
97
+
98
+ - [Storage subsystem](../../../docs/subsystems/storage.md) — the backend contract, domain semantics, and generated API.
99
+ - [Storage package map](../README.md) — the family's packages and their repository position.
100
+ - [JSON storage backend](../storage-json/README.md) — the human-readable medium for small, inspectable data.
101
+ - [domain KV storage Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.md) — the design behind the backend family and the deferred session-backend migration.
102
+
103
+ -----
104
+
105
+ <a id="model-experience"></a>
22
106
  ## Model Experience
23
107
 
24
108
  ### Stored domain records
25
109
 
26
110
  #### What the model sees
27
111
 
28
- Nothing. This backend contributes no prompt, tool, or schema; it persists non-session domain data (workspace records, future session sidecar metadata) behind `ctx.storage` for host-side consumers only.
112
+ Nothing. This backend contributes no prompt, tool, or schema; it persists non-session domain data behind `ctx.storage` for host-side consumers only.
29
113
 
30
114
  #### Token effect
31
115
 
@@ -37,7 +121,22 @@ None — the backend never touches live request prefixes.
37
121
 
38
122
  ## Known Limitations and Deferred Work
39
123
 
40
- - **`DatabaseSync` is synchronous** — each write blocks the event loop for its (single-statement) duration; acceptable at domain-data scale.
41
- - **No busy-wait or retry policy** — another connection holding a write transaction rejects the operation immediately; there is no multi-process write protection.
42
- - **Only the current `STORAGE_SQLITE_SCHEMA_VERSION` opens** — any other stamped version is rejected rather than migrated (pre-release stance).
43
- - **`openDatabase` duplicates the session-persistence SQLite open sequence** — extraction into a shared media layer is deferred to the planned session-backend migration (see the Agent Note's reuse audit).
124
+ <a id="known-limitations-and-deferred-work"></a>
125
+
126
+
127
+ These limits define when this backend is a poor fit or needs special operational care. They are current package constraints, not a task backlog.
128
+
129
+ - **Synchronous driver blocks the event loop** — each write is a synchronous `DatabaseSync` call; the block lasts a single statement, which is acceptable at domain-data scale.
130
+ - **No busy-wait or retry policy** — a competing connection holding a write lock rejects the operation immediately instead of waiting; the domain layer's write chain serializes writes within one process, and cross-process coordination is out of scope.
131
+ - **Only the current physical layout version opens** — any other stamped `user_version` is rejected rather than migrated (pre-release stance).
132
+ - **Open sequence duplicated with the query provider** — `openDatabase` and `session-query-sqlite` both enforce SQLite file ownership, but each package owns a distinct application identity and schema; no shared medium helper couples them.
133
+
134
+ <a id="dev-note"></a>
135
+ ### Dev Note
136
+
137
+ <details>
138
+ <summary>Working context for maintainers — click to expand</summary>
139
+
140
+ None.
141
+
142
+ </details>
package/README.zh.md CHANGED
@@ -1,31 +1,115 @@
1
+ ---
2
+ description: "SQLite 存储后端:面向在单个数据库文件中选择、配置或排查按行存储文档的 KV 存储的宿主与维护者。"
3
+ kind: "package-reference"
4
+ ---
5
+
1
6
  # @deepseek-ai/dsh-storage-sqlite
2
7
 
3
8
  [English](README.md) | 中文
4
9
 
5
- [存储中心](../storage/README.zh.md)的 SQLite 后端:注册为后端 `sqlite`,通过一个数据库提供 `kv` facet;该数据库由 `node:sqlite` 操作,可以是单个文件,也可以是 `:memory:`。设计与取舍见[领域 KV 存储 Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md)。
10
+ ## 概述
11
+
12
+ `dsh-storage-sqlite` 是一个存储后端:把每个已路由单元托管在同一个 SQLite 数据库文件中,每条记录按行存储一份 JSON 文档,注册为后端 `sqlite`。单条记录更新恰好触碰一行,这正是它适合高频定点写入的原因。当领域数据变动频繁、或部署偏好单一可查询数据库时选择它;当数据需要以纯文本文件形式可读时选择 JSON 后端。本后端只面向宿主侧:它不贡献提示词、工具或 schema,因此模型与 agent loop(智能体循环)永远不会看到它。
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
+ -----
6
24
 
7
- ## 存储模型
25
+ <a id="use-this-package"></a>
26
+ ## 使用本包
8
27
 
9
- 每行一个文档:每个单元表都会成为一个物理 STRICT 表 `"u_<unit>_<table>" (key TEXT PRIMARY KEY, value TEXT)`,其中 `value` 是记录的 JSON 文本,因此一个 key 只更新一行(高频变更领域路由到这里而非 JSON 后端的原因)。单元标识位于两个元数据表中:`units` 在单元首次打开时标记其格式版本,描述符不同时以 `version-mismatch` 拒绝;`unit_globals` 保存每个单元的全局单例行。物理布局版本位于 `PRAGMA user_version`;其他任何标记值都会被拒绝(未发布格式,不迁移)。单元名和表名在进入 DDL 之前依据中心的 `UNIT_NAME_RE` 进行验证,因此不会把外部输入插值到 SQL 标识符中。
28
+ 当组合把频繁更新的领域数据保存在一个数据库中时使用本包:把相关领域路由到此后端,每个单元即作为表物化在配置的数据库文件中。
10
29
 
11
- 每个写入原语都是一条预处理语句:SQLite 的逐语句原子性无需显式事务即可满足 KV 约定,写入顺序仍由调用方负责(领域层写入链)。缺失目录和数据库文件会以仅所有者可访问的权限创建(`0o700`/`0o600`),与会话持久化 SQLite 后端一致。
30
+ ### 何时选择
12
31
 
13
- ## 配置(schemastery)
32
+ 当写入频繁且为定点更新时选择它——每个键恰好映射到一行,因此更新一条记录只触碰一行,而不是重写整个文件。当人类需要以纯文本文件查看或编辑已存数据时选择 JSON 后端。同步的 `node:sqlite` 驱动会在每条单语句调用期间阻塞 JavaScript 线程,这在领域数据规模下可以接受,但高写入率时值得纳入考量。
14
33
 
15
- ```ts
16
- interface Config {
17
- path: string // SQLite database file path, or ':memory:' for an in-process DB
18
- journalMode?: 'wal' | 'delete' | 'truncate' | 'persist' // journal_mode pragma; default 'wal'
19
- }
34
+ ### 配置
35
+
36
+ 两个字段:数据库路径与 journal mode。`:memory:` 打开一个进程内数据库,其内容随进程消失。
37
+
38
+ ```yaml
39
+ - name: '@deepseek-ai/dsh-storage'
40
+ - name: '@deepseek-ai/dsh-storage-sqlite'
41
+ config:
42
+ path: /var/lib/dsh/data.db
43
+ - name: '@deepseek-ai/dsh-storage-domain'
44
+ config:
45
+ backend: sqlite
20
46
  ```
21
47
 
48
+ | 字段 | 默认值 | 含义 |
49
+ |---|---|---|
50
+ | `path` | 必填 | SQLite 数据库文件路径,或 `:memory:` |
51
+ | `journalMode` | `wal` | Journal mode:`wal`、`delete`、`truncate` 或 `persist` |
52
+
53
+ `wal` 适合本地磁盘;回滚日志模式(`delete`/`truncate`/`persist`)适合 WAL 共享内存文件不可用的文件系统,例如网络挂载。生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-storage-sqlite)是每个受支持字段及其 JSDoc 的穷尽式真源。
54
+
55
+ ### 可观察行为
56
+
57
+ 缺失的目录与数据库文件会以仅所有者可访问的权限创建(`0o700`/`0o600`);已有数据库保持其既有模式。已存格式版本与描述符不同的单元拒绝 `version-mismatch`,盖有非当前物理布局版本的数据库会直接拒绝——不做迁移,预发布立场。失败携带稳定的 `StorageError` 代码,写入 resolve 后即已持久。
58
+
59
+ -----
60
+
61
+ <a id="understand-the-implementation"></a>
62
+ ## 理解实现
63
+
64
+ <details>
65
+ <summary>实现细节——点击展开</summary>
66
+
67
+ 本后端是在单个 `node:sqlite` 连接之上的文档按行布局,设计目标是让每次按键更新都是一条预处理语句。
68
+
69
+ ### 设计理念
70
+
71
+ - **每行一份文档。** 每个单元表都变成一张物理 STRICT 表 `u_<unit>_<table> (key TEXT PRIMARY KEY, value TEXT)`,其 `value` 列保存记录的 JSON 文本;全局单例存放在共享的 `unit_globals` 表中。一个键的更新恰好触碰一行——这就是把高频变更领域路由到这里的原因。
72
+ - **单语句原子性。** 每个写入原语都是一条预处理语句,因此 SQLite 的逐语句原子性无需显式事务即可满足 KV 约定;写入顺序仍由调用方负责(领域层的写入链)。
73
+ - **名称在 DDL 之前校验。** 单元名与表名在进入 DDL 之前必须匹配 `UNIT_NAME_RE`,因此任何外部输入都不会被插值进 SQL 标识符。
74
+ - **版本明确报错。** 物理布局版本存放在 `PRAGMA user_version`(全新数据库最后盖戳);单元格式版本存放在 `units` 表中。任何其他已标记值都会被拒绝——不做迁移。
75
+
76
+ ### 打开顺序
77
+
78
+ 打开数据库时会以 `0o700` 创建父目录、以 `0o600` 独占创建缺失文件、应用 `PRAGMA foreign_keys = ON` 与 journal mode、检查 `user_version`、创建 `units` 与 `unit_globals` 元数据表,并在最后给全新数据库盖戳,让失败留下未盖戳的介质。
79
+
80
+ ### 源码地图
81
+
82
+ | 文件 | 职责 |
83
+ |---|---|
84
+ | [`src/index.ts`](src/index.ts) | 插件入口:后端注册、`path`/`journalMode` 配置、单元表 |
85
+ | [`src/schema.ts`](src/schema.ts) | 打开顺序、物理布局版本、元数据表、记录表命名 |
86
+ | [`src/unit.ts`](src/unit.ts) | 一个已打开单元:预处理语句、JSON 值解析、关闭 |
87
+ | [`src/invariant.ts`](src/invariant.ts) | 不变式伴生插件(无运行时不变式:版本是打开时检查) |
88
+
89
+ </details>
90
+
91
+ -----
92
+
93
+ <a id="further-exploration"></a>
94
+ ## 进一步探索
95
+
96
+ 当本后端视角不够用时阅读以下页面:子系统参考是权威约定,兄弟后端展示了另一种介质。
97
+
98
+ - [存储子系统](../../../docs/subsystems/storage.zh.md)——后端约定、领域语义与生成的 API。
99
+ - [存储包映射](../README.zh.md)——家族的各包及其在仓库中的位置。
100
+ - [JSON 存储后端](../storage-json/README.zh.md)——面向小而可检查数据的人类可读介质。
101
+ - [领域 KV 存储 Agent Note](../../../.agents/notes/proposed/architecture/2026-07-24-domain-kv-storage-and-workspace.zh.md)——后端家族背后的设计与被推迟的会话后端迁移。
102
+
103
+ -----
104
+
105
+ <a id="model-experience"></a>
22
106
  ## 模型体验
23
107
 
24
108
  ### 已存领域记录
25
109
 
26
- #### 模型看到的内容
110
+ #### 模型看到什么
27
111
 
28
- 无。该后端不贡献提示词、工具或 schema;它在 `ctx.storage` 后面持久化非会话领域数据(工作区记录、未来的会话伴随元数据),只供主机侧消费方使用。
112
+ 无。本后端不贡献提示词、工具或 schema;它在 `ctx.storage` 后面持久化非会话领域数据,只供宿主侧消费方使用。
29
113
 
30
114
  #### Token 影响
31
115
 
@@ -33,11 +117,26 @@ interface Config {
33
117
 
34
118
  #### KV Cache 影响
35
119
 
36
- 无:该后端从不触碰实时请求前缀。
120
+ 无:本后端从不触碰实时请求前缀。
121
+
122
+ ## 已知限制与延期工作
123
+
124
+ <a id="known-limitations-and-deferred-work"></a>
125
+
126
+
127
+ 这些限制说明本后端何时不合适,或何时需要特别的运维注意。它们是当前包约束,不是任务积压。
128
+
129
+ - **同步驱动阻塞事件循环**——每次写入都是一次同步 `DatabaseSync` 调用;阻塞只持续一条语句,在领域数据规模下可以接受。
130
+ - **没有忙等待或重试策略**——持有写锁的竞争连接会立即拒绝操作,而不是等待;领域层的写入链在单进程内串行化写入,跨进程协调属于范围外。
131
+ - **只打开当前的物理布局版本**——任何其他已标记的 `user_version` 都会被拒绝而不是迁移(预发布立场)。
132
+ - **打开顺序与 query provider 重复**——`openDatabase` 与 `session-query-sqlite` 都强制执行 SQLite 文件 ownership,但两个 package 分别拥有不同的 application identity 与 schema;没有共享 medium helper 将其耦合。
133
+
134
+ <a id="dev-note"></a>
135
+ ### 开发备注
136
+
137
+ <details>
138
+ <summary>维护者的工作上下文——点击展开</summary>
37
139
 
38
- ## 已知限制与暂缓事项
140
+ 无。
39
141
 
40
- - **`DatabaseSync` 是同步的**:每次写入都会在单条语句执行期间阻塞事件循环;在领域数据规模下可以接受。
41
- - **没有忙等待或重试策略**:另一个连接持有写事务时,该操作会立即被拒绝;没有多进程写入保护。
42
- - **只打开当前的 `STORAGE_SQLITE_SCHEMA_VERSION`**:其他任何已标记版本都会被拒绝而不是迁移(预发布立场)。
43
- - **`openDatabase` 重复了会话持久化 SQLite 的打开顺序**:提取到共享介质层的工作暂缓至计划的会话后端迁移(见 Agent Note 的复用审计)。
142
+ </details>
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-storage-sqlite",
3
3
  "description": "SQLite storage backend (kv facet) for the DeepSeek Harness storage hub",
4
- "version": "0.1.1-rc.2",
4
+ "version": "0.1.2-alpha.3",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -32,16 +32,16 @@
32
32
  ],
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
- "@deepseek-ai/dsh-storage": "^0.1.1-rc.2",
36
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
37
- "@deepseek-ai/cordis": "^4.0.1"
35
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
36
+ "@deepseek-ai/dsh-storage": "^0.1.2-alpha.3",
37
+ "@deepseek-ai/cordis": "^4.0.2"
38
38
  },
39
39
  "dependencies": {
40
- "@deepseek-ai/schemastery": "^3.18.1"
40
+ "@deepseek-ai/schemastery": "^3.18.2"
41
41
  },
42
42
  "devDependencies": {
43
- "@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
44
- "@deepseek-ai/dsh-storage": "^0.1.1-rc.2",
45
- "@deepseek-ai/cordis": "^4.0.1"
43
+ "@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
44
+ "@deepseek-ai/dsh-storage": "^0.1.2-alpha.3",
45
+ "@deepseek-ai/cordis": "^4.0.2"
46
46
  }
47
47
  }