dsh-trivium 0.4.17 → 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 CHANGED
@@ -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,8 @@ 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
+
245
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.
246
251
 
247
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.
@@ -300,7 +305,7 @@ Local source checkout: `npm install`, then `node scripts/link-dsh.mjs`, then res
300
305
  ## Release info
301
306
 
302
307
  - GitHub: https://github.com/QWQcool/dsh-trivium
303
- - npm: [`dsh-trivium@0.4.17`](https://www.npmjs.com/package/dsh-trivium)
308
+ - npm: [`dsh-trivium@0.4.18`](https://www.npmjs.com/package/dsh-trivium)
304
309
  - Tested host: `@deepseek-ai/dsh@0.1.5-rc.1` (also `0.1.1-rc.2` / `0.1.0-rc.8`)
305
310
  - License: MIT (depends on [TriviumDB](https://github.com/YoKONCy/TriviumDB), Apache-2.0)
306
311
 
package/README.zh-CN.md CHANGED
@@ -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,8 @@ Trivium 是记忆内核,不是日记、日历或聊天伴侣。它按**节点
238
241
 
239
242
  ## 更新说明
240
243
 
244
+ **0.4.18** — `~/.dsh/trivium.json` 改为原子写(此前写到一半被杀会让所有开关和芯片钉选静默回默认),POSIX 下权限收紧为仅属主可读。新增 `npm test` 聚合入口(每个脚本用独立临时设置文件),`smoke-p0` 新增「重开后 hybrid 文本索引仍可用」的断言。四工具 / 短地图 / find 路径行为不变。
245
+
241
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 路径不变。
242
247
 
243
248
  **0.4.16** — 存储升到 `triviumdb@0.8.4`:引擎构造函数改为 options 对象(`{ dim }`),不再收数字位置参数;TQL 行 id 变字符串,插件内部归一为数字。四工具 / 短地图 / find 路径不变。
@@ -296,7 +301,7 @@ Trivium 是记忆内核,不是日记、日历或聊天伴侣。它按**节点
296
301
  ## 发布信息
297
302
 
298
303
  - GitHub: https://github.com/QWQcool/dsh-trivium
299
- - npm: [`dsh-trivium@0.4.17`](https://www.npmjs.com/package/dsh-trivium)
304
+ - npm: [`dsh-trivium@0.4.18`](https://www.npmjs.com/package/dsh-trivium)
300
305
  - 测试宿主:`@deepseek-ai/dsh@0.1.5-rc.1`(兼 `0.1.1-rc.2` / `0.1.0-rc.8`)
301
306
  - License: MIT(依赖 [TriviumDB](https://github.com/YoKONCy/TriviumDB) 为 Apache-2.0)
302
307
 
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/index.js CHANGED
@@ -454,10 +454,12 @@ export function apply(ctx, config = {}) {
454
454
  extra.push(
455
455
  createUserMessage({
456
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.
457
460
  source: {
458
461
  kind: "plugin",
459
462
  plugin: "dsh-trivium",
460
- form: "pins",
461
463
  },
462
464
  }),
463
465
  );
package/lib/settings.js CHANGED
@@ -1,10 +1,14 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { homedir, tmpdir } from "node:os";
3
- import { dirname, join, relative, resolve, sep } from "node:path";
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
- const file = settingsFilePath();
16
- mkdirSync(dirname(file), { recursive: true });
17
- writeFileSync(file, JSON.stringify(next, null, 2), "utf8");
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, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
5
- import { dirname, join, resolve } from "node:path";
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
- atomicWrite(file, text);
281
+ atomicWriteFile(file, text);
297
282
  rememberHash(cwd, hash);
298
283
  return { path: file, hash, wrote: true };
299
284
  }
package/lib/store.js CHANGED
@@ -124,6 +124,11 @@ export async function openWorkspaceDb(cwd) {
124
124
  // indexes may already exist
125
125
  }
126
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.
127
132
  rebuildTextIndex(db);
128
133
  dbs.set(key, db);
129
134
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-trivium",
3
- "version": "0.4.17",
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",