dsh-trivium 0.4.16 → 0.4.18
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.md +14 -3
- package/README.zh-CN.md +14 -3
- package/lib/atomic.js +42 -0
- package/lib/client.js +4 -1
- package/lib/index.js +19 -4
- package/lib/settings.js +9 -5
- package/lib/sidecar.js +4 -19
- package/lib/store.js +9 -0
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ The kernel stays close to DSH: no extra process, no extra nav by default. Pin me
|
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
|
-
> Prerequisite: install [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) and start `dsh web` at least once. Tested host: `@deepseek-ai/dsh@0.1.
|
|
19
|
+
> Prerequisite: install [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) and start `dsh web` at least once. Tested host: `@deepseek-ai/dsh@0.1.5-rc.1` (`dsh-llm` / `dsh-tools` peers also accept `0.1.1-rc.2` and `0.1.0-rc.8`).
|
|
20
20
|
|
|
21
21
|
```sh
|
|
22
22
|
dsh plugin --profile web add dsh-trivium
|
|
@@ -46,8 +46,11 @@ Local source checkout:
|
|
|
46
46
|
```sh
|
|
47
47
|
npm install
|
|
48
48
|
node scripts/link-dsh.mjs
|
|
49
|
+
npm test # optional: runs every offline smoke script
|
|
49
50
|
```
|
|
50
51
|
|
|
52
|
+
`npm test` needs no DSH host and never touches your real `~/.dsh/trivium.json` (each script gets its own temp settings file). Pass a substring to narrow it: `npm test -- p8`.
|
|
53
|
+
|
|
51
54
|
Then restart `dsh web`.
|
|
52
55
|
|
|
53
56
|
## Updating
|
|
@@ -242,6 +245,14 @@ Off by default. After you turn **Chips (memory whitelist)** on in Settings, the
|
|
|
242
245
|
|
|
243
246
|
## Changelog
|
|
244
247
|
|
|
248
|
+
**0.4.18** — `~/.dsh/trivium.json` is now written atomically (a truncated write used to reset every switch and chip pin silently) and is owner-only on POSIX. `npm test` runs every offline smoke script with isolated settings, and `smoke-p0` pins the hybrid text-index-after-reopen invariant. Four tools / short map / find paths unchanged.
|
|
249
|
+
|
|
250
|
+
**0.4.17** — Storage bump to `triviumdb@0.8.8` (engine behavior unchanged from 0.4.16). Host adaptation for `@deepseek-ai/dsh@0.1.5-rc.1`: `session.header.seedLength` was removed, so the session-layer fork cut now reads `session.inheritedEventCount`. Four tools / short map / find paths unchanged.
|
|
251
|
+
|
|
252
|
+
**0.4.16** — Storage bump to `triviumdb@0.8.4`: the engine constructor now takes an options object (`{ dim }`) instead of numeric positional args, and TQL rows return string ids that the plugin coerces back to numbers. Four tools / short map / find paths unchanged.
|
|
253
|
+
|
|
254
|
+
**0.4.15** — Storage bump to `triviumdb@0.8.2`. The settings API rejects non-loopback requests and unlisted `cwd` values; the extract buffer is cleared after a run and `savePending` no longer blocks the turn.
|
|
255
|
+
|
|
245
256
|
**0.4.14** — Storage bump to `triviumdb@0.8.1` (harder WAL/locks, optional read-only share, ARM64 fixes). Four tools / short map / find paths unchanged.
|
|
246
257
|
|
|
247
258
|
**0.4.13** — Title-bar tab is **Chips** (short); the Settings switch stays **Chips (memory whitelist)**. Toggling the session layer refreshes the Chips tab immediately. Settings / Chips screenshots updated.
|
|
@@ -294,8 +305,8 @@ Local source checkout: `npm install`, then `node scripts/link-dsh.mjs`, then res
|
|
|
294
305
|
## Release info
|
|
295
306
|
|
|
296
307
|
- GitHub: https://github.com/QWQcool/dsh-trivium
|
|
297
|
-
- npm: [`dsh-trivium@0.4.
|
|
298
|
-
- Tested host: `@deepseek-ai/dsh@0.1.
|
|
308
|
+
- npm: [`dsh-trivium@0.4.18`](https://www.npmjs.com/package/dsh-trivium)
|
|
309
|
+
- Tested host: `@deepseek-ai/dsh@0.1.5-rc.1` (also `0.1.1-rc.2` / `0.1.0-rc.8`)
|
|
299
310
|
- License: MIT (depends on [TriviumDB](https://github.com/YoKONCy/TriviumDB), Apache-2.0)
|
|
300
311
|
|
|
301
312
|
---
|
package/README.zh-CN.md
CHANGED
|
@@ -14,7 +14,7 @@ DeepSeek Harness 的进程内图记忆插件:按节点和边记,默认少注
|
|
|
14
14
|
|
|
15
15
|
## 安装
|
|
16
16
|
|
|
17
|
-
> 前提:已安装 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 并至少启动过一次 `dsh web`。当前测试宿主:`@deepseek-ai/dsh@0.1.
|
|
17
|
+
> 前提:已安装 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 并至少启动过一次 `dsh web`。当前测试宿主:`@deepseek-ai/dsh@0.1.5-rc.1`(`dsh-llm` / `dsh-tools` peer 兼容 `0.1.1-rc.2` 与 `0.1.0-rc.8`)。
|
|
18
18
|
|
|
19
19
|
```sh
|
|
20
20
|
dsh plugin --profile web add dsh-trivium
|
|
@@ -44,8 +44,11 @@ clone / `git pull` 后若 jsonl 变了会自动导回。设置里 **Git 旁路**
|
|
|
44
44
|
```sh
|
|
45
45
|
npm install
|
|
46
46
|
node scripts/link-dsh.mjs
|
|
47
|
+
npm test # 可选:跑全部离线 smoke
|
|
47
48
|
```
|
|
48
49
|
|
|
50
|
+
`npm test` 不需要 DSH 宿主,也不会碰你真实的 `~/.dsh/trivium.json`(每个脚本用独立的临时设置文件)。想只跑一部分就传子串:`npm test -- p8`。
|
|
51
|
+
|
|
49
52
|
然后重启 `dsh web`。
|
|
50
53
|
|
|
51
54
|
## 更新
|
|
@@ -238,6 +241,14 @@ Trivium 是记忆内核,不是日记、日历或聊天伴侣。它按**节点
|
|
|
238
241
|
|
|
239
242
|
## 更新说明
|
|
240
243
|
|
|
244
|
+
**0.4.18** — `~/.dsh/trivium.json` 改为原子写(此前写到一半被杀会让所有开关和芯片钉选静默回默认),POSIX 下权限收紧为仅属主可读。新增 `npm test` 聚合入口(每个脚本用独立临时设置文件),`smoke-p0` 新增「重开后 hybrid 文本索引仍可用」的断言。四工具 / 短地图 / find 路径行为不变。
|
|
245
|
+
|
|
246
|
+
**0.4.17** — 存储升到 `triviumdb@0.8.8`(引擎行为相对 0.4.16 不变)。适配宿主 `@deepseek-ai/dsh@0.1.5-rc.1`:`session.header.seedLength` 被移除,会话层 fork 切点改读 `session.inheritedEventCount`。四工具 / 短地图 / find 路径不变。
|
|
247
|
+
|
|
248
|
+
**0.4.16** — 存储升到 `triviumdb@0.8.4`:引擎构造函数改为 options 对象(`{ dim }`),不再收数字位置参数;TQL 行 id 变字符串,插件内部归一为数字。四工具 / 短地图 / find 路径不变。
|
|
249
|
+
|
|
250
|
+
**0.4.15** — 存储升到 `triviumdb@0.8.2`。设置 API 拒绝非回环请求与未登记 `cwd`;抽取后清空缓冲,`savePending` 不再阻塞当轮。
|
|
251
|
+
|
|
241
252
|
**0.4.14** — 存储升到 `triviumdb@0.8.1`(WAL/锁更硬、可选只读共享、ARM64 修复)。四工具 / 短地图 / find 路径不变。
|
|
242
253
|
|
|
243
254
|
**0.4.13** — 标题栏只显示 **芯片**;设置里的开关仍叫 **芯片(记忆白名单)**。会话层开关立刻刷新芯片页。设置 / 芯片截图更新。
|
|
@@ -290,8 +301,8 @@ Trivium 是记忆内核,不是日记、日历或聊天伴侣。它按**节点
|
|
|
290
301
|
## 发布信息
|
|
291
302
|
|
|
292
303
|
- GitHub: https://github.com/QWQcool/dsh-trivium
|
|
293
|
-
- npm: [`dsh-trivium@0.4.
|
|
294
|
-
- 测试宿主:`@deepseek-ai/dsh@0.1.1-rc.2
|
|
304
|
+
- npm: [`dsh-trivium@0.4.18`](https://www.npmjs.com/package/dsh-trivium)
|
|
305
|
+
- 测试宿主:`@deepseek-ai/dsh@0.1.5-rc.1`(兼 `0.1.1-rc.2` / `0.1.0-rc.8`)
|
|
295
306
|
- License: MIT(依赖 [TriviumDB](https://github.com/YoKONCy/TriviumDB) 为 Apache-2.0)
|
|
296
307
|
|
|
297
308
|
---
|
package/lib/atomic.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic text write: write a sibling temp file, then rename over the target.
|
|
3
|
+
*
|
|
4
|
+
* `writeFileSync(file, ...)` truncates in place, so a crash (or a kill) between
|
|
5
|
+
* truncate and write leaves a half-written file. For the settings file that
|
|
6
|
+
* means `readUiSettings()` silently returns `{}` and every switch / chip pin
|
|
7
|
+
* resets. A rename is atomic on the same volume, so readers see either the old
|
|
8
|
+
* file or the new one, never a truncated one.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param file absolute target path
|
|
15
|
+
* @param text UTF-8 payload
|
|
16
|
+
* @param mode optional POSIX mode (ignored on Windows). Applied to the temp
|
|
17
|
+
* file, which the rename then promotes to the target.
|
|
18
|
+
* @returns the target path
|
|
19
|
+
*/
|
|
20
|
+
export function atomicWriteFile(file, text, { mode } = {}) {
|
|
21
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
22
|
+
const tmp = `${file}.tmp`;
|
|
23
|
+
// Drop a stale temp first so `mode` is actually applied on creation.
|
|
24
|
+
try {
|
|
25
|
+
unlinkSync(tmp);
|
|
26
|
+
} catch {
|
|
27
|
+
// nothing stale to remove
|
|
28
|
+
}
|
|
29
|
+
writeFileSync(tmp, text, mode === undefined ? "utf8" : { encoding: "utf8", mode });
|
|
30
|
+
try {
|
|
31
|
+
renameSync(tmp, file);
|
|
32
|
+
} catch {
|
|
33
|
+
// Windows cannot always rename over an existing file; clear and retry.
|
|
34
|
+
try {
|
|
35
|
+
unlinkSync(file);
|
|
36
|
+
} catch {
|
|
37
|
+
// destination may not exist yet
|
|
38
|
+
}
|
|
39
|
+
renameSync(tmp, file);
|
|
40
|
+
}
|
|
41
|
+
return file;
|
|
42
|
+
}
|
package/lib/client.js
CHANGED
|
@@ -3283,7 +3283,10 @@ window.__ModuleLoader__.load({
|
|
|
3283
3283
|
open: (id) => sessions.open(id),
|
|
3284
3284
|
seedLengthOf: (id) => {
|
|
3285
3285
|
try {
|
|
3286
|
-
const
|
|
3286
|
+
const face = sessions.binding?.(id)?.session;
|
|
3287
|
+
// DSH 0.1.5 dropped header.seedLength; the live session exposes
|
|
3288
|
+
// the fork-inherited prefix length as inheritedEventCount.
|
|
3289
|
+
const seed = face?.inheritedEventCount ?? face?.header?.seedLength;
|
|
3287
3290
|
const n = Number(seed);
|
|
3288
3291
|
return Number.isFinite(n) && n > 0 ? n : null;
|
|
3289
3292
|
} catch {
|
package/lib/index.js
CHANGED
|
@@ -68,6 +68,19 @@ function sessionKey(session) {
|
|
|
68
68
|
return String(session?.id || session?.header?.id || "");
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Fork-inherited prefix length. DSH 0.1.5 moved this off the header
|
|
73
|
+
* (`header.seedLength` is now rejected as an invalid field) onto the live
|
|
74
|
+
* session as `inheritedEventCount`; keep the header read as a fallback for
|
|
75
|
+
* older hosts.
|
|
76
|
+
*/
|
|
77
|
+
function sessionSeedLength(session) {
|
|
78
|
+
const live = session?.inheritedEventCount;
|
|
79
|
+
if (Number.isFinite(Number(live))) return Number(live);
|
|
80
|
+
const legacy = session?.header?.seedLength;
|
|
81
|
+
return Number.isFinite(Number(legacy)) ? Number(legacy) : 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
71
84
|
function bufOf(session) {
|
|
72
85
|
const id = sessionKey(session);
|
|
73
86
|
if (!buffers.has(id)) {
|
|
@@ -296,15 +309,15 @@ export function apply(ctx, config = {}) {
|
|
|
296
309
|
const session = agent.session;
|
|
297
310
|
const sessionId = sessionKey(session);
|
|
298
311
|
if (sessionId && sessionLayerEnabledOf(optionsOf())) {
|
|
299
|
-
const seedLen = session
|
|
312
|
+
const seedLen = sessionSeedLength(session);
|
|
300
313
|
ensureTail(db, sessionId, {
|
|
301
|
-
atSeq:
|
|
314
|
+
atSeq: seedLen > 0 ? seedLen - 1 : 0,
|
|
302
315
|
});
|
|
303
316
|
if (session?.header?.parentSession) {
|
|
304
317
|
syncForkLineage(db, {
|
|
305
318
|
childSessionId: sessionId,
|
|
306
319
|
parentSessionId: session.header.parentSession,
|
|
307
|
-
atSeq:
|
|
320
|
+
atSeq: seedLen > 0 ? seedLen - 1 : undefined,
|
|
308
321
|
});
|
|
309
322
|
}
|
|
310
323
|
}
|
|
@@ -441,10 +454,12 @@ export function apply(ctx, config = {}) {
|
|
|
441
454
|
extra.push(
|
|
442
455
|
createUserMessage({
|
|
443
456
|
content: [{ type: "text", text: pin.text }],
|
|
457
|
+
// No `form`: "pins" was never in DSH's ContextForm union, so the
|
|
458
|
+
// client already rendered it as undeclared (opaque) context.
|
|
459
|
+
// Omitting the field is the documented default for that.
|
|
444
460
|
source: {
|
|
445
461
|
kind: "plugin",
|
|
446
462
|
plugin: "dsh-trivium",
|
|
447
|
-
form: "pins",
|
|
448
463
|
},
|
|
449
464
|
}),
|
|
450
465
|
);
|
package/lib/settings.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir, tmpdir } from "node:os";
|
|
3
|
-
import {
|
|
3
|
+
import { join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { atomicWriteFile } from "./atomic.js";
|
|
4
5
|
import { looksHttpUrl } from "./embed.js";
|
|
5
6
|
|
|
6
7
|
export const SETTINGS_FILE = join(homedir(), ".dsh", "trivium.json");
|
|
7
8
|
|
|
9
|
+
/** This file can hold an embedding API key; keep it owner-only. No-op on Windows. */
|
|
10
|
+
const SETTINGS_FILE_MODE = 0o600;
|
|
11
|
+
|
|
8
12
|
/** Tests may set DSH_TRIVIUM_SETTINGS to a temp file so we never touch the live home config. */
|
|
9
13
|
export function settingsFilePath() {
|
|
10
14
|
const override = String(process.env.DSH_TRIVIUM_SETTINGS || "").trim();
|
|
@@ -12,9 +16,9 @@ export function settingsFilePath() {
|
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
function persistSettings(next) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
// Atomic: a truncated settings file would make readUiSettings() return {}
|
|
20
|
+
// and silently reset every switch / chip pin.
|
|
21
|
+
atomicWriteFile(settingsFilePath(), JSON.stringify(next, null, 2), { mode: SETTINGS_FILE_MODE });
|
|
18
22
|
return next;
|
|
19
23
|
}
|
|
20
24
|
|
package/lib/sidecar.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/** Git-friendly JSONL sidecar next to trivium.tdb. No embeddings, episodes, or chip pins. */
|
|
2
2
|
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
import { existsSync,
|
|
5
|
-
import {
|
|
4
|
+
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
|
+
import { atomicWriteFile } from "./atomic.js";
|
|
6
7
|
import { BUSINESS_EDGES } from "./schema.js";
|
|
7
8
|
import {
|
|
8
9
|
gitSidecarEnabledOf,
|
|
@@ -196,22 +197,6 @@ function readSidecarFile(cwd) {
|
|
|
196
197
|
}
|
|
197
198
|
}
|
|
198
199
|
|
|
199
|
-
function atomicWrite(file, text) {
|
|
200
|
-
mkdirSync(dirname(file), { recursive: true });
|
|
201
|
-
const tmp = `${file}.tmp`;
|
|
202
|
-
writeFileSync(tmp, text, "utf8");
|
|
203
|
-
try {
|
|
204
|
-
renameSync(tmp, file);
|
|
205
|
-
} catch {
|
|
206
|
-
try {
|
|
207
|
-
unlinkSync(file);
|
|
208
|
-
} catch {
|
|
209
|
-
// dest may not exist
|
|
210
|
-
}
|
|
211
|
-
renameSync(tmp, file);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
200
|
function rememberHash(cwd, hash) {
|
|
216
201
|
const key = resolve(cwd);
|
|
217
202
|
memoryHash.set(key, hash);
|
|
@@ -293,7 +278,7 @@ export function writeSidecarNow(cwd, db, { force = false } = {}) {
|
|
|
293
278
|
if (!force && hash === knownHash(cwd) && existsSync(file)) {
|
|
294
279
|
return { path: file, hash, wrote: false };
|
|
295
280
|
}
|
|
296
|
-
|
|
281
|
+
atomicWriteFile(file, text);
|
|
297
282
|
rememberHash(cwd, hash);
|
|
298
283
|
return { path: file, hash, wrote: true };
|
|
299
284
|
}
|
package/lib/store.js
CHANGED
|
@@ -114,6 +114,8 @@ export async function openWorkspaceDb(cwd) {
|
|
|
114
114
|
if (dbs.has(key)) return dbs.get(key);
|
|
115
115
|
mkdirSync(join(key, ".dsh"), { recursive: true });
|
|
116
116
|
const Engine = loadEngine();
|
|
117
|
+
// triviumdb 0.8.3+ removed numeric positional constructor args
|
|
118
|
+
// (TDB_API_MIGRATION_REQUIRED); dim now rides an options object.
|
|
117
119
|
const db = new Engine(dbPathFor(key), { dim: DIM });
|
|
118
120
|
try {
|
|
119
121
|
db.createIndex("type");
|
|
@@ -122,6 +124,11 @@ export async function openWorkspaceDb(cwd) {
|
|
|
122
124
|
// indexes may already exist
|
|
123
125
|
}
|
|
124
126
|
ensureWorkspaceRoot(db, cwd);
|
|
127
|
+
// Do NOT remove. triviumdb opens with `loadTextIndex: false` by default, so
|
|
128
|
+
// the persisted BM25 sidecar is not loaded and `searchHybrid`'s text channel
|
|
129
|
+
// returns nothing after a reopen until the index is rebuilt here. `searchNodes`
|
|
130
|
+
// cannot paper over the loss: its keyword fallback still scores, so a
|
|
131
|
+
// regression is silent at the tool level. scripts/smoke-p0.mjs pins this.
|
|
125
132
|
rebuildTextIndex(db);
|
|
126
133
|
dbs.set(key, db);
|
|
127
134
|
try {
|
|
@@ -191,6 +198,8 @@ function normalizeTql(rows) {
|
|
|
191
198
|
return rows
|
|
192
199
|
.map((row) => {
|
|
193
200
|
const node = row?._ || row?.["*"] || row?.a || Object.values(row || {})[0];
|
|
201
|
+
// triviumdb 0.8.x serializes TQL row ids as strings; coerce so the rest
|
|
202
|
+
// of the plugin keeps comparing numbers.
|
|
194
203
|
if (!node) return null;
|
|
195
204
|
const id = Number(node.id);
|
|
196
205
|
if (!Number.isFinite(id)) return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-trivium",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.18",
|
|
4
4
|
"description": "In-process graph memory for DeepSeek Harness, backed by TriviumDB.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
}
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
+
"test": "node scripts/run-all.mjs",
|
|
34
35
|
"link-dsh": "node scripts/link-dsh.mjs",
|
|
35
36
|
"smoke-p0": "node scripts/smoke-p0.mjs",
|
|
36
37
|
"smoke-p1": "node scripts/smoke-p1.mjs",
|
|
@@ -41,6 +42,7 @@
|
|
|
41
42
|
"smoke-p6": "node scripts/smoke-p6.mjs",
|
|
42
43
|
"smoke-p7": "node scripts/smoke-p7.mjs",
|
|
43
44
|
"smoke-p8": "node scripts/smoke-p8.mjs",
|
|
45
|
+
"smoke-p9": "node scripts/smoke-p9.mjs",
|
|
44
46
|
"smoke-p10": "node scripts/smoke-p10.mjs",
|
|
45
47
|
"smoke-p11": "node scripts/smoke-p11.mjs",
|
|
46
48
|
"smoke-p12": "node scripts/smoke-p12.mjs",
|
|
@@ -55,7 +57,7 @@
|
|
|
55
57
|
"@deepseek-ai/dsh-tools": ">=0.1.0-rc.8 <0.1.1 || >=0.1.1-rc.2 <0.2.0"
|
|
56
58
|
},
|
|
57
59
|
"dependencies": {
|
|
58
|
-
"triviumdb": "^0.8.
|
|
60
|
+
"triviumdb": "^0.8.8"
|
|
59
61
|
},
|
|
60
62
|
"keywords": [
|
|
61
63
|
"dsh-plugin",
|