@dzhechkov/memory 0.2.19 → 0.2.20
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/.dz-manifest.json +33 -13
- package/README.md +45 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/sqlite-backend.d.ts +35 -0
- package/dist/sqlite-backend.d.ts.map +1 -1
- package/dist/sqlite-backend.js +109 -66
- package/dist/sqlite-backend.js.map +1 -1
- package/dist/sqlite-readonly.d.ts +94 -0
- package/dist/sqlite-readonly.d.ts.map +1 -0
- package/dist/sqlite-readonly.js +182 -0
- package/dist/sqlite-readonly.js.map +1 -0
- package/package.json +1 -1
- package/sbom.json +62 -12
- package/src/index.ts +2 -0
- package/src/sqlite-backend.ts +124 -72
- package/src/sqlite-readonly.ts +244 -0
package/.dz-manifest.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
{
|
|
11
11
|
"path": "README.md",
|
|
12
|
-
"sha256": "
|
|
12
|
+
"sha256": "9f48bf4e1b45d539be0478df0abeb43c10bffc6a8dc43a247d41a521254536da"
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"path": "dist/backend.d.ts",
|
|
@@ -77,19 +77,19 @@
|
|
|
77
77
|
},
|
|
78
78
|
{
|
|
79
79
|
"path": "dist/index.d.ts",
|
|
80
|
-
"sha256": "
|
|
80
|
+
"sha256": "c4e8be344baf02933c8e453bdba9ec3f95062d28672c8d0dabdd8035ef39e58e"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"path": "dist/index.d.ts.map",
|
|
84
|
-
"sha256": "
|
|
84
|
+
"sha256": "9576e873ac4003f6b217e0eff04f2cd5f8633397e004164abe7f10f9d7886511"
|
|
85
85
|
},
|
|
86
86
|
{
|
|
87
87
|
"path": "dist/index.js",
|
|
88
|
-
"sha256": "
|
|
88
|
+
"sha256": "b6f72385ca8f3fbde2a5c11cf0deb98f58ea9836d216377e6585dc22b4b629b5"
|
|
89
89
|
},
|
|
90
90
|
{
|
|
91
91
|
"path": "dist/index.js.map",
|
|
92
|
-
"sha256": "
|
|
92
|
+
"sha256": "92e221dc762befc999d234a173b09b2e5c09c7e927518fccdd8bbebe15923712"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"path": "dist/json-backend.d.ts",
|
|
@@ -125,19 +125,19 @@
|
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
"path": "dist/sqlite-backend.d.ts",
|
|
128
|
-
"sha256": "
|
|
128
|
+
"sha256": "c077c7e0b1001aaf46d7312dbf572eb497fadc1dc69e8ff6750c1e324ec854f5"
|
|
129
129
|
},
|
|
130
130
|
{
|
|
131
131
|
"path": "dist/sqlite-backend.d.ts.map",
|
|
132
|
-
"sha256": "
|
|
132
|
+
"sha256": "0df025e4853d8420efc87d2b7aa8ea9f36e5b9a0a0be4351a7eb7614caf62b4c"
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"path": "dist/sqlite-backend.js",
|
|
136
|
-
"sha256": "
|
|
136
|
+
"sha256": "4f2a5c61fc27a8aa39f1adfb06215af80bbde0236f1f2a7f73ab65d3ab3f3652"
|
|
137
137
|
},
|
|
138
138
|
{
|
|
139
139
|
"path": "dist/sqlite-backend.js.map",
|
|
140
|
-
"sha256": "
|
|
140
|
+
"sha256": "4106d2a7cfa2ad50ea5497dab8ad18d2102019f05e5a3aca2469ab2ec325ce48"
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
"path": "dist/sqlite-probe.d.ts",
|
|
@@ -155,9 +155,25 @@
|
|
|
155
155
|
"path": "dist/sqlite-probe.js.map",
|
|
156
156
|
"sha256": "083e083ff09058e6802bae5d1415b53d39218a6bf1f31cd6d041e10dbeae9c61"
|
|
157
157
|
},
|
|
158
|
+
{
|
|
159
|
+
"path": "dist/sqlite-readonly.d.ts",
|
|
160
|
+
"sha256": "58f6de024b2cd030c79fa056353537eb595f443868cdc1351d06be28c2dda367"
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
"path": "dist/sqlite-readonly.d.ts.map",
|
|
164
|
+
"sha256": "09688dd92c8d5956865795394ebc47d69108997281a891aa54ce81d5a00c6e36"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"path": "dist/sqlite-readonly.js",
|
|
168
|
+
"sha256": "7dbb93d4bd754e761e73b82ea7cd944743d41b44e4d376af871145555e136f45"
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
"path": "dist/sqlite-readonly.js.map",
|
|
172
|
+
"sha256": "03ddeec35c1389f249b0595f2612b8c3f86e5069b4c3f1be1e30eb6f6949bf1c"
|
|
173
|
+
},
|
|
158
174
|
{
|
|
159
175
|
"path": "package.json",
|
|
160
|
-
"sha256": "
|
|
176
|
+
"sha256": "308ba5ec95c7f2403d52522c4a92e8aac51bd0c514a68517658b4906900bbcd3"
|
|
161
177
|
},
|
|
162
178
|
{
|
|
163
179
|
"path": "src/backend.ts",
|
|
@@ -177,7 +193,7 @@
|
|
|
177
193
|
},
|
|
178
194
|
{
|
|
179
195
|
"path": "src/index.ts",
|
|
180
|
-
"sha256": "
|
|
196
|
+
"sha256": "9152ce2677f8b209bb26a90cf15b1f97dccd7e5fcdcc95e7cf417a698798fc56"
|
|
181
197
|
},
|
|
182
198
|
{
|
|
183
199
|
"path": "src/json-backend.ts",
|
|
@@ -189,13 +205,17 @@
|
|
|
189
205
|
},
|
|
190
206
|
{
|
|
191
207
|
"path": "src/sqlite-backend.ts",
|
|
192
|
-
"sha256": "
|
|
208
|
+
"sha256": "08b618bf7fd351120aac5c4140ae84c2d207c45d708dc42a98f175a99938a11c"
|
|
193
209
|
},
|
|
194
210
|
{
|
|
195
211
|
"path": "src/sqlite-probe.ts",
|
|
196
212
|
"sha256": "28bc13308f61bc553fc36a9c0efcf382522769de20769f3fcef8c3bbb4734b7e"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"path": "src/sqlite-readonly.ts",
|
|
216
|
+
"sha256": "c973b0eeae5567af4c519c178ccb48d496fa1cf2ad226363b26ecc2e7d11dc45"
|
|
197
217
|
}
|
|
198
218
|
]
|
|
199
219
|
},
|
|
200
|
-
"signature": "
|
|
220
|
+
"signature": "s+Nrd0LXNPnN8qKkeIdScdM2meQp7ow1fvWUhRpYdepVas6CFzlxM4qwBBjrP6EnchUVqnu2lu856TFsRpQrBA=="
|
|
201
221
|
}
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ host memory files.
|
|
|
10
10
|
| `backend` | `MemoryBackend`, `MemoryRecord`, `MemoryQuery` | The storage contract |
|
|
11
11
|
| `json-backend` | `JsonFileBackend` | The default backend — pure JS, zero-dependency, JSON-file persistence, scored keyword retrieval |
|
|
12
12
|
| `cascade` | `selectBackend`, `BackendProbe` | Probe optional backends, fall back gracefully |
|
|
13
|
+
| `sqlite-readonly` | `openSqliteReadOnly`, `SqliteReadOnlyStore`, `ReadOnlyHandle`, `ReadOnlyStore`, `OpenReadOnlyOptions` | Read-only SQLite access from directories the process may not write to (in-place → tmp-copy → honest failure) — see "Reading from a read-only directory" below |
|
|
13
14
|
| `reflexion` | `Reflexion` | Record skill outcomes (`record(skillId, outcome, score)`); rank skills |
|
|
14
15
|
| `bridge` | `MemoryBridge`, `importMemoryMarkdown` | Import a host memory markdown file into `MemoryRecord`s |
|
|
15
16
|
|
|
@@ -44,13 +45,56 @@ guaranteed backend if none is available. Heavier backends (`agentdb`, `sql.js`)
|
|
|
44
45
|
are intentionally *not* hard dependencies — see
|
|
45
46
|
`features/extended-a-migration/autonomous-log/decisions.md` (D7.1).
|
|
46
47
|
|
|
48
|
+
## Reading from a read-only directory (ADR-001, `store-readonly-reads`)
|
|
49
|
+
|
|
50
|
+
`{ readonly: true }` alone does **not** read a WAL-mode SQLite database from a directory the
|
|
51
|
+
process cannot write to: a WAL reader still needs to create (or find) `-shm`/`-wal` next to the
|
|
52
|
+
file, and on a read-only-mounted directory that fails with `unable to open database file`
|
|
53
|
+
(measured against better-sqlite3 11.10.0 / SQLite 3.49.2 on a `chattr +i` directory). `openSqliteReadOnly(filePath, opts?)` climbs a three-step ladder instead of assuming step 1 always
|
|
54
|
+
works:
|
|
55
|
+
|
|
56
|
+
1. **In place.** `{ readonly: true, fileMustExist: true }`, then a real read — the WAL error
|
|
57
|
+
surfaces on the first *query*, not on open. Succeeds when a live writer already left
|
|
58
|
+
`-shm`/`-wal` behind, or the directory is writable (note: opening a WAL database whose
|
|
59
|
+
`-wal`/`-shm` were already cleaned up, in a *writable* directory, legitimately (re)creates them
|
|
60
|
+
here — that is SQLite's own WAL mechanics, not a DDL call this package makes).
|
|
61
|
+
2. **Copy.** Only on `SQLITE_CANTOPEN` / "unable to open database file": copies the database
|
|
62
|
+
(+ its `-wal`, if present — **never** `-shm`, which is derived and SQLite recreates it) into a
|
|
63
|
+
fresh `mkdtemp` directory and opens *that* read-only.
|
|
64
|
+
3. **Honest failure.** Any other error, or step 2 itself failing, throws — naming both the file
|
|
65
|
+
path and the original cause. Never a swallowed empty result.
|
|
66
|
+
|
|
67
|
+
`SqliteBackend.openReadOnly(filePath, opts?)` builds on this: it returns a `ReadOnlyStore`
|
|
68
|
+
(`querySync`/`allSync`/`countSync`/`close`) that shares the writer's exact ranking logic
|
|
69
|
+
(`searchPreparedRecords`) but runs **no** `INIT_SQL`, **no** `FTS5_SQL`, and no FTS rebuild — FTS5
|
|
70
|
+
presence is discovered by reading `sqlite_master`, never by (re)creating the table. `put`/
|
|
71
|
+
`putMany`/`remove`/`removeSync` on a `ReadOnlyStore` throw `read-only backend: <method> is not
|
|
72
|
+
available` rather than silently no-op. `close()` closes the underlying connection and then removes
|
|
73
|
+
a step-2 temporary copy, if one was made.
|
|
74
|
+
|
|
75
|
+
**A limit, named honestly:** step 2's copy can race a *concurrent* writer on the same host — a
|
|
76
|
+
copy taken mid-transaction is a torn read. The result is an open/read error naming the cause, not
|
|
77
|
+
silent zeros; retrying the read is the cure. Measured frequency: n=0 (the write window is
|
|
78
|
+
milliseconds); this is a named risk, not a dismissed one.
|
|
79
|
+
|
|
80
|
+
**A second limit, named honestly (fix round 1, LOW #7):** on a store whose `memory_fts` table is
|
|
81
|
+
missing, the writer creates/rebuilds FTS5 on open while the read-only store only checks
|
|
82
|
+
`sqlite_master` and falls back to keyword search — so search results and ranking can differ between
|
|
83
|
+
the two on that specific old/incomplete schema; not fixed here, a named limit.
|
|
84
|
+
|
|
85
|
+
**Реестр мутаций:** этот пакет несёт собственный `test/mutation-registry.json` (ADR-001 C-1…C-3 —
|
|
86
|
+
опенер чтения) — `dz mutation-gate --package packages/@dzhechkov/memory --only <id>` мутирует
|
|
87
|
+
названное свойство в scratch-копии и требует, чтобы `npx vitest run test/sqlite-readonly.test.ts
|
|
88
|
+
test/sqlite-backend.test.ts` покраснел; форму реестра проверяет
|
|
89
|
+
`test/mutation-registry-shape.test.ts`.
|
|
90
|
+
|
|
47
91
|
## Status
|
|
48
92
|
|
|
49
93
|
`0.1.0` — alpha, part of the `extended-a-migration` feature (Phase 7).
|
|
50
94
|
|
|
51
95
|
## Status
|
|
52
96
|
|
|
53
|
-
`0.2.
|
|
97
|
+
`0.2.20` — a signature-only republish; `0.2.11` shipped with a stale manifest. No behaviour changes.
|
|
54
98
|
|
|
55
99
|
`0.2.11` — a query that matched nothing returns nothing (see above). `0.2.10` — the lexical tokenizer is Unicode-aware. It split on `[^a-z0-9]+`, so every non-Latin letter
|
|
56
100
|
was a separator and a Cyrillic query produced **zero terms**: the FTS5 branch was skipped, relevance
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export { selectBackend } from './cascade.js';
|
|
|
12
12
|
export type { BackendProbe, CascadeResult } from './cascade.js';
|
|
13
13
|
export { SqliteBackend } from './sqlite-backend.js';
|
|
14
14
|
export type { SqliteBackendOptions } from './sqlite-backend.js';
|
|
15
|
+
export { openSqliteReadOnly, SqliteReadOnlyStore } from './sqlite-readonly.js';
|
|
16
|
+
export type { ReadOnlyHandle, ReadOnlyStore, OpenReadOnlyOptions } from './sqlite-readonly.js';
|
|
15
17
|
export { SqliteProbe } from './sqlite-probe.js';
|
|
16
18
|
export type { SqliteProbeOptions } from './sqlite-probe.js';
|
|
17
19
|
export { Reflexion } from './reflexion.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,yDAAyD;AACzD,eAAO,MAAM,cAAc,EAAE,MACuD,CAAC;AAErF,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC5G,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,yDAAyD;AACzD,eAAO,MAAM,cAAc,EAAE,MACuD,CAAC;AAErF,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC/E,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACjE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC5G,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export const MEMORY_VERSION = createRequire(import.meta.url)('../package.json').
|
|
|
9
9
|
export { JsonFileBackend } from './json-backend.js';
|
|
10
10
|
export { selectBackend } from './cascade.js';
|
|
11
11
|
export { SqliteBackend } from './sqlite-backend.js';
|
|
12
|
+
export { openSqliteReadOnly, SqliteReadOnlyStore } from './sqlite-readonly.js';
|
|
12
13
|
export { SqliteProbe } from './sqlite-probe.js';
|
|
13
14
|
export { Reflexion } from './reflexion.js';
|
|
14
15
|
export { importMemoryMarkdown, MemoryBridge } from './bridge.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,yDAAyD;AACzD,MAAM,CAAC,MAAM,cAAc,GACxB,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;AAGrF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,yDAAyD;AACzD,MAAM,CAAC,MAAM,cAAc,GACxB,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;AAGrF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE/E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/sqlite-backend.d.ts
CHANGED
|
@@ -8,6 +8,31 @@
|
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
10
|
import type { MemoryBackend, MemoryQuery, MemoryRecord } from './backend.js';
|
|
11
|
+
import type { OpenReadOnlyOptions, ReadOnlyStore } from './sqlite-readonly.js';
|
|
12
|
+
/** FTS5 query — matching records with their relevance rank (lower = better). */
|
|
13
|
+
export declare const FTS5_SEARCH_SQL = "\n SELECT mr.*, fts.rank AS _rank FROM memory_fts fts\n JOIN memory_records mr ON mr.rowid = fts.rowid\n WHERE memory_fts MATCH ?\n ORDER BY fts.rank\n";
|
|
14
|
+
export declare const FTS5_SEARCH_SKILL_SQL = "\n SELECT mr.*, fts.rank AS _rank FROM memory_fts fts\n JOIN memory_records mr ON mr.rowid = fts.rowid\n WHERE memory_fts MATCH ? AND mr.skill_id = ?\n ORDER BY fts.rank\n";
|
|
15
|
+
export declare const ALL_SQL = "SELECT * FROM memory_records";
|
|
16
|
+
export declare const COUNT_SQL = "SELECT COUNT(*) as cnt FROM memory_records";
|
|
17
|
+
export declare const BY_SKILL_SQL = "SELECT * FROM memory_records WHERE skill_id = ?";
|
|
18
|
+
/**
|
|
19
|
+
* The FTS5-ranked / keyword-overlap search decision, extracted from `querySync` so a
|
|
20
|
+
* read-only backend (which prepares the same statements but never runs `INIT_SQL`/`FTS5_SQL`)
|
|
21
|
+
* can share it byte-for-byte instead of forking its own copy. A forked copy is exactly the
|
|
22
|
+
* class of bug documented above (lines 24-29): a reader whose ranking diverges from the
|
|
23
|
+
* writer's silently regresses recall. Behaviorally IDENTICAL to the body it replaced — same
|
|
24
|
+
* sort order, same `stemOf` prefixes, same `relevance > 0` filter, same `terms.length > 0`
|
|
25
|
+
* branch. `@internal` — exported only so `sqlite-readonly.ts` can call it; not part of the
|
|
26
|
+
* package's public surface (see `index.ts`, which does not re-export it).
|
|
27
|
+
*
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export declare function searchPreparedRecords(stmts: {
|
|
31
|
+
fts?: any;
|
|
32
|
+
ftsSkill?: any;
|
|
33
|
+
all: any;
|
|
34
|
+
bySkill: any;
|
|
35
|
+
}, hasFts5: boolean, query: MemoryQuery): MemoryRecord[];
|
|
11
36
|
/** Options for SqliteBackend. */
|
|
12
37
|
export interface SqliteBackendOptions {
|
|
13
38
|
/** Path to the SQLite database file. */
|
|
@@ -33,6 +58,14 @@ export declare class SqliteBackend implements MemoryBackend {
|
|
|
33
58
|
constructor(db: any);
|
|
34
59
|
/** Open (or create) a SQLite database at the given path. */
|
|
35
60
|
static open(filePath: string): SqliteBackend;
|
|
61
|
+
/**
|
|
62
|
+
* Open a SQLite database for READING ONLY (ADR-001, Решение 2). Never runs `INIT_SQL`,
|
|
63
|
+
* `FTS5_SQL`, or the FTS rebuild — the writer's `constructor` above stays untouched byte
|
|
64
|
+
* for byte. Presence of the FTS5 table is discovered by reading `sqlite_master`, not by
|
|
65
|
+
* attempting to (re)create it. `put`/`putMany`/`remove`/`removeSync` on the returned store
|
|
66
|
+
* throw `read-only backend: <method> is not available`.
|
|
67
|
+
*/
|
|
68
|
+
static openReadOnly(filePath: string, opts?: OpenReadOnlyOptions): ReadOnlyStore;
|
|
36
69
|
put(record: MemoryRecord): Promise<void>;
|
|
37
70
|
query(query: MemoryQuery): Promise<MemoryRecord[]>;
|
|
38
71
|
/**
|
|
@@ -53,4 +86,6 @@ export declare class SqliteBackend implements MemoryBackend {
|
|
|
53
86
|
/** Close the database connection. */
|
|
54
87
|
close(): void;
|
|
55
88
|
}
|
|
89
|
+
/** Convert a raw SQLite row to a MemoryRecord. */
|
|
90
|
+
export declare function rowToRecord(row: any): MemoryRecord;
|
|
56
91
|
//# sourceMappingURL=sqlite-backend.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-backend.d.ts","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-backend.d.ts","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAwG/E,gFAAgF;AAChF,eAAO,MAAM,eAAe,gKAK3B,CAAC;AAEF,eAAO,MAAM,qBAAqB,oLAKjC,CAAC;AASF,eAAO,MAAM,OAAO,iCAAiC,CAAC;AACtD,eAAO,MAAM,SAAS,+CAA+C,CAAC;AACtE,eAAO,MAAM,YAAY,oDAAoD,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,EAAE,GAAG,CAAC;IAAC,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,GAAG,CAAA;CAAE,EAC5D,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,WAAW,GACjB,YAAY,EAAE,CAmEhB;AAED,iCAAiC;AACjC,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,aAAa;IACjD,QAAQ,CAAC,IAAI,YAAY;IAEzB,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAM;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAM;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAM;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAM;IAClC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAkB;IACrD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,EAAE,EAAE,GAAG;IAyBnB,4DAA4D;IAC5D,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa;IAW5C;;;;;;OAMG;IACH,MAAM,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,aAAa;IAmBhF,GAAG,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAaxC,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIlD;;;;OAIG;IACH,SAAS,CAAC,KAAK,EAAE,WAAW,GAAG,YAAY,EAAE;IAQ7C,GAAG,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAI9B,6CAA6C;IAC7C,OAAO,IAAI,YAAY,EAAE;IAIzB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKjC,sIAAsI;IACtI,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI5B,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAIxB,oEAAoE;IACpE,OAAO,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI;IAiB/C,qCAAqC;IACrC,KAAK,IAAI,IAAI;CAGd;AAED,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,YAAY,CAUlD"}
|
package/dist/sqlite-backend.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { createRequire } from 'node:module';
|
|
11
11
|
import { mkdirSync } from 'node:fs';
|
|
12
12
|
import { dirname } from 'node:path';
|
|
13
|
+
import { openSqliteReadOnly, SqliteReadOnlyStore } from './sqlite-readonly.js';
|
|
13
14
|
const require = createRequire(import.meta.url);
|
|
14
15
|
const DEFAULT_LIMIT = 20;
|
|
15
16
|
/**
|
|
@@ -117,13 +118,13 @@ const FTS5_SQL = `
|
|
|
117
118
|
END;
|
|
118
119
|
`;
|
|
119
120
|
/** FTS5 query — matching records with their relevance rank (lower = better). */
|
|
120
|
-
const FTS5_SEARCH_SQL = `
|
|
121
|
+
export const FTS5_SEARCH_SQL = `
|
|
121
122
|
SELECT mr.*, fts.rank AS _rank FROM memory_fts fts
|
|
122
123
|
JOIN memory_records mr ON mr.rowid = fts.rowid
|
|
123
124
|
WHERE memory_fts MATCH ?
|
|
124
125
|
ORDER BY fts.rank
|
|
125
126
|
`;
|
|
126
|
-
const FTS5_SEARCH_SKILL_SQL = `
|
|
127
|
+
export const FTS5_SEARCH_SKILL_SQL = `
|
|
127
128
|
SELECT mr.*, fts.rank AS _rank FROM memory_fts fts
|
|
128
129
|
JOIN memory_records mr ON mr.rowid = fts.rowid
|
|
129
130
|
WHERE memory_fts MATCH ? AND mr.skill_id = ?
|
|
@@ -134,9 +135,83 @@ const UPSERT_SQL = `
|
|
|
134
135
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
135
136
|
`;
|
|
136
137
|
const DELETE_SQL = 'DELETE FROM memory_records WHERE id = ?';
|
|
137
|
-
const ALL_SQL = 'SELECT * FROM memory_records';
|
|
138
|
-
const COUNT_SQL = 'SELECT COUNT(*) as cnt FROM memory_records';
|
|
139
|
-
const BY_SKILL_SQL = 'SELECT * FROM memory_records WHERE skill_id = ?';
|
|
138
|
+
export const ALL_SQL = 'SELECT * FROM memory_records';
|
|
139
|
+
export const COUNT_SQL = 'SELECT COUNT(*) as cnt FROM memory_records';
|
|
140
|
+
export const BY_SKILL_SQL = 'SELECT * FROM memory_records WHERE skill_id = ?';
|
|
141
|
+
/**
|
|
142
|
+
* The FTS5-ranked / keyword-overlap search decision, extracted from `querySync` so a
|
|
143
|
+
* read-only backend (which prepares the same statements but never runs `INIT_SQL`/`FTS5_SQL`)
|
|
144
|
+
* can share it byte-for-byte instead of forking its own copy. A forked copy is exactly the
|
|
145
|
+
* class of bug documented above (lines 24-29): a reader whose ranking diverges from the
|
|
146
|
+
* writer's silently regresses recall. Behaviorally IDENTICAL to the body it replaced — same
|
|
147
|
+
* sort order, same `stemOf` prefixes, same `relevance > 0` filter, same `terms.length > 0`
|
|
148
|
+
* branch. `@internal` — exported only so `sqlite-readonly.ts` can call it; not part of the
|
|
149
|
+
* package's public surface (see `index.ts`, which does not re-export it).
|
|
150
|
+
*
|
|
151
|
+
* @internal
|
|
152
|
+
*/
|
|
153
|
+
export function searchPreparedRecords(stmts, hasFts5, query) {
|
|
154
|
+
const limit = query.limit ?? DEFAULT_LIMIT;
|
|
155
|
+
// FTS5 path — use SQLite full-text search when available and text query provided
|
|
156
|
+
if (hasFts5 && query.text !== undefined && query.text.trim().length > 0) {
|
|
157
|
+
try {
|
|
158
|
+
// FTS5 query syntax: simple terms joined by spaces (implicit AND → OR with ranking)
|
|
159
|
+
// Each token also contributes its prefix-stem as `stem*` (see stemOf): the exact word and
|
|
160
|
+
// its inflections all match, and a row holding the exact token matches BOTH disjuncts, so
|
|
161
|
+
// bm25 ranks it at or above a prefix-only row. Tokens are \p{L}\p{N}-only — safe to
|
|
162
|
+
// interpolate; the star is appended HERE, never taken from user text.
|
|
163
|
+
const ftsQuery = tokenize(query.text)
|
|
164
|
+
.flatMap((t) => {
|
|
165
|
+
const stem = stemOf(t);
|
|
166
|
+
return stem === null ? [t] : [t, stem + '*'];
|
|
167
|
+
})
|
|
168
|
+
.join(' OR ');
|
|
169
|
+
if (ftsQuery.length > 0) {
|
|
170
|
+
let rows;
|
|
171
|
+
if (query.skillId !== undefined) {
|
|
172
|
+
rows = stmts.ftsSkill.all(ftsQuery, query.skillId);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
rows = stmts.fts.all(ftsQuery);
|
|
176
|
+
}
|
|
177
|
+
// Rank by FTS5 relevance FIRST (lower rank = better match), with score
|
|
178
|
+
// then timestamp as a true tiebreak between equally-relevant rows. Score
|
|
179
|
+
// must NOT be primary — that would discard FTS5's relevance signal and
|
|
180
|
+
// diverge from the JSON backend (which also ranks relevance-primary).
|
|
181
|
+
const ranked = rows
|
|
182
|
+
.map((row) => ({ record: rowToRecord(row), rank: row._rank }))
|
|
183
|
+
.sort((a, b) => a.rank - b.rank ||
|
|
184
|
+
b.record.score - a.record.score ||
|
|
185
|
+
b.record.timestamp.localeCompare(a.record.timestamp));
|
|
186
|
+
return ranked.slice(0, limit).map((entry) => entry.record);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// FTS5 query failed (e.g., special chars) — fall through to keyword approach
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Keyword overlap fallback
|
|
194
|
+
const terms = query.text !== undefined ? tokenize(query.text) : [];
|
|
195
|
+
let rows;
|
|
196
|
+
if (query.skillId !== undefined) {
|
|
197
|
+
rows = stmts.bySkill.all(query.skillId);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
rows = stmts.all.all();
|
|
201
|
+
}
|
|
202
|
+
const records = rows.map(rowToRecord);
|
|
203
|
+
const ranked = records
|
|
204
|
+
.map((record) => ({ record, relevance: relevanceOf(record, terms) }))
|
|
205
|
+
.sort((a, b) => b.relevance - a.relevance ||
|
|
206
|
+
b.record.score - a.record.score ||
|
|
207
|
+
b.record.timestamp.localeCompare(a.record.timestamp));
|
|
208
|
+
// The SAME guard the JSON backend applies, so a store's answers never depend on which backend is
|
|
209
|
+
// installed. This is the keyword FALLBACK; the FTS5 path above already returns zero honestly and
|
|
210
|
+
// is untouched. With no usable terms there was nothing to match on, so the store still comes back
|
|
211
|
+
// ranked by confidence — that distinction is the whole decision (ADR-001).
|
|
212
|
+
const filtered = terms.length > 0 ? ranked.filter((entry) => entry.relevance > 0) : ranked;
|
|
213
|
+
return filtered.slice(0, limit).map((entry) => entry.record);
|
|
214
|
+
}
|
|
140
215
|
/**
|
|
141
216
|
* SQLite-backed memory store. Write-through, WAL mode, indexed.
|
|
142
217
|
*
|
|
@@ -188,6 +263,33 @@ export class SqliteBackend {
|
|
|
188
263
|
db.pragma('synchronous = NORMAL');
|
|
189
264
|
return new SqliteBackend(db);
|
|
190
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Open a SQLite database for READING ONLY (ADR-001, Решение 2). Never runs `INIT_SQL`,
|
|
268
|
+
* `FTS5_SQL`, or the FTS rebuild — the writer's `constructor` above stays untouched byte
|
|
269
|
+
* for byte. Presence of the FTS5 table is discovered by reading `sqlite_master`, not by
|
|
270
|
+
* attempting to (re)create it. `put`/`putMany`/`remove`/`removeSync` on the returned store
|
|
271
|
+
* throw `read-only backend: <method> is not available`.
|
|
272
|
+
*/
|
|
273
|
+
static openReadOnly(filePath, opts) {
|
|
274
|
+
const handle = openSqliteReadOnly(filePath, opts);
|
|
275
|
+
try {
|
|
276
|
+
return new SqliteReadOnlyStore(handle);
|
|
277
|
+
}
|
|
278
|
+
catch (err) {
|
|
279
|
+
// The constructor prepares statements (`sqlite_master` lookup, ALL/COUNT/BY_SKILL) against
|
|
280
|
+
// an ALREADY-OPEN handle — if any of that throws (e.g. a valid SQLite file missing
|
|
281
|
+
// `memory_records`), the handle must not leak: close the connection and remove a tmp-copy
|
|
282
|
+
// before rethrowing (fix round 1, HIGH #2).
|
|
283
|
+
try {
|
|
284
|
+
handle.db.close();
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
// already failing on the caller's side — a close failure here must not mask the real cause
|
|
288
|
+
}
|
|
289
|
+
handle.cleanup();
|
|
290
|
+
throw err;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
191
293
|
put(record) {
|
|
192
294
|
this.upsertStmt.run(record.id, record.skillId, record.text, record.score, record.outcome, record.timestamp, record.metadata ? JSON.stringify(record.metadata) : null);
|
|
193
295
|
return Promise.resolve();
|
|
@@ -201,66 +303,7 @@ export class SqliteBackend {
|
|
|
201
303
|
* path (a recommender / `dz recall`) can query the store without an async ripple.
|
|
202
304
|
*/
|
|
203
305
|
querySync(query) {
|
|
204
|
-
|
|
205
|
-
// FTS5 path — use SQLite full-text search when available and text query provided
|
|
206
|
-
if (this.hasFts5 && query.text !== undefined && query.text.trim().length > 0) {
|
|
207
|
-
try {
|
|
208
|
-
// FTS5 query syntax: simple terms joined by spaces (implicit AND → OR with ranking)
|
|
209
|
-
// Each token also contributes its prefix-stem as `stem*` (see stemOf): the exact word and
|
|
210
|
-
// its inflections all match, and a row holding the exact token matches BOTH disjuncts, so
|
|
211
|
-
// bm25 ranks it at or above a prefix-only row. Tokens are \p{L}\p{N}-only — safe to
|
|
212
|
-
// interpolate; the star is appended HERE, never taken from user text.
|
|
213
|
-
const ftsQuery = tokenize(query.text)
|
|
214
|
-
.flatMap((t) => {
|
|
215
|
-
const stem = stemOf(t);
|
|
216
|
-
return stem === null ? [t] : [t, stem + '*'];
|
|
217
|
-
})
|
|
218
|
-
.join(' OR ');
|
|
219
|
-
if (ftsQuery.length > 0) {
|
|
220
|
-
let rows;
|
|
221
|
-
if (query.skillId !== undefined) {
|
|
222
|
-
rows = this.ftsSearchSkillStmt.all(ftsQuery, query.skillId);
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
rows = this.ftsSearchStmt.all(ftsQuery);
|
|
226
|
-
}
|
|
227
|
-
// Rank by FTS5 relevance FIRST (lower rank = better match), with score
|
|
228
|
-
// then timestamp as a true tiebreak between equally-relevant rows. Score
|
|
229
|
-
// must NOT be primary — that would discard FTS5's relevance signal and
|
|
230
|
-
// diverge from the JSON backend (which also ranks relevance-primary).
|
|
231
|
-
const ranked = rows
|
|
232
|
-
.map((row) => ({ record: rowToRecord(row), rank: row._rank }))
|
|
233
|
-
.sort((a, b) => a.rank - b.rank ||
|
|
234
|
-
b.record.score - a.record.score ||
|
|
235
|
-
b.record.timestamp.localeCompare(a.record.timestamp));
|
|
236
|
-
return ranked.slice(0, limit).map((entry) => entry.record);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
catch {
|
|
240
|
-
// FTS5 query failed (e.g., special chars) — fall through to keyword approach
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
// Keyword overlap fallback
|
|
244
|
-
const terms = query.text !== undefined ? tokenize(query.text) : [];
|
|
245
|
-
let rows;
|
|
246
|
-
if (query.skillId !== undefined) {
|
|
247
|
-
rows = this.bySkillStmt.all(query.skillId);
|
|
248
|
-
}
|
|
249
|
-
else {
|
|
250
|
-
rows = this.allStmt.all();
|
|
251
|
-
}
|
|
252
|
-
const records = rows.map(rowToRecord);
|
|
253
|
-
const ranked = records
|
|
254
|
-
.map((record) => ({ record, relevance: relevanceOf(record, terms) }))
|
|
255
|
-
.sort((a, b) => b.relevance - a.relevance ||
|
|
256
|
-
b.record.score - a.record.score ||
|
|
257
|
-
b.record.timestamp.localeCompare(a.record.timestamp));
|
|
258
|
-
// The SAME guard the JSON backend applies, so a store's answers never depend on which backend is
|
|
259
|
-
// installed. This is the keyword FALLBACK; the FTS5 path above already returns zero honestly and
|
|
260
|
-
// is untouched. With no usable terms there was nothing to match on, so the store still comes back
|
|
261
|
-
// ranked by confidence — that distinction is the whole decision (ADR-001).
|
|
262
|
-
const filtered = terms.length > 0 ? ranked.filter((entry) => entry.relevance > 0) : ranked;
|
|
263
|
-
return filtered.slice(0, limit).map((entry) => entry.record);
|
|
306
|
+
return searchPreparedRecords({ fts: this.ftsSearchStmt, ftsSkill: this.ftsSearchSkillStmt, all: this.allStmt, bySkill: this.bySkillStmt }, this.hasFts5, query);
|
|
264
307
|
}
|
|
265
308
|
all() {
|
|
266
309
|
return Promise.resolve(this.allSync());
|
|
@@ -295,7 +338,7 @@ export class SqliteBackend {
|
|
|
295
338
|
}
|
|
296
339
|
}
|
|
297
340
|
/** Convert a raw SQLite row to a MemoryRecord. */
|
|
298
|
-
function rowToRecord(row) {
|
|
341
|
+
export function rowToRecord(row) {
|
|
299
342
|
return {
|
|
300
343
|
id: row.id,
|
|
301
344
|
skillId: row.skill_id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-backend.js","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"sqlite-backend.js","sourceRoot":"","sources":["../src/sqlite-backend.ts"],"names":[],"mappings":"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"}
|