dsh-hypatia-auto-memory 0.5.0 → 1.0.0
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 +46 -24
- package/README.zh-CN.md +37 -22
- package/cordis.patch.yml +4 -3
- package/lib/client.js +111 -114
- package/lib/client.js.map +1 -1
- package/package.json +15 -15
- package/src/client/SettingsCard.module.css +5 -37
- package/src/client/SettingsCard.tsx +406 -412
- package/src/client/index.tsx +38 -47
- package/src/client/locales.ts +6 -6
- package/src/client/memory-client.ts +48 -0
- package/src/client/shelves.ts +6 -38
- package/src/client/slot-contract.ts +5 -5
- package/src/config.js +145 -76
- package/src/index.js +127 -65
- package/src/memory-api.js +54 -3
- package/src/shelf.js +5 -99
package/lib/client.js
CHANGED
|
@@ -47,31 +47,6 @@ window.__ModuleLoader__.load({
|
|
|
47
47
|
}
|
|
48
48
|
return candidates;
|
|
49
49
|
}
|
|
50
|
-
function isRecord$1(value) {
|
|
51
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Pick the inventory namespace out of a settings descriptor.
|
|
55
|
-
*
|
|
56
|
-
* Reads the composition layer (`base`), which only the Host sets, rather than
|
|
57
|
-
* the resolved value: a stray user-layer section for this namespace would
|
|
58
|
-
* otherwise freeze the list.
|
|
59
|
-
* @param described - the `namespaces` array a settings `describe()` returns.
|
|
60
|
-
* @returns the listing, or undefined when the Host does not serve one.
|
|
61
|
-
*/
|
|
62
|
-
function readShelfInventory(described) {
|
|
63
|
-
const view = described.find((entry) => isRecord$1(entry) && entry.ns === "hypatia-auto-memory-shelves");
|
|
64
|
-
if (!isRecord$1(view) || !isRecord$1(view.base)) return void 0;
|
|
65
|
-
const { shelves, error } = view.base;
|
|
66
|
-
return {
|
|
67
|
-
shelves: Array.isArray(shelves) ? shelves.flatMap((shelf) => isRecord$1(shelf) && typeof shelf.name === "string" ? [{
|
|
68
|
-
name: shelf.name,
|
|
69
|
-
path: typeof shelf.path === "string" ? shelf.path : "",
|
|
70
|
-
connected: shelf.connected === true
|
|
71
|
-
}] : []) : [],
|
|
72
|
-
error: typeof error === "string" ? error : ""
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
50
|
/**
|
|
76
51
|
* Every shelf the dropdown offers: the listing, plus the stored and composed
|
|
77
52
|
* values when the listing does not carry them, so the current choice always
|
|
@@ -98,7 +73,7 @@ window.__ModuleLoader__.load({
|
|
|
98
73
|
}
|
|
99
74
|
//#endregion
|
|
100
75
|
//#region \0dsh-hypatia-css:/home/runner/work/dsh-hypatia-auto-memory/dsh-hypatia-auto-memory/src/client/SettingsCard.module.css.mjs
|
|
101
|
-
const css$1 = ".YVNYya_card{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-
|
|
76
|
+
const css$1 = ".YVNYya_card{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-2);border-radius:16px}.YVNYya_header{align-items:center;gap:12px;padding:14px 16px;display:flex}.YVNYya_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.YVNYya_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.YVNYya_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.YVNYya_pending{corner-shape:round;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);white-space:nowrap;border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.YVNYya_body{border-top:.5px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.YVNYya_status,.YVNYya_readOnly,.YVNYya_hint,.YVNYya_advancedHint,.YVNYya_error{margin:0;font-size:12px;line-height:1.5}.YVNYya_status,.YVNYya_readOnly,.YVNYya_hint,.YVNYya_advancedHint{color:var(--dsw-alias-label-tertiary)}.YVNYya_status{padding:12px 16px}.YVNYya_readOnly{padding-top:12px}.YVNYya_field{gap:6px;padding:12px 0;display:grid}.YVNYya_body>.YVNYya_field+.YVNYya_group,.YVNYya_body>.YVNYya_group+.YVNYya_field{border-top:.5px solid var(--dsw-alias-border-l2)}.YVNYya_group{padding:12px 0}.YVNYya_groupTitle{color:var(--dsw-alias-label-primary);margin:0;font-size:13px;font-weight:500;line-height:1.5}.YVNYya_limitsTitle{border-top:.5px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);margin:16px 0 0;padding-top:12px;font-size:13px;font-weight:500;line-height:1.5}.YVNYya_limitsTitle+.YVNYya_hint{margin-top:4px}.YVNYya_group>.YVNYya_field:first-of-type{padding-top:12px}.YVNYya_group>.YVNYya_field+.YVNYya_field,.YVNYya_pairedFields{border-top:.5px solid var(--dsw-alias-border-l2)}.YVNYya_fieldHead{align-items:center;gap:8px;display:flex}.YVNYya_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.YVNYya_reset{color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border:0;padding:0;font-size:12px;line-height:1.5}.YVNYya_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.YVNYya_reset:disabled{cursor:default}.YVNYya_input{box-sizing:border-box;width:100%;min-width:0;display:flex}.YVNYya_select{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);width:100%;min-width:0;height:32px;color:var(--dsw-alias-label-primary);font:inherit;background:0 0;border-radius:8px;padding:0 8px;font-size:13px}.YVNYya_select:disabled{opacity:.5}.YVNYya_select:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.YVNYya_warning{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.YVNYya_pairedFields{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin:4px 0;padding:4px 0;display:grid}.YVNYya_pairedFields .YVNYya_field{min-width:0}.YVNYya_switch{box-sizing:border-box;background:var(--dsw-alias-border-l3);cursor:pointer;border:0;border-radius:10px;width:36px;height:20px;padding:2px;position:relative}.YVNYya_switchOn{background:var(--dsw-alias-brand-primary)}.YVNYya_switch:disabled{cursor:default;opacity:.5}.YVNYya_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.YVNYya_switchThumb{corner-shape:round;background:var(--dsw-alias-label-primary-foreground);border-radius:50%;width:16px;height:16px;transition:transform .12s;display:block}.YVNYya_switchOn .YVNYya_switchThumb{transform:translate(16px)}.YVNYya_modelSelection{gap:10px;padding:12px 0;display:grid}.YVNYya_notice{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.YVNYya_catalogError{color:var(--dsw-alias-label-error);justify-content:space-between;align-items:center;gap:12px;font-size:12px;line-height:1.5;display:flex}.YVNYya_catalogError button{color:var(--dsw-alias-brand-primary);cursor:pointer;font:inherit;background:0 0;border:0;padding:0}.YVNYya_models{border:.5px solid var(--dsw-alias-border-l4);border-radius:8px;gap:6px;min-width:0;max-height:280px;margin:0;padding:10px;display:grid;overflow:auto}.YVNYya_models legend{color:var(--dsw-alias-label-secondary);padding:0 4px;font-size:12px}.YVNYya_modelGroup{gap:6px;display:grid}.YVNYya_modelGroup+.YVNYya_modelGroup{border-top:.5px solid var(--dsw-alias-border-l3);margin-top:4px;padding-top:10px}.YVNYya_providerName{color:var(--dsw-alias-label-tertiary);padding:0 6px;font-size:11px;font-weight:500}.YVNYya_model{cursor:pointer;border-radius:6px;grid-template-columns:auto minmax(0,1fr) auto;align-items:center;gap:8px;min-width:0;padding:6px;display:grid}.YVNYya_model:hover{background:var(--dsw-alias-bg-layer-4)}.YVNYya_modelName,.YVNYya_route{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.YVNYya_modelName{color:var(--dsw-alias-label-primary);font-size:13px}.YVNYya_route{color:var(--dsw-alias-label-tertiary);margin-top:2px;font-size:11px}.YVNYya_unavailable{color:var(--dsw-alias-label-tertiary);font-size:11px}.YVNYya_error{color:var(--dsw-alias-label-error)}.YVNYya_advancedHint{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:12px}.YVNYya_footer{border-top:.5px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}@media (width<=480px){.YVNYya_pairedFields{grid-template-columns:minmax(0,1fr);gap:0}.YVNYya_pairedFields .YVNYya_field+.YVNYya_field{border-top:.5px solid var(--dsw-alias-border-l2)}}";
|
|
102
77
|
const tagId$1 = "dsh-hypatia-auto-memory/SettingsCard.module.css";
|
|
103
78
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
104
79
|
const tag = document.createElement("style");
|
|
@@ -111,10 +86,7 @@ window.__ModuleLoader__.load({
|
|
|
111
86
|
"advancedHint": "YVNYya_advancedHint",
|
|
112
87
|
"body": "YVNYya_body",
|
|
113
88
|
"card": "YVNYya_card",
|
|
114
|
-
"cardOpen": "YVNYya_cardOpen",
|
|
115
89
|
"catalogError": "YVNYya_catalogError",
|
|
116
|
-
"chevron": "YVNYya_chevron",
|
|
117
|
-
"chevronOpen": "YVNYya_chevronOpen",
|
|
118
90
|
"description": "YVNYya_description",
|
|
119
91
|
"error": "YVNYya_error",
|
|
120
92
|
"field": "YVNYya_field",
|
|
@@ -162,9 +134,9 @@ window.__ModuleLoader__.load({
|
|
|
162
134
|
description: "自动将对话轮次记录到 Hypatia,并在新内容足够多时通过独立模型路由进行后台整合。",
|
|
163
135
|
enable: "启用自动记忆",
|
|
164
136
|
autoApprove: "自动批准模型发起的 hypatia 命令",
|
|
165
|
-
autoApproveHint: "仅限不含管道、重定向、命令串联的纯 hypatia
|
|
137
|
+
autoApproveHint: "仅限不含管道、重定向、命令串联的纯 hypatia 调用;插件自身的写入不经过此路径。关闭后模型每次检索都会弹出批准框。保存后采集器会自动重启以应用新值。",
|
|
166
138
|
shelf: "记忆写入的 shelf",
|
|
167
|
-
shelfHint: "插件的记录、整合与会话开始时的规则预加载都读写这个 shelf;不是 default 时,会话开头会提示模型为 hypatia 命令加上 --shelf
|
|
139
|
+
shelfHint: "插件的记录、整合与会话开始时的规则预加载都读写这个 shelf;不是 default 时,会话开头会提示模型为 hypatia 命令加上 --shelf。保存后采集器会自动重启,改写到新的 shelf。每个 shelf 的记录进度单独保存,切回原来的 shelf 会从上次的位置继续;切到新的 shelf 后,每个有活动的会话会把完整历史重新记录进去并整合一遍(会消耗模型调用)。列表来自 hypatia list,打开本页时按需读取。",
|
|
168
140
|
shelfLoading: "正在读取 hypatia list…",
|
|
169
141
|
shelfLoadFailed: "无法读取 shelf 列表。",
|
|
170
142
|
shelfListingFailed: "hypatia list 执行失败,列表可能已过时:{message}",
|
|
@@ -194,7 +166,7 @@ window.__ModuleLoader__.load({
|
|
|
194
166
|
maxOutputTokens: "输出预算(token)",
|
|
195
167
|
maxWorkUnitsPerRun: "每次最多提取工作单元数",
|
|
196
168
|
recallPreload: "召回:会话开始时预加载规则与禁忌",
|
|
197
|
-
advancedHint: "
|
|
169
|
+
advancedHint: "高级字段(二进制、收集器上限、队列调优)可在「设置 → 插件」的本插件页,或 profile patch 的 {ns} 条目 config 下编辑。",
|
|
198
170
|
reset: "重置",
|
|
199
171
|
discard: "放弃",
|
|
200
172
|
save: "保存",
|
|
@@ -251,9 +223,9 @@ window.__ModuleLoader__.load({
|
|
|
251
223
|
description: "Auto-memory logs conversation turns into Hypatia and runs background consolidation on a dedicated model route.",
|
|
252
224
|
enable: "Enable auto-memory",
|
|
253
225
|
autoApprove: "Auto-approve the agent's own hypatia commands",
|
|
254
|
-
autoApproveHint: "Only plain hypatia calls with no pipe, redirect or chaining; this plugin's own writes never take that path. With it off, every retrieval raises an approval prompt.
|
|
226
|
+
autoApproveHint: "Only plain hypatia calls with no pipe, redirect or chaining; this plugin's own writes never take that path. With it off, every retrieval raises an approval prompt. Saving a change restarts the collector to apply it.",
|
|
255
227
|
shelf: "Memory shelf",
|
|
256
|
-
shelfHint: "Logging, consolidation and the session-start rules preload all read and write this shelf; when it is not default, each session is told to pass --shelf to its hypatia commands.
|
|
228
|
+
shelfHint: "Logging, consolidation and the session-start rules preload all read and write this shelf; when it is not default, each session is told to pass --shelf to its hypatia commands. Saving a change restarts the collector on the new shelf. Progress is kept per shelf, so switching back resumes where that shelf left off; on a new shelf, each session is logged from its start the next time it is active, and consolidated again (model calls). Choices come from hypatia list, read on demand when this page opens.",
|
|
257
229
|
shelfLoading: "Reading hypatia list…",
|
|
258
230
|
shelfLoadFailed: "Unable to load the shelf list.",
|
|
259
231
|
shelfListingFailed: "hypatia list failed; the list may be stale: {message}",
|
|
@@ -283,7 +255,7 @@ window.__ModuleLoader__.load({
|
|
|
283
255
|
maxOutputTokens: "Output budget (tokens)",
|
|
284
256
|
maxWorkUnitsPerRun: "Max work units per run",
|
|
285
257
|
recallPreload: "Recall: preload rules & taboos at session start",
|
|
286
|
-
advancedHint: "Advanced fields (binaries, collector caps, queue tuning) can be edited
|
|
258
|
+
advancedHint: "Advanced fields (binaries, collector caps, queue tuning) can be edited in Settings → Plugins under this plugin, or under the {ns} entry's config in the profile patch.",
|
|
287
259
|
reset: "Reset",
|
|
288
260
|
discard: "Discard",
|
|
289
261
|
save: "Save",
|
|
@@ -355,21 +327,21 @@ window.__ModuleLoader__.load({
|
|
|
355
327
|
batchSize: 16
|
|
356
328
|
}
|
|
357
329
|
};
|
|
358
|
-
function getSnapshotValue(
|
|
359
|
-
return
|
|
330
|
+
function getSnapshotValue(form) {
|
|
331
|
+
return form.getSnapshot();
|
|
360
332
|
}
|
|
361
|
-
function subscribe(
|
|
362
|
-
return
|
|
333
|
+
function subscribe(form, cb) {
|
|
334
|
+
return form.subscribe(cb);
|
|
363
335
|
}
|
|
364
336
|
function sectionValue(snap) {
|
|
365
337
|
return snap.value ?? snap.base ?? void 0;
|
|
366
338
|
}
|
|
367
|
-
function useScopeValue(
|
|
368
|
-
const [snap, setSnap] = (0, react.useState)(() => getSnapshotValue(
|
|
339
|
+
function useScopeValue(form) {
|
|
340
|
+
const [snap, setSnap] = (0, react.useState)(() => getSnapshotValue(form));
|
|
369
341
|
(0, react.useEffect)(() => {
|
|
370
|
-
setSnap(getSnapshotValue(
|
|
371
|
-
return subscribe(
|
|
372
|
-
}, [
|
|
342
|
+
setSnap(getSnapshotValue(form));
|
|
343
|
+
return subscribe(form, () => setSnap(getSnapshotValue(form)));
|
|
344
|
+
}, [form]);
|
|
373
345
|
return {
|
|
374
346
|
snap,
|
|
375
347
|
value: (0, react.useMemo)(() => sectionValue(snap), [snap])
|
|
@@ -390,7 +362,6 @@ window.__ModuleLoader__.load({
|
|
|
390
362
|
recall: { preloadRulesTaboos: true }
|
|
391
363
|
}, [value, base]);
|
|
392
364
|
const [draft, setDraft] = (0, react.useState)(resolved);
|
|
393
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
394
365
|
const [saving, setSaving] = (0, react.useState)(false);
|
|
395
366
|
const [error, setError] = (0, react.useState)(null);
|
|
396
367
|
const [catalogStatus, setCatalogStatus] = (0, react.useState)("idle");
|
|
@@ -433,12 +404,11 @@ window.__ModuleLoader__.load({
|
|
|
433
404
|
}
|
|
434
405
|
}, [loadModelCatalog]);
|
|
435
406
|
(0, react.useEffect)(() => {
|
|
436
|
-
if (!open) return;
|
|
437
407
|
loadCatalog();
|
|
438
408
|
return () => {
|
|
439
409
|
catalogGeneration.current += 1;
|
|
440
410
|
};
|
|
441
|
-
}, [
|
|
411
|
+
}, [loadCatalog]);
|
|
442
412
|
const loadShelves = (0, react.useCallback)(async () => {
|
|
443
413
|
const generation = ++shelfGeneration.current;
|
|
444
414
|
setShelfStatus("loading");
|
|
@@ -454,12 +424,11 @@ window.__ModuleLoader__.load({
|
|
|
454
424
|
}
|
|
455
425
|
}, [loadShelfInventory]);
|
|
456
426
|
(0, react.useEffect)(() => {
|
|
457
|
-
if (!open) return;
|
|
458
427
|
loadShelves();
|
|
459
428
|
return () => {
|
|
460
429
|
shelfGeneration.current += 1;
|
|
461
430
|
};
|
|
462
|
-
}, [
|
|
431
|
+
}, [loadShelves]);
|
|
463
432
|
const draftShelf = draft.shelf ?? "default";
|
|
464
433
|
const baseShelf = base.shelf ?? "default";
|
|
465
434
|
const choices = (0, react.useMemo)(() => shelfChoices(shelves, [
|
|
@@ -505,13 +474,20 @@ window.__ModuleLoader__.load({
|
|
|
505
474
|
setSaving(true);
|
|
506
475
|
setError(null);
|
|
507
476
|
try {
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
477
|
+
const ops = [];
|
|
478
|
+
const setField = (field, value) => {
|
|
479
|
+
ops.push({
|
|
480
|
+
op: "set",
|
|
481
|
+
path: [field],
|
|
482
|
+
value
|
|
483
|
+
});
|
|
484
|
+
};
|
|
485
|
+
if (!same(draft.enabled, resolved.enabled)) setField("enabled", draft.enabled);
|
|
486
|
+
if (!same(draft.autoApprove, resolved.autoApprove)) setField("autoApprove", draft.autoApprove);
|
|
487
|
+
if (!same(draft.shelf, resolved.shelf)) setField("shelf", draft.shelf);
|
|
488
|
+
if (!same(draft.consolidation, resolved.consolidation)) setField("consolidation", draft.consolidation);
|
|
489
|
+
if (!same(draft.recall, resolved.recall)) setField("recall", draft.recall);
|
|
490
|
+
if (ops.length > 0 && !await scope.mutate(ops)) throw new Error("the Host refused the change; nothing was saved");
|
|
515
491
|
} catch (err) {
|
|
516
492
|
const message = err instanceof Error ? err.message : String(err);
|
|
517
493
|
console.error(`[${NS}] save failed:`, err);
|
|
@@ -525,11 +501,11 @@ window.__ModuleLoader__.load({
|
|
|
525
501
|
saveBlocked,
|
|
526
502
|
scope
|
|
527
503
|
]);
|
|
528
|
-
if (snap.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
504
|
+
if (snap.status === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
529
505
|
className: SettingsCard_module_css_default.status,
|
|
530
506
|
children: t("loading", { ns: NS })
|
|
531
507
|
});
|
|
532
|
-
if (snap.status === "unavailable") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("
|
|
508
|
+
if (snap.status === "unavailable") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
533
509
|
className: SettingsCard_module_css_default.status,
|
|
534
510
|
children: t("unavailable", { ns: NS })
|
|
535
511
|
});
|
|
@@ -569,31 +545,25 @@ window.__ModuleLoader__.load({
|
|
|
569
545
|
}) : null
|
|
570
546
|
]
|
|
571
547
|
}, candidate.key);
|
|
572
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
573
|
-
className:
|
|
574
|
-
|
|
575
|
-
|
|
548
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
549
|
+
className: SettingsCard_module_css_default.card,
|
|
550
|
+
"aria-label": t("title"),
|
|
551
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
576
552
|
className: SettingsCard_module_css_default.header,
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
className: SettingsCard_module_css_default.pending,
|
|
592
|
-
children: t("unsaved")
|
|
593
|
-
}) : null,
|
|
594
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: `${SettingsCard_module_css_default.chevron} ${open ? SettingsCard_module_css_default.chevronOpen : ""}` })
|
|
595
|
-
]
|
|
596
|
-
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
553
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
554
|
+
className: SettingsCard_module_css_default.headText,
|
|
555
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
556
|
+
className: SettingsCard_module_css_default.name,
|
|
557
|
+
children: t("title")
|
|
558
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
559
|
+
className: SettingsCard_module_css_default.description,
|
|
560
|
+
children: t("description")
|
|
561
|
+
})]
|
|
562
|
+
}), dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
563
|
+
className: SettingsCard_module_css_default.pending,
|
|
564
|
+
children: t("unsaved")
|
|
565
|
+
}) : null]
|
|
566
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
597
567
|
className: SettingsCard_module_css_default.body,
|
|
598
568
|
children: [
|
|
599
569
|
!snap.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -1083,22 +1053,11 @@ window.__ModuleLoader__.load({
|
|
|
1083
1053
|
})]
|
|
1084
1054
|
})
|
|
1085
1055
|
]
|
|
1086
|
-
})
|
|
1056
|
+
})]
|
|
1087
1057
|
});
|
|
1088
1058
|
}
|
|
1089
1059
|
//#endregion
|
|
1090
1060
|
//#region src/client/memory-client.ts
|
|
1091
|
-
/**
|
|
1092
|
-
* The Memory tab's read side: one same-origin request per refresh.
|
|
1093
|
-
*
|
|
1094
|
-
* The Host half serves it from `/api/dsh-hypatia-auto-memory/session`
|
|
1095
|
-
* (see `src/memory-api.js`), which is parameterized by session and authenticates
|
|
1096
|
-
* itself — unlike a settings namespace, it can answer for the one session the
|
|
1097
|
-
* tab is bound to instead of shipping every session's state to the browser.
|
|
1098
|
-
*
|
|
1099
|
-
* Zero runtime imports beyond `fetch`, so the parsing half is unit-tested under
|
|
1100
|
-
* `node:test` like `shelves.ts`.
|
|
1101
|
-
*/
|
|
1102
1061
|
/** Route prefix the Host claims; the tab appends `/session`. */
|
|
1103
1062
|
const MEMORY_API_PREFIX = "/api/dsh-hypatia-auto-memory";
|
|
1104
1063
|
/**
|
|
@@ -1129,6 +1088,48 @@ window.__ModuleLoader__.load({
|
|
|
1129
1088
|
return payload;
|
|
1130
1089
|
};
|
|
1131
1090
|
/**
|
|
1091
|
+
* Fetch the shelf listing for the settings card.
|
|
1092
|
+
*
|
|
1093
|
+
* Rides the same self-authenticating route family as the Memory tab — the
|
|
1094
|
+
* settings page passes the same loopback / same-origin checks — because the
|
|
1095
|
+
* 0.1.7 settings domain no longer lets a Host plugin publish a dynamic,
|
|
1096
|
+
* non-config namespace for the card to read.
|
|
1097
|
+
*/
|
|
1098
|
+
const fetchShelves = async () => {
|
|
1099
|
+
const response = await fetch(`${MEMORY_API_PREFIX}/shelves`, {
|
|
1100
|
+
method: "GET",
|
|
1101
|
+
credentials: "same-origin",
|
|
1102
|
+
headers: { accept: "application/json" }
|
|
1103
|
+
});
|
|
1104
|
+
const body = await response.json().catch(() => void 0);
|
|
1105
|
+
if (!response.ok) {
|
|
1106
|
+
const message = isRecord(body) && typeof body.error === "string" ? body.error : `HTTP ${String(response.status)}`;
|
|
1107
|
+
throw new Error(message);
|
|
1108
|
+
}
|
|
1109
|
+
const inventory = parseShelfInventory(body);
|
|
1110
|
+
if (inventory === void 0) throw new Error("the Host returned an unreadable shelf listing");
|
|
1111
|
+
return inventory;
|
|
1112
|
+
};
|
|
1113
|
+
/**
|
|
1114
|
+
* Read the Host's `/shelves` answer into the card's shape, defensively.
|
|
1115
|
+
* @param value - the parsed response body.
|
|
1116
|
+
* @returns the listing, or undefined when the body is not one.
|
|
1117
|
+
*/
|
|
1118
|
+
function parseShelfInventory(value) {
|
|
1119
|
+
if (!isRecord(value)) return void 0;
|
|
1120
|
+
return {
|
|
1121
|
+
shelves: Array.isArray(value.shelves) ? value.shelves.flatMap((shelf) => {
|
|
1122
|
+
if (!isRecord(shelf) || typeof shelf.name !== "string" || shelf.name === "") return [];
|
|
1123
|
+
return [{
|
|
1124
|
+
name: shelf.name,
|
|
1125
|
+
path: typeof shelf.path === "string" ? shelf.path : "",
|
|
1126
|
+
connected: shelf.connected === true
|
|
1127
|
+
}];
|
|
1128
|
+
}) : [],
|
|
1129
|
+
error: str(value.error)
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1132
1133
|
* Session-log events that are recorded but not yet consolidated.
|
|
1133
1134
|
*
|
|
1134
1135
|
* Both watermarks index the same log, so their difference is exactly the work
|
|
@@ -1689,27 +1690,29 @@ window.__ModuleLoader__.load({
|
|
|
1689
1690
|
delete globalThis[APPLY_CLAIM];
|
|
1690
1691
|
}
|
|
1691
1692
|
/**
|
|
1692
|
-
* Required client services:
|
|
1693
|
-
*
|
|
1694
|
-
* `
|
|
1693
|
+
* Required client services: slots, locale, the Host model catalog, and the
|
|
1694
|
+
* settings domain's shared configuration forms (the 0.1.7 successor of the
|
|
1695
|
+
* removed `settingsScope` service). Each Remote namespace is its own service,
|
|
1696
|
+
* so `remote.session` must be declared here or reading it throws.
|
|
1695
1697
|
*/
|
|
1696
1698
|
const inject = [
|
|
1697
1699
|
"slots",
|
|
1698
|
-
"
|
|
1700
|
+
"configForms",
|
|
1699
1701
|
"locale",
|
|
1700
1702
|
"remote",
|
|
1701
|
-
"remote.session"
|
|
1702
|
-
"remote.settings"
|
|
1703
|
+
"remote.session"
|
|
1703
1704
|
];
|
|
1704
1705
|
/** Mount the browser half. */
|
|
1705
1706
|
function apply(ctx) {
|
|
1706
1707
|
if (!claimApply()) return;
|
|
1707
|
-
const
|
|
1708
|
+
const configForms = ctx.get("configForms");
|
|
1709
|
+
const form = configForms.get(NS);
|
|
1708
1710
|
const locale = ctx.get("locale");
|
|
1709
1711
|
ctx.effect(() => locale.register(NS, {
|
|
1710
1712
|
zh,
|
|
1711
1713
|
en
|
|
1712
1714
|
}), "dsh-hypatia-auto-memory: locale dictionaries");
|
|
1715
|
+
const t = locale.bind(NS);
|
|
1713
1716
|
const loadModelCatalog = async () => {
|
|
1714
1717
|
const response = await ctx.remote.session.modelCatalog();
|
|
1715
1718
|
if (!response.ok) throw new Error(`${response.error.code}: ${response.error.message}`);
|
|
@@ -1718,25 +1721,19 @@ window.__ModuleLoader__.load({
|
|
|
1718
1721
|
partial: response.value.failures.length > 0
|
|
1719
1722
|
};
|
|
1720
1723
|
};
|
|
1721
|
-
const loadShelfInventory =
|
|
1722
|
-
const response = await ctx.remote.settings.describe();
|
|
1723
|
-
if (!response.ok) throw new Error(`${response.error.code}: ${response.error.message}`);
|
|
1724
|
-
const inventory = readShelfInventory(response.value.namespaces);
|
|
1725
|
-
if (inventory === void 0) throw new Error("the Host publishes no shelf listing");
|
|
1726
|
-
return inventory;
|
|
1727
|
-
};
|
|
1724
|
+
const loadShelfInventory = fetchShelves;
|
|
1728
1725
|
const renderCard = (props) => (0, react.createElement)(SettingsCard, {
|
|
1729
|
-
scope,
|
|
1726
|
+
scope: form,
|
|
1730
1727
|
loadModelCatalog,
|
|
1731
1728
|
loadShelfInventory,
|
|
1732
1729
|
...props
|
|
1733
1730
|
});
|
|
1734
|
-
const unregister = ctx.slots.inject("settings.
|
|
1735
|
-
name: "settings.
|
|
1736
|
-
|
|
1731
|
+
const unregister = ctx.effect(() => configForms.whileServed([NS], () => ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
|
|
1732
|
+
name: "settings.plugins.tab",
|
|
1733
|
+
id: NS,
|
|
1734
|
+
label: () => t("title"),
|
|
1737
1735
|
locale: NS
|
|
1738
|
-
}, renderCard));
|
|
1739
|
-
const t = locale.bind(NS);
|
|
1736
|
+
}, renderCard))), "dsh-hypatia-auto-memory: plugins settings tab");
|
|
1740
1737
|
const unregisterMemoryView = ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
1741
1738
|
name: "conversation.view",
|
|
1742
1739
|
id: "memory",
|