dsh-trivium 0.4.9 → 0.4.13

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/lib/client.js CHANGED
@@ -29,12 +29,17 @@ window.__ModuleLoader__.load({
29
29
  extract: "compaction/end 抽取",
30
30
  extractHint: "规则 + 一次小 prompt;失败只记日志。turn 结束后约 12 秒空闲会再抽一次,下次 session-start 也会重放 pending。每轮写入正文合计 ≤3000 字、最多 24 条。",
31
31
  chipTab: "芯片",
32
- chipsSetting: "芯片标签(默认关)",
33
- chipsSettingHint: "打开后,标题栏「对话 / 轨迹」旁出现「芯片」。勾选的条目从下一轮注入(L0,≤300 token)。节点仍在 .tdb,关开关不删记忆。改完保存后重启 dsh web,标签才会出现或消失。",
32
+ chipsSetting: "芯片(记忆白名单,默认关)",
33
+ chipsSettingHint: "打开后,标题栏「对话 / 轨迹」旁立刻出现「芯片」(记忆白名单)。勾选的条目从下一轮注入(L0,≤300 token)。选择写入 ~/.dsh/trivium.json,重启后仍保持。节点仍在 .tdb,关开关不删记忆。不必再点「保存设置」,也不必重启。",
34
34
  sessionLayer: "会话层(情节画布)",
35
35
  sessionLayerHint: "可选。把 compaction / 分叉画成方框,可生成检查点。关着不记 episode,不影响 ctx_find。需先打开芯片标签。",
36
36
  markdown: "Markdown 投影",
37
37
  markdownHint: "只读导出,带边(- 鉴权走 header X —about→ AuthGateway)。给编辑器和 git 看。改完回写用 JSON 导入或本页编辑,不解析自由散文。",
38
+ gitSidecar: "Git 旁路(trivium.jsonl,默认关)",
39
+ gitSidecarHint: "图有改动后延迟约 1.5 秒写入工作区 .dsh/trivium.jsonl(只含业务节点和边,不含向量、情节、芯片钉选)。把这个文件交给 git,.tdb 建议 gitignore。clone / git pull 后若文件变了会自动导回。开关本身不删文件。开着时可点「生成」用当前 .tdb 覆盖写出;关掉后可点「删除 jsonl」。",
40
+ gitSidecarGenerate: "生成",
41
+ gitSidecarDelete: "删除 jsonl",
42
+ gitSidecarDeleteConfirm: "将删除已知工作区里的 trivium.jsonl。.tdb 记忆还在,可以再打开开关后点「生成」写回。确定删除?",
38
43
  exportMd: "导出 Markdown",
39
44
  external: "外部记忆一次性导入",
40
45
  externalHint: "发现 WorkBuddy MEMORY.md、Claude Code CLAUDE.md、Codex AGENTS.md。偏严宁漏:闲聊、一次性改文件、密钥、乱码、工具 dump、无规则线索的段落跳过。不持续同步,不导入会话 jsonl。每批同样 ≤3000 字 / 24 条。",
@@ -49,6 +54,15 @@ window.__ModuleLoader__.load({
49
54
  updateInstalled: "已安装",
50
55
  updateLatest: "npm latest",
51
56
  updateFailed: "暂时查不到 registry",
57
+ configPanel: "配置设置",
58
+ configPanelHint: "注入、抽取、芯片、Git 旁路、界面语言、导入和 embedding。默认收起。",
59
+ configPanelOpen: "展开",
60
+ configPanelClose: "收起",
61
+ pluginLocale: "界面语言(本插件)",
62
+ pluginLocaleHint: "只改本插件设置页和芯片标签的文案,不改 DSH 宿主语言。未选时跟随宿主。",
63
+ pluginLocaleHost: "跟随宿主",
64
+ pluginLocaleZh: "中文",
65
+ pluginLocaleEn: "English",
52
66
  embed: "远程 embedding(OpenAI 兼容,默认关)",
53
67
  embedHint: "失败退回关键词 + 图检索。需单独的 embeddings endpoint;官方 DeepSeek chat API 不含 embedding。",
54
68
  embedReady: "已就绪",
@@ -113,13 +127,13 @@ window.__ModuleLoader__.load({
113
127
  cutNeedTurn: "当前对话还没有可切的轮次。先在对话里发一条,再生成检查点。",
114
128
  cutOk: "已把当前「后续」收成左边一格(seq {seq}),右边仍是后续。这不压缩 DSH 上下文。",
115
129
  cutDup: "这个位置已经切过了(seq {seq})。再聊几轮后再生成。",
116
- forkOpenFail: "无法打开分叉会话:会话图没有拿到 session.open",
117
- chatViewFail: "无法切换到对话页:会话图没有拿到同一 chat store 的 setView",
118
- trajViewFail: "无法切换到轨迹页:会话图没有拿到同一 chat store 的 setView",
130
+ forkOpenFail: "无法打开分叉会话:芯片页没有拿到 session.open",
131
+ chatViewFail: "无法切换到对话页:芯片页没有拿到同一 chat store 的 setView",
132
+ trajViewFail: "无法切换到轨迹页:芯片页没有拿到同一 chat store 的 setView",
119
133
  noFork: "当前壳没有 session.fork",
120
134
  renameFail: "会话已分叉,但侧边栏改名失败:{err}",
121
135
  deleteConfirm: "删除会从 .tdb 里拿掉这 {n} 条,不可恢复。归档只是藏起来,find / 短地图 / 芯片都不再出现。确定删除?",
122
- sessionMap: "会话图",
136
+ sessionMap: "芯片",
123
137
  chips: "记忆芯片",
124
138
  chipsHint: "共 {n} 条,已钉 {pins}。默认不勾选:不钉就不会按芯片白名单注入,但仍可能被开场短地图点名带上,模型也能 find 到。",
125
139
  cutBtn: "生成检查点",
@@ -171,12 +185,17 @@ window.__ModuleLoader__.load({
171
185
  extract: "Extract on compaction/end",
172
186
  extractHint: "Rules + one small prompt; failures are logged. Idle ~12s after turn/end also extracts; pending replays on next session-start. Per batch: body ≤3000 chars, at most 24 items.",
173
187
  chipTab: "Chips",
174
- chipsSetting: "Chips tab (off by default)",
175
- chipsSettingHint: "When on, the title bar shows Chips next to Conversation / Trajectory. Checked items inject from the next turn (L0, ≤300 tokens). Nodes stay in .tdb; turning this off does not delete memory. Restart dsh web after save for the tab to appear or disappear.",
188
+ chipsSetting: "Chips (memory whitelist, off by default)",
189
+ chipsSettingHint: "When on, the Chips tab appears next to Conversation / Trajectory immediately (memory whitelist). Checked items inject from the next turn (L0, ≤300 tokens). Stored in ~/.dsh/trivium.json and kept across restarts. Nodes stay in .tdb; turning this off does not delete memory. No extra Save click and no restart.",
176
190
  sessionLayer: "Session layer (plot canvas)",
177
191
  sessionLayerHint: "Optional. Draws compaction / forks as boxes; Create checkpoint lives here. Off means no episode writes; ctx_find is unchanged. Requires the Chips tab first.",
178
192
  markdown: "Markdown projection",
179
193
  markdownHint: "Read-only export with edges (e.g. - auth in header X —about→ AuthGateway). For editors and git. Write back with JSON import or this page, not free prose.",
194
+ gitSidecar: "Git sidecar (trivium.jsonl, off by default)",
195
+ gitSidecarHint: "After graph writes, debounce ~1.5s and write workspace .dsh/trivium.jsonl (business nodes and edges only — no embeddings, episodes, or chip pins). Commit that file; gitignore .tdb. Clone or git pull re-imports when the file changes. The switch does not delete files. While on, Generate overwrites jsonl from the current .tdb. After turning off, Delete removes known jsonl files.",
196
+ gitSidecarGenerate: "Generate",
197
+ gitSidecarDelete: "Delete jsonl",
198
+ gitSidecarDeleteConfirm: "This deletes known workspace trivium.jsonl files. .tdb memory stays; turn the switch on and Generate to write them back. Delete anyway?",
180
199
  exportMd: "Export Markdown",
181
200
  external: "One-shot external import",
182
201
  externalHint: "Discovers WorkBuddy MEMORY.md, Claude Code CLAUDE.md, and Codex AGENTS.md. Strict: skip chitchat, one-off file edits, secrets, mojibake, tool dumps, and lines with no rule cue. No watch, no session jsonl. Same cap: ≤3000 chars / 24 items.",
@@ -191,6 +210,15 @@ window.__ModuleLoader__.load({
191
210
  updateInstalled: "Installed",
192
211
  updateLatest: "npm latest",
193
212
  updateFailed: "Registry unavailable",
213
+ configPanel: "Settings",
214
+ configPanelHint: "Injection, extract, chips, Git sidecar, language, import, and embedding. Collapsed by default.",
215
+ configPanelOpen: "Expand",
216
+ configPanelClose: "Collapse",
217
+ pluginLocale: "Plugin language",
218
+ pluginLocaleHint: "Only this plugin's Settings card and Chips tab. Does not change the DSH host. Default follows the host.",
219
+ pluginLocaleHost: "Follow host",
220
+ pluginLocaleZh: "中文",
221
+ pluginLocaleEn: "English",
194
222
  embed: "Remote embedding (OpenAI-compatible, off by default)",
195
223
  embedHint: "Failures fall back to keyword + graph search. Needs a separate embeddings endpoint; the official DeepSeek chat API has none.",
196
224
  embedReady: "Ready",
@@ -261,7 +289,7 @@ window.__ModuleLoader__.load({
261
289
  noFork: "This host has no session.fork",
262
290
  renameFail: "Session forked, but sidebar rename failed: {err}",
263
291
  deleteConfirm: "Delete removes these {n} nodes from .tdb and cannot be undone. Archive only hides them from find / short map / chips. Delete anyway?",
264
- sessionMap: "Session graph",
292
+ sessionMap: "Chips",
265
293
  chips: "Memory chips",
266
294
  chipsHint: "{n} chips, {pins} pinned. Unchecked by default: pinning is a whitelist, but the short map may still name them and the model can still find them.",
267
295
  cutBtn: "Create checkpoint",
@@ -297,6 +325,8 @@ window.__ModuleLoader__.load({
297
325
  };
298
326
 
299
327
  let uiLocale = "zh";
328
+ let hostLocale = "";
329
+ let pluginLocalePref = "host";
300
330
  const localeListeners = new Set();
301
331
 
302
332
  function normalizeLocale(value) {
@@ -306,6 +336,11 @@ window.__ModuleLoader__.load({
306
336
  return "";
307
337
  }
308
338
 
339
+ function resolvedUiLocale() {
340
+ if (pluginLocalePref === "zh" || pluginLocalePref === "en") return pluginLocalePref;
341
+ return hostLocale || "zh";
342
+ }
343
+
309
344
  function setUiLocale(next) {
310
345
  const loc = normalizeLocale(next) || "zh";
311
346
  if (loc === uiLocale) return;
@@ -319,6 +354,22 @@ window.__ModuleLoader__.load({
319
354
  });
320
355
  }
321
356
 
357
+ function applyResolvedLocale() {
358
+ setUiLocale(resolvedUiLocale());
359
+ }
360
+
361
+ function setHostLocale(next) {
362
+ const loc = normalizeLocale(next);
363
+ if (loc) hostLocale = loc;
364
+ applyResolvedLocale();
365
+ }
366
+
367
+ function setPluginLocalePref(next) {
368
+ const v = String(next || "").toLowerCase();
369
+ pluginLocalePref = v === "zh" || v === "en" ? v : "host";
370
+ applyResolvedLocale();
371
+ }
372
+
322
373
  function t(key, vars) {
323
374
  let s = (I18N[uiLocale] && I18N[uiLocale][key]) || I18N.en[key] || I18N.zh[key] || key;
324
375
  if (vars) {
@@ -385,6 +436,54 @@ window.__ModuleLoader__.load({
385
436
  saved: { fontSize: 12, opacity: 0.75 },
386
437
  };
387
438
 
439
+ const chipUi = { ctx: null, sessions: null, dispose: null };
440
+ const chipViewListeners = new Set();
441
+
442
+ function subscribeChipView(fn) {
443
+ chipViewListeners.add(fn);
444
+ return () => chipViewListeners.delete(fn);
445
+ }
446
+
447
+ function refreshChipView(hint) {
448
+ for (const fn of chipViewListeners) {
449
+ try {
450
+ fn(hint);
451
+ } catch {
452
+ // ignore
453
+ }
454
+ }
455
+ }
456
+
457
+ function setChipsTab(enabled) {
458
+ const ctx = chipUi.ctx;
459
+ const sessions = chipUi.sessions;
460
+ if (!ctx || !sessions) return;
461
+ if (enabled) {
462
+ if (chipUi.dispose) return;
463
+ chipUi.dispose = registerChipView(ctx, sessions);
464
+ return;
465
+ }
466
+ if (!chipUi.dispose) return;
467
+ try {
468
+ chipUi.dispose();
469
+ } catch {
470
+ // already gone
471
+ }
472
+ chipUi.dispose = null;
473
+ }
474
+
475
+ async function syncChipsTabFromServer() {
476
+ for (let i = 0; i < 10; i += 1) {
477
+ try {
478
+ const st = await api("/api/dsh-trivium/status");
479
+ setChipsTab(st && st.chipsEnabled === true);
480
+ return;
481
+ } catch {
482
+ await new Promise((ok) => setTimeout(ok, 200));
483
+ }
484
+ }
485
+ }
486
+
388
487
  async function api(path, opts) {
389
488
  const resp = await fetch(path, opts);
390
489
  let data = null;
@@ -422,6 +521,9 @@ window.__ModuleLoader__.load({
422
521
  const [extractEnabled, setExtractEnabled] = react.useState(true);
423
522
  const [chipsEnabled, setChipsEnabled] = react.useState(false);
424
523
  const [sessionLayerEnabled, setSessionLayerEnabled] = react.useState(false);
524
+ const [gitSidecarEnabled, setGitSidecarEnabled] = react.useState(false);
525
+ const [pluginLocale, setPluginLocale] = react.useState("host");
526
+ const [configPanelOpen, setConfigPanelOpen] = react.useState(false);
425
527
  const [embeddingEnabled, setEmbeddingEnabled] = react.useState(false);
426
528
  const [embeddingUrl, setEmbeddingUrl] = react.useState("");
427
529
  const [embeddingModel, setEmbeddingModel] = react.useState("text-embedding-3-small");
@@ -434,20 +536,30 @@ window.__ModuleLoader__.load({
434
536
  const [buddyNote, setBuddyNote] = react.useState("");
435
537
  const [updateInfo, setUpdateInfo] = react.useState(null);
436
538
  const [updateBusy, setUpdateBusy] = react.useState(false);
539
+ const persistSeq = react.useRef(0);
540
+ const loadSeq = react.useRef(0);
437
541
 
438
- const load = react.useCallback(async () => {
542
+ const load = react.useCallback(async (opts = {}) => {
543
+ const seq = ++loadSeq.current;
439
544
  setError("");
440
545
  try {
441
546
  const st = await api("/api/dsh-trivium/status");
547
+ if (seq !== loadSeq.current) return;
442
548
  setStatus(st);
443
- setRecallMode(st.recallMode || (st.autoRecall ? "auto" : st.anchorRecall ? "anchor" : "off"));
444
- setExtractEnabled(st.extractEnabled !== false);
445
- setChipsEnabled(!!st.chipsEnabled);
446
- setSessionLayerEnabled(!!st.sessionLayerEnabled);
447
- setEmbeddingEnabled(!!st.embeddingEnabled);
448
- setEmbeddingUrl(st.embeddingUrl || "");
449
- setEmbeddingModel(st.embeddingModel || "text-embedding-3-small");
450
- setEmbeddingApiKeySet(!!st.embeddingApiKeySet);
549
+ if (!opts.keepFlags) {
550
+ setRecallMode(st.recallMode || (st.autoRecall ? "auto" : st.anchorRecall ? "anchor" : "off"));
551
+ setExtractEnabled(st.extractEnabled !== false);
552
+ setChipsEnabled(!!st.chipsEnabled);
553
+ setSessionLayerEnabled(!!st.sessionLayerEnabled);
554
+ setGitSidecarEnabled(st.gitSidecarEnabled === true);
555
+ setPluginLocale(st.pluginLocale === "zh" || st.pluginLocale === "en" ? st.pluginLocale : "host");
556
+ setPluginLocalePref(st.pluginLocale);
557
+ setConfigPanelOpen(st.configPanelOpen === true);
558
+ setEmbeddingEnabled(!!st.embeddingEnabled);
559
+ setEmbeddingUrl(st.embeddingUrl || "");
560
+ setEmbeddingModel(st.embeddingModel || "text-embedding-3-small");
561
+ setEmbeddingApiKeySet(!!st.embeddingApiKeySet);
562
+ }
451
563
  const params = new URLSearchParams();
452
564
  if (type) params.set("type", type);
453
565
  if (q.trim()) params.set("q", q.trim());
@@ -475,26 +587,96 @@ window.__ModuleLoader__.load({
475
587
  until: n.until || "",
476
588
  };
477
589
 
478
- const saveFlags = async () => {
590
+ const flagsRef = react.useRef({
591
+ recallMode,
592
+ extractEnabled,
593
+ chipsEnabled,
594
+ sessionLayerEnabled,
595
+ gitSidecarEnabled,
596
+ pluginLocale,
597
+ configPanelOpen,
598
+ embeddingEnabled,
599
+ embeddingUrl,
600
+ embeddingModel,
601
+ embeddingApiKey,
602
+ });
603
+ flagsRef.current = {
604
+ recallMode,
605
+ extractEnabled,
606
+ chipsEnabled,
607
+ sessionLayerEnabled,
608
+ gitSidecarEnabled,
609
+ pluginLocale,
610
+ configPanelOpen,
611
+ embeddingEnabled,
612
+ embeddingUrl,
613
+ embeddingModel,
614
+ embeddingApiKey,
615
+ };
616
+
617
+ const persistFlags = async (patch = {}) => {
618
+ const cur = { ...flagsRef.current, ...patch };
619
+ const seq = ++persistSeq.current;
620
+ loadSeq.current += 1;
479
621
  setBusy(true);
480
622
  setSaved(false);
481
623
  try {
482
624
  const body = {
483
- recallMode,
484
- extractEnabled,
485
- chipsEnabled,
486
- sessionLayerEnabled,
487
- embeddingEnabled,
488
- embeddingUrl,
489
- embeddingModel,
625
+ recallMode: cur.recallMode,
626
+ extractEnabled: cur.extractEnabled,
627
+ chipsEnabled: cur.chipsEnabled,
628
+ sessionLayerEnabled: cur.sessionLayerEnabled,
629
+ gitSidecarEnabled: cur.gitSidecarEnabled === true,
630
+ pluginLocale: cur.pluginLocale === "zh" || cur.pluginLocale === "en" ? cur.pluginLocale : "host",
631
+ configPanelOpen: cur.configPanelOpen === true,
632
+ embeddingEnabled: cur.embeddingEnabled,
633
+ embeddingUrl: cur.embeddingUrl,
634
+ embeddingModel: cur.embeddingModel,
490
635
  };
491
- if (embeddingApiKey.trim()) body.embeddingApiKey = embeddingApiKey.trim();
636
+ if (String(cur.embeddingApiKey || "").trim()) body.embeddingApiKey = String(cur.embeddingApiKey).trim();
492
637
  await api("/api/dsh-trivium/settings", {
493
638
  method: "POST",
494
639
  headers: { "content-type": "application/json" },
495
640
  body: JSON.stringify(body),
496
641
  });
497
- setEmbeddingApiKey("");
642
+ if (seq !== persistSeq.current) return;
643
+ setChipsTab(cur.chipsEnabled === true);
644
+ refreshChipView({ sessionLayerEnabled: cur.sessionLayerEnabled === true });
645
+ setPluginLocalePref(cur.pluginLocale);
646
+ if (patch.embeddingApiKey != null || !Object.keys(patch).length) setEmbeddingApiKey("");
647
+ setSaved(true);
648
+ await load({ keepFlags: true });
649
+ } catch (err) {
650
+ if (seq === persistSeq.current) setError(String(err.message || err));
651
+ } finally {
652
+ if (seq === persistSeq.current) setBusy(false);
653
+ }
654
+ };
655
+
656
+ const saveFlags = () => persistFlags({});
657
+
658
+ const sidecarGenerate = async () => {
659
+ setBusy(true);
660
+ setSaved(false);
661
+ setError("");
662
+ try {
663
+ await api("/api/dsh-trivium/sidecar/generate", { method: "POST" });
664
+ setSaved(true);
665
+ await load();
666
+ } catch (err) {
667
+ setError(String(err.message || err));
668
+ } finally {
669
+ setBusy(false);
670
+ }
671
+ };
672
+
673
+ const sidecarDelete = async () => {
674
+ if (!window.confirm(t("gitSidecarDeleteConfirm"))) return;
675
+ setBusy(true);
676
+ setSaved(false);
677
+ setError("");
678
+ try {
679
+ await api("/api/dsh-trivium/sidecar/delete", { method: "POST" });
498
680
  setSaved(true);
499
681
  await load();
500
682
  } catch (err) {
@@ -683,7 +865,10 @@ window.__ModuleLoader__.load({
683
865
  type: "radio",
684
866
  name: "dsh-trivium-recall",
685
867
  checked: recallMode === value,
686
- onChange: () => setRecallMode(value),
868
+ onChange: () => {
869
+ setRecallMode(value);
870
+ persistFlags({ recallMode: value });
871
+ },
687
872
  style: { marginTop: 3 },
688
873
  }),
689
874
  jsxs("span", {
@@ -695,6 +880,25 @@ window.__ModuleLoader__.load({
695
880
  ],
696
881
  });
697
882
 
883
+ const localeRadio = (value, label) =>
884
+ jsxs("label", {
885
+ style: ui.radio,
886
+ children: [
887
+ jsx("input", {
888
+ type: "radio",
889
+ name: "dsh-trivium-plugin-locale",
890
+ checked: pluginLocale === value,
891
+ onChange: () => {
892
+ setPluginLocale(value);
893
+ setPluginLocalePref(value);
894
+ persistFlags({ pluginLocale: value });
895
+ },
896
+ style: { marginTop: 3 },
897
+ }),
898
+ jsx("span", { style: { fontWeight: 600 }, children: label }),
899
+ ],
900
+ });
901
+
698
902
  return jsxs("div", {
699
903
  style: ui.page,
700
904
  children: [
@@ -792,6 +996,54 @@ window.__ModuleLoader__.load({
792
996
  }),
793
997
  ],
794
998
  }),
999
+ jsxs("div", {
1000
+ style: ui.card,
1001
+ children: [
1002
+ jsxs("button", {
1003
+ type: "button",
1004
+ onClick: () => {
1005
+ const on = !configPanelOpen;
1006
+ setConfigPanelOpen(on);
1007
+ persistFlags({ configPanelOpen: on });
1008
+ },
1009
+ style: {
1010
+ display: "flex",
1011
+ alignItems: "center",
1012
+ justifyContent: "space-between",
1013
+ gap: 8,
1014
+ width: "100%",
1015
+ margin: 0,
1016
+ padding: 0,
1017
+ border: 0,
1018
+ background: "transparent",
1019
+ color: "inherit",
1020
+ font: "inherit",
1021
+ cursor: "pointer",
1022
+ textAlign: "left",
1023
+ },
1024
+ children: [
1025
+ jsx("span", { style: ui.cardTitle, children: L.configPanel }),
1026
+ jsx("span", {
1027
+ style: { fontSize: 12, opacity: 0.65 },
1028
+ children: configPanelOpen ? L.configPanelClose : L.configPanelOpen,
1029
+ }),
1030
+ ],
1031
+ }),
1032
+ jsx("div", { style: ui.hint, children: L.configPanelHint }),
1033
+ configPanelOpen
1034
+ ? jsxs("div", {
1035
+ style: { display: "flex", flexDirection: "column", gap: 10 },
1036
+ children: [
1037
+ jsxs("div", {
1038
+ style: ui.card,
1039
+ children: [
1040
+ jsx("div", { style: ui.cardTitle, children: L.pluginLocale }),
1041
+ jsx("div", { style: ui.hint, children: L.pluginLocaleHint }),
1042
+ localeRadio("host", L.pluginLocaleHost),
1043
+ localeRadio("zh", L.pluginLocaleZh),
1044
+ localeRadio("en", L.pluginLocaleEn),
1045
+ ],
1046
+ }),
795
1047
  jsxs("div", {
796
1048
  style: ui.card,
797
1049
  children: [
@@ -811,7 +1063,11 @@ window.__ModuleLoader__.load({
811
1063
  jsx("input", {
812
1064
  type: "checkbox",
813
1065
  checked: extractEnabled,
814
- onChange: (e) => setExtractEnabled(e.target.checked),
1066
+ onChange: (e) => {
1067
+ const on = e.target.checked;
1068
+ setExtractEnabled(on);
1069
+ persistFlags({ extractEnabled: on });
1070
+ },
815
1071
  }),
816
1072
  jsx("span", { children: L.extract }),
817
1073
  ],
@@ -828,7 +1084,11 @@ window.__ModuleLoader__.load({
828
1084
  jsx("input", {
829
1085
  type: "checkbox",
830
1086
  checked: chipsEnabled,
831
- onChange: (e) => setChipsEnabled(e.target.checked),
1087
+ onChange: (e) => {
1088
+ const on = e.target.checked;
1089
+ setChipsEnabled(on);
1090
+ persistFlags({ chipsEnabled: on });
1091
+ },
832
1092
  }),
833
1093
  jsx("span", { children: L.chipsSetting }),
834
1094
  ],
@@ -841,7 +1101,11 @@ window.__ModuleLoader__.load({
841
1101
  type: "checkbox",
842
1102
  checked: sessionLayerEnabled,
843
1103
  disabled: !chipsEnabled,
844
- onChange: (e) => setSessionLayerEnabled(e.target.checked),
1104
+ onChange: (e) => {
1105
+ const on = e.target.checked;
1106
+ setSessionLayerEnabled(on);
1107
+ persistFlags({ sessionLayerEnabled: on });
1108
+ },
845
1109
  }),
846
1110
  jsx("span", { children: L.sessionLayer }),
847
1111
  ],
@@ -849,6 +1113,52 @@ window.__ModuleLoader__.load({
849
1113
  jsx("div", { style: { ...ui.hint, paddingLeft: 22 }, children: L.sessionLayerHint }),
850
1114
  ],
851
1115
  }),
1116
+ jsxs("div", {
1117
+ style: ui.card,
1118
+ children: [
1119
+ jsxs("label", {
1120
+ style: ui.switch,
1121
+ onClick: (e) => {
1122
+ e.preventDefault();
1123
+ if (busy) return;
1124
+ const on = !gitSidecarEnabled;
1125
+ setGitSidecarEnabled(on);
1126
+ persistFlags({ gitSidecarEnabled: on });
1127
+ },
1128
+ children: [
1129
+ jsx("input", {
1130
+ type: "checkbox",
1131
+ checked: gitSidecarEnabled,
1132
+ disabled: busy,
1133
+ onChange: () => {},
1134
+ }),
1135
+ jsx("span", { children: L.gitSidecar }),
1136
+ ],
1137
+ }),
1138
+ jsx("div", { style: ui.hint, children: L.gitSidecarHint }),
1139
+ jsxs("div", {
1140
+ style: { display: "flex", flexWrap: "wrap", gap: 8 },
1141
+ children: [
1142
+ jsx(Button, {
1143
+ type: "button",
1144
+ variant: "outline",
1145
+ size: "sm",
1146
+ disabled: busy || !gitSidecarEnabled,
1147
+ onClick: sidecarGenerate,
1148
+ children: L.gitSidecarGenerate,
1149
+ }),
1150
+ jsx(Button, {
1151
+ type: "button",
1152
+ variant: "outline",
1153
+ size: "sm",
1154
+ disabled: busy || gitSidecarEnabled,
1155
+ onClick: sidecarDelete,
1156
+ children: L.gitSidecarDelete,
1157
+ }),
1158
+ ],
1159
+ }),
1160
+ ],
1161
+ }),
852
1162
  jsxs("div", {
853
1163
  style: ui.card,
854
1164
  children: [
@@ -892,7 +1202,11 @@ window.__ModuleLoader__.load({
892
1202
  jsx("input", {
893
1203
  type: "checkbox",
894
1204
  checked: embeddingEnabled,
895
- onChange: (e) => setEmbeddingEnabled(e.target.checked),
1205
+ onChange: (e) => {
1206
+ const on = e.target.checked;
1207
+ setEmbeddingEnabled(on);
1208
+ persistFlags({ embeddingEnabled: on });
1209
+ },
896
1210
  }),
897
1211
  jsx("span", { children: L.embed }),
898
1212
  ],
@@ -950,6 +1264,11 @@ window.__ModuleLoader__.load({
950
1264
  }),
951
1265
  jsx("div", { style: ui.hint, children: L.embedKeyHint }),
952
1266
  ],
1267
+ }),
1268
+ ],
1269
+ })
1270
+ : null,
1271
+ ],
953
1272
  }),
954
1273
  jsxs("div", {
955
1274
  style: ui.card,
@@ -2293,6 +2612,22 @@ window.__ModuleLoader__.load({
2293
2612
  load();
2294
2613
  }, [load, lineageKey]);
2295
2614
 
2615
+ react.useEffect(
2616
+ () =>
2617
+ subscribeChipView((hint) => {
2618
+ if (hint && typeof hint.sessionLayerEnabled === "boolean") {
2619
+ const layer = hint.sessionLayerEnabled;
2620
+ setSessionLayer(layer);
2621
+ if (!layer) {
2622
+ setNodes([]);
2623
+ setEdges([]);
2624
+ }
2625
+ }
2626
+ load();
2627
+ }),
2628
+ [load],
2629
+ );
2630
+
2296
2631
  const { chain, kids } = react.useMemo(
2297
2632
  () => layoutChain(nodes, edges, sessionId, liveSessions),
2298
2633
  [nodes, edges, sessionId, liveSessions],
@@ -2932,7 +3267,7 @@ window.__ModuleLoader__.load({
2932
3267
  }
2933
3268
 
2934
3269
  function registerChipView(ctx, sessions) {
2935
- ctx.slots.inject(
3270
+ return ctx.slots.inject(
2936
3271
  "conversation.view",
2937
3272
  () => {
2938
3273
  const chatStore = sharedChatStore(ctx);
@@ -2972,7 +3307,6 @@ window.__ModuleLoader__.load({
2972
3307
  SessionMapView,
2973
3308
  );
2974
3309
  },
2975
- "dsh-trivium: chips",
2976
3310
  );
2977
3311
  }
2978
3312
 
@@ -2981,13 +3315,13 @@ window.__ModuleLoader__.load({
2981
3315
  const sessions = ctx.sessions;
2982
3316
  try {
2983
3317
  const snap = ctx.locale && typeof ctx.locale.getLocale === "function" ? ctx.locale.getLocale() : null;
2984
- if (snap && snap.active) setUiLocale(snap.active);
3318
+ if (snap && snap.active) setHostLocale(snap.active);
2985
3319
  } catch {
2986
3320
  // host may not expose locale
2987
3321
  }
2988
3322
  try {
2989
3323
  ctx.on("locale/change", (snap) => {
2990
- if (snap && snap.active) setUiLocale(snap.active);
3324
+ if (snap && snap.active) setHostLocale(snap.active);
2991
3325
  });
2992
3326
  } catch {
2993
3327
  // ignore
@@ -3007,14 +3341,9 @@ window.__ModuleLoader__.load({
3007
3341
  ),
3008
3342
  "dsh-trivium: settings section",
3009
3343
  );
3010
- fetch("/api/dsh-trivium/status")
3011
- .then((resp) => (resp && resp.ok ? resp.json() : null))
3012
- .then((st) => {
3013
- if (st && st.chipsEnabled === true) registerChipView(ctx, sessions);
3014
- })
3015
- .catch(() => {
3016
- // default off: no title-bar tab
3017
- });
3344
+ chipUi.ctx = ctx;
3345
+ chipUi.sessions = sessions;
3346
+ syncChipsTabFromServer();
3018
3347
  }
3019
3348
 
3020
3349
  exports.apply = apply;
package/lib/index.js CHANGED
@@ -24,6 +24,7 @@ import { applyRecallMode, chipsEnabledOf, recallModeOf, sessionLayerEnabledOf }
24
24
  import { configureEmbedding } from "./embed.js";
25
25
  import { recordCheckpoint, ensureTail, syncForkLineage } from "./episode.js";
26
26
  import { buildPinInject } from "./pins.js";
27
+ import { syncSidecarOnOpen } from "./sidecar.js";
27
28
 
28
29
  export const name = "dsh-trivium";
29
30
  export const inject = ["tools"];
@@ -35,6 +36,9 @@ const DEFAULTS = {
35
36
  extractEnabled: true,
36
37
  chipsEnabled: false,
37
38
  sessionLayerEnabled: false,
39
+ gitSidecarEnabled: false,
40
+ pluginLocale: "host",
41
+ configPanelOpen: false,
38
42
  writeApproval: false,
39
43
  mapTokenBudget: 400,
40
44
  expandDepth: 1,
@@ -282,6 +286,11 @@ export function apply(ctx, config = {}) {
282
286
  const cwd = ready?.cwd || cwdOf(agent);
283
287
  const db = ready?.db || (await openWorkspaceDb(cwd));
284
288
  setActiveCwd(cwd);
289
+ try {
290
+ await syncSidecarOnOpen(cwd, db);
291
+ } catch (err) {
292
+ log.warn?.(`[dsh-trivium] sidecar sync skipped: ${err.message}`);
293
+ }
285
294
  try {
286
295
  const session = agent.session;
287
296
  const sessionId = sessionKey(session);