dsh-hypatia-auto-memory 1.1.1 → 2.1.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 +90 -17
- package/README.zh-CN.md +67 -16
- package/lib/client.js +488 -17
- package/lib/client.js.map +1 -1
- package/package.json +2 -1
- package/skills/hypatia-memory/SKILL.md +47 -3
- package/src/cascade.js +39 -19
- package/src/client/MemoryView.module.css +38 -1
- package/src/client/MemoryView.tsx +94 -1
- package/src/client/SettingsCard.module.css +83 -0
- package/src/client/SettingsCard.tsx +160 -15
- package/src/client/consolidation-models.ts +151 -1
- package/src/client/index.tsx +2 -2
- package/src/client/locales.ts +48 -4
- package/src/client/memory-client.ts +96 -0
- package/src/config.js +20 -3
- package/src/consolidator.js +245 -115
- package/src/index.js +70 -9
- package/src/memory-api.js +81 -7
- package/src/model-log.js +64 -15
- package/src/on-demand.js +249 -0
- package/src/reasoning.js +209 -0
- package/src/writer.js +7 -3
package/lib/client.js
CHANGED
|
@@ -13,6 +13,108 @@ window.__ModuleLoader__.load({
|
|
|
13
13
|
return `${route.provider}\0${route.model}`;
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
+
* Move one entry of the priority list, returning a new array.
|
|
17
|
+
*
|
|
18
|
+
* The stored `consolidation.models` array IS the priority order, so this is the
|
|
19
|
+
* only thing a reorder has to do: reorder the array. `to` is clamped instead of
|
|
20
|
+
* rejected, so a click that cannot move anything (the first row's "up") is a
|
|
21
|
+
* no-op rather than an error. A no-op returns the ORIGINAL reference, which lets
|
|
22
|
+
* a caller use identity to skip the state update.
|
|
23
|
+
*
|
|
24
|
+
* @param models - Priority-ordered routes; not mutated.
|
|
25
|
+
* @param from - Index to move.
|
|
26
|
+
* @param to - Target index, clamped into `models`.
|
|
27
|
+
*/
|
|
28
|
+
function moveConsolidationModel(models, from, to) {
|
|
29
|
+
if (!Number.isInteger(from) || from < 0 || from >= models.length) return models;
|
|
30
|
+
if (!Number.isFinite(to)) return models;
|
|
31
|
+
const target = Math.min(Math.max(0, Math.trunc(to)), models.length - 1);
|
|
32
|
+
if (target === from) return models;
|
|
33
|
+
const next = [...models];
|
|
34
|
+
const [moved] = next.splice(from, 1);
|
|
35
|
+
next.splice(target, 0, moved);
|
|
36
|
+
return next;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The stored routes in their configured (priority) order, each resolved to its
|
|
40
|
+
* catalog entry for a display name, an availability flag and the reasoning
|
|
41
|
+
* levels the route declares.
|
|
42
|
+
*
|
|
43
|
+
* `consolidationModelCandidates` already guarantees every stored route appears —
|
|
44
|
+
* taken from the catalog, or appended as unavailable — so the candidate list is
|
|
45
|
+
* a lookup table for this. A route that somehow is not in it is still shown,
|
|
46
|
+
* named by its raw ids and marked unavailable, because dropping it from the
|
|
47
|
+
* priority list would hide a configured route from the only UI that can remove
|
|
48
|
+
* it.
|
|
49
|
+
*
|
|
50
|
+
* `reasoningEffort` is taken from the STORED route, never from the catalog: the
|
|
51
|
+
* catalog describes what a model can do, the stored route is what the user chose.
|
|
52
|
+
*/
|
|
53
|
+
function orderedConsolidationModels(stored, candidates) {
|
|
54
|
+
const byKey = new Map(candidates.map((candidate) => [candidate.key, candidate]));
|
|
55
|
+
return stored.map((route) => {
|
|
56
|
+
const key = consolidationModelKey(route);
|
|
57
|
+
return {
|
|
58
|
+
...byKey.get(key) ?? {
|
|
59
|
+
...route,
|
|
60
|
+
key,
|
|
61
|
+
providerName: route.provider,
|
|
62
|
+
modelName: route.model,
|
|
63
|
+
available: false,
|
|
64
|
+
selected: true
|
|
65
|
+
},
|
|
66
|
+
reasoningEffort: route.reasoningEffort
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The thinking-level options for one route, in the order they are offered.
|
|
72
|
+
*
|
|
73
|
+
* Two rules, both about not inventing a vocabulary this plugin does not own:
|
|
74
|
+
*
|
|
75
|
+
* - The list comes from the route's own declared `efforts`, in the adapter's
|
|
76
|
+
* preferred order, labelled with the adapter's own names. Nothing is
|
|
77
|
+
* hard-coded — the ids are opaque, and a level one adapter accepts is not one
|
|
78
|
+
* another does.
|
|
79
|
+
* - "Follow the purpose policy" (`undefined`) is always first and is what an
|
|
80
|
+
* absent field stores. It is NOT the same as the adapter's `defaultEffort`:
|
|
81
|
+
* omitting the field lets the plugin's own per-purpose policy apply, which for
|
|
82
|
+
* extraction and adjudication means asking for no reasoning at all.
|
|
83
|
+
*
|
|
84
|
+
* A stored value the route no longer declares is appended and flagged rather
|
|
85
|
+
* than dropped, so the card can show what is actually configured instead of
|
|
86
|
+
* silently rewriting it — and so a route whose catalog entry disappeared still
|
|
87
|
+
* displays its setting.
|
|
88
|
+
*
|
|
89
|
+
* @param reasoning - The route's catalog metadata; absent when the catalog does
|
|
90
|
+
* not advertise the route, or the model declares no reasoning control.
|
|
91
|
+
* @param stored - The configured value, if any.
|
|
92
|
+
*/
|
|
93
|
+
function reasoningEffortChoices(reasoning, stored) {
|
|
94
|
+
const efforts = Array.isArray(reasoning?.efforts) ? reasoning.efforts : [];
|
|
95
|
+
const choices = [{
|
|
96
|
+
key: "purpose",
|
|
97
|
+
effort: void 0
|
|
98
|
+
}];
|
|
99
|
+
for (const effort of efforts) {
|
|
100
|
+
const id = typeof effort?.id === "string" ? effort.id : "";
|
|
101
|
+
if (id === "") continue;
|
|
102
|
+
const name = typeof effort.name === "string" && effort.name !== "" ? effort.name : id;
|
|
103
|
+
choices.push({
|
|
104
|
+
key: `effort:${id}`,
|
|
105
|
+
effort: id,
|
|
106
|
+
name
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const value = typeof stored === "string" ? stored.trim() : "";
|
|
110
|
+
if (value !== "" && !efforts.some((effort) => effort?.id === value)) choices.push({
|
|
111
|
+
key: `stale:${value}`,
|
|
112
|
+
effort: value,
|
|
113
|
+
stale: true
|
|
114
|
+
});
|
|
115
|
+
return choices;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
16
118
|
* Join the live model directory with selected routes that disappeared from it.
|
|
17
119
|
* Disappeared routes remain visible so users can remove them deliberately.
|
|
18
120
|
*/
|
|
@@ -31,7 +133,8 @@ window.__ModuleLoader__.load({
|
|
|
31
133
|
providerName: group.name,
|
|
32
134
|
modelName: model.name,
|
|
33
135
|
available: true,
|
|
34
|
-
selected: selected.has(key)
|
|
136
|
+
selected: selected.has(key),
|
|
137
|
+
...model.reasoning === void 0 ? {} : { reasoning: model.reasoning }
|
|
35
138
|
};
|
|
36
139
|
}));
|
|
37
140
|
for (const route of storedByKey.values()) {
|
|
@@ -73,7 +176,7 @@ window.__ModuleLoader__.load({
|
|
|
73
176
|
}
|
|
74
177
|
//#endregion
|
|
75
178
|
//#region \0dsh-hypatia-css:/home/runner/work/dsh-hypatia-auto-memory/dsh-hypatia-auto-memory/src/client/SettingsCard.module.css.mjs
|
|
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)}}";
|
|
179
|
+
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_selectedRow{border-radius:6px;align-items:center;gap:8px;min-width:0;padding:6px;display:flex}.YVNYya_selectedRow:hover{background:var(--dsw-alias-bg-layer-4)}.YVNYya_order{min-width:16px;color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;text-align:right;font-size:11px}.YVNYya_selectedText{flex:auto;min-width:0}.YVNYya_effort{border:.5px solid var(--dsw-alias-border-l4);max-width:132px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border-radius:5px;flex:none;padding:2px 4px;font-size:12px}.YVNYya_effort:disabled{cursor:default;opacity:.5}.YVNYya_effort:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.YVNYya_rowActions{align-items:center;gap:2px;display:flex}.YVNYya_move{border:.5px solid var(--dsw-alias-border-l4);width:22px;height:22px;color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border-radius:5px;padding:0;font-size:12px;line-height:1}.YVNYya_move:hover:not(:disabled){background:var(--dsw-alias-bg-layer-4);color:var(--dsw-alias-label-primary)}.YVNYya_move:disabled{cursor:default;opacity:.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)}}";
|
|
77
180
|
const tagId$1 = "dsh-hypatia-auto-memory/SettingsCard.module.css";
|
|
78
181
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
79
182
|
const tag = document.createElement("style");
|
|
@@ -88,6 +191,7 @@ window.__ModuleLoader__.load({
|
|
|
88
191
|
"card": "YVNYya_card",
|
|
89
192
|
"catalogError": "YVNYya_catalogError",
|
|
90
193
|
"description": "YVNYya_description",
|
|
194
|
+
"effort": "YVNYya_effort",
|
|
91
195
|
"error": "YVNYya_error",
|
|
92
196
|
"field": "YVNYya_field",
|
|
93
197
|
"fieldHead": "YVNYya_fieldHead",
|
|
@@ -105,15 +209,20 @@ window.__ModuleLoader__.load({
|
|
|
105
209
|
"modelName": "YVNYya_modelName",
|
|
106
210
|
"modelSelection": "YVNYya_modelSelection",
|
|
107
211
|
"models": "YVNYya_models",
|
|
212
|
+
"move": "YVNYya_move",
|
|
108
213
|
"name": "YVNYya_name",
|
|
109
214
|
"notice": "YVNYya_notice",
|
|
215
|
+
"order": "YVNYya_order",
|
|
110
216
|
"pairedFields": "YVNYya_pairedFields",
|
|
111
217
|
"pending": "YVNYya_pending",
|
|
112
218
|
"providerName": "YVNYya_providerName",
|
|
113
219
|
"readOnly": "YVNYya_readOnly",
|
|
114
220
|
"reset": "YVNYya_reset",
|
|
115
221
|
"route": "YVNYya_route",
|
|
222
|
+
"rowActions": "YVNYya_rowActions",
|
|
116
223
|
"select": "YVNYya_select",
|
|
224
|
+
"selectedRow": "YVNYya_selectedRow",
|
|
225
|
+
"selectedText": "YVNYya_selectedText",
|
|
117
226
|
"status": "YVNYya_status",
|
|
118
227
|
"switch": "YVNYya_switch",
|
|
119
228
|
"switchOn": "YVNYya_switchOn",
|
|
@@ -146,14 +255,25 @@ window.__ModuleLoader__.load({
|
|
|
146
255
|
shelfDisconnectedWarning: "shelf {shelf} 已注册但未连接,写入会失败。",
|
|
147
256
|
shelfNotListedWarning: "hypatia 没有注册 shelf {shelf},写入会失败;请先执行 hypatia connect <目录> --name {shelf}。",
|
|
148
257
|
consolidationTitle: "整合候选模型",
|
|
149
|
-
modelSelectionHint: "
|
|
258
|
+
modelSelectionHint: "选择一个或多个模型并决定优先级。列表自上而下即优先级:每次整合先用最上面那条,该次尝试失败时,重试会依次降级到下一条(可用的备选条数受队列重试次数限制)。未选择时只记录对话,不执行整合。",
|
|
150
259
|
modelCatalogLoading: "正在读取可用模型…",
|
|
151
260
|
modelCatalogFailed: "无法读取可用模型。",
|
|
152
261
|
modelCatalogPartial: "部分提供方未能返回模型目录。",
|
|
153
262
|
modelCatalogEmpty: "当前没有可选择的模型。",
|
|
154
|
-
selectedModels: "
|
|
263
|
+
selectedModels: "优先级顺序(自上而下)",
|
|
264
|
+
selectedModelsEmpty: "还没有选择模型。",
|
|
265
|
+
availableModels: "可选模型",
|
|
155
266
|
unavailableModels: "不可用或未再声明的模型",
|
|
156
267
|
modelUnavailable: "当前不可用",
|
|
268
|
+
moveUp: "上移",
|
|
269
|
+
moveDown: "下移",
|
|
270
|
+
remove: "移除",
|
|
271
|
+
reasoningEffort: "思考程度",
|
|
272
|
+
reasoningFollowPurpose: "跟随用途策略",
|
|
273
|
+
reasoningUnavailable: "该路由未声明思考档位,无法设置",
|
|
274
|
+
reasoningStale: "该路由已不再声明",
|
|
275
|
+
reasoningDefault: "未指定时该路由默认为:{effort}",
|
|
276
|
+
reasoningBudgetHint: "思考程度决定该模型思考多久。选「跟随用途策略」时,摘要与裁决会尽量关闭思考(两者都是机械变换,思考只花掉输出预算),归档则沿用模型自己的默认档位。显式选了档位就会覆盖上述策略;非关闭档位与正文共用输出预算,插件会自动追加余量,但高强度档位仍建议同步调大输出预算。档位由模型自己声明,未声明的值会在调用时被忽略并记一条警告。",
|
|
157
277
|
retry: "重试",
|
|
158
278
|
checkEveryTurns: "两次整合至少间隔 N 轮",
|
|
159
279
|
minNewTokens: "最小新增 token 数",
|
|
@@ -187,6 +307,17 @@ window.__ModuleLoader__.load({
|
|
|
187
307
|
memoryConsolidated: "摘要整合",
|
|
188
308
|
memoryCaughtUp: "已结清",
|
|
189
309
|
memoryBehind: "较记录落后 {behind} 条",
|
|
310
|
+
memorySummarize: "立即整理",
|
|
311
|
+
memorySummarizing: "整理中…",
|
|
312
|
+
memorySummarizeHint: "不等阈值,立刻把这段尚未摘要的对话交给后台整合。已结清时按钮禁用但保留在原位,下次落后时不必重新找它。",
|
|
313
|
+
memorySummarizeQueued: "已入队:将整理第 {from} – {to} 条事件;摘要在后台由插件的专用模型路由写入,不会占用本会话的上下文。",
|
|
314
|
+
memorySummarizeEmpty: "已结清,没有需要整理的内容。",
|
|
315
|
+
memorySummarizeBusy: "同样的区间已有一次整合在队列中,无需重复触发。",
|
|
316
|
+
memorySummarizeDisabled: "整合功能已在设置中关闭。",
|
|
317
|
+
memorySummarizeUnavailable: "整合不可用:当前组合没有挂载模型路由。",
|
|
318
|
+
memorySummarizeUnknown: "这个会话已经不在内存中,无法整理。",
|
|
319
|
+
memorySummarizeUnknownReason: "未能整理:{reason}",
|
|
320
|
+
memorySummarizeFailed: "无法触发整理:{message}",
|
|
190
321
|
memoryPending: "待整合内容",
|
|
191
322
|
memoryPendingValue: "约 {tokens} token",
|
|
192
323
|
memorySessionNode: "会话节点",
|
|
@@ -235,14 +366,25 @@ window.__ModuleLoader__.load({
|
|
|
235
366
|
shelfDisconnectedWarning: "Shelf {shelf} is registered but not connected; writes will fail.",
|
|
236
367
|
shelfNotListedWarning: "hypatia has no shelf named {shelf}; writes will fail until you run hypatia connect <dir> --name {shelf}.",
|
|
237
368
|
consolidationTitle: "Consolidation candidate models",
|
|
238
|
-
modelSelectionHint: "Choose one or more models.
|
|
369
|
+
modelSelectionHint: "Choose one or more models and set their priority. The list is read top-down: every consolidation starts on the first route, and each failed attempt degrades to the next one (how many fallbacks a task can reach is bounded by the queue's retry limit). Without a selection, conversations continue logging but consolidation stays idle.",
|
|
239
370
|
modelCatalogLoading: "Loading available models…",
|
|
240
371
|
modelCatalogFailed: "Unable to load available models.",
|
|
241
372
|
modelCatalogPartial: "Some providers did not return a model catalog.",
|
|
242
373
|
modelCatalogEmpty: "No models are currently available.",
|
|
243
|
-
selectedModels: "
|
|
374
|
+
selectedModels: "Priority order (top first)",
|
|
375
|
+
selectedModelsEmpty: "No model selected yet.",
|
|
376
|
+
availableModels: "Available models",
|
|
244
377
|
unavailableModels: "Unavailable or no longer advertised",
|
|
245
378
|
modelUnavailable: "Unavailable",
|
|
379
|
+
moveUp: "Move up",
|
|
380
|
+
moveDown: "Move down",
|
|
381
|
+
remove: "Remove",
|
|
382
|
+
reasoningEffort: "Reasoning effort",
|
|
383
|
+
reasoningFollowPurpose: "Follow the purpose policy",
|
|
384
|
+
reasoningUnavailable: "This route declares no reasoning levels",
|
|
385
|
+
reasoningStale: "no longer declared by this route",
|
|
386
|
+
reasoningDefault: "Without one, this route defaults to: {effort}",
|
|
387
|
+
reasoningBudgetHint: "Reasoning effort is how long the model may think. On \"follow the purpose policy\", summaries and adjudication ask for no reasoning where the route allows it — both are mechanical transforms, so thinking only spends the output budget — while the archive call keeps the model's own default. Choosing a level explicitly overrides that policy; any level other than off shares the output budget with the answer, so the plugin adds its thinking allowance, and a high level is still worth pairing with a larger output budget. Levels are declared by the model itself, and one it does not declare is dropped at call time with a warning.",
|
|
246
388
|
retry: "Retry",
|
|
247
389
|
checkEveryTurns: "Minimum turns between consolidations",
|
|
248
390
|
minNewTokens: "Min new tokens",
|
|
@@ -276,6 +418,17 @@ window.__ModuleLoader__.load({
|
|
|
276
418
|
memoryConsolidated: "Summarisation",
|
|
277
419
|
memoryCaughtUp: "caught up",
|
|
278
420
|
memoryBehind: "{behind} events behind the log",
|
|
421
|
+
memorySummarize: "Summarise now",
|
|
422
|
+
memorySummarizing: "Summarising…",
|
|
423
|
+
memorySummarizeHint: "Consolidate the part of this conversation that has not been summarised yet, without waiting for the thresholds. Once caught up the button is disabled but stays in place, so it need not be rediscovered the next time the session falls behind.",
|
|
424
|
+
memorySummarizeQueued: "Queued: events {from}–{to} will be summarised. The summary is written in the background on the plugin's own model route and costs this session no context.",
|
|
425
|
+
memorySummarizeEmpty: "Nothing to summarise: the log is already consolidated.",
|
|
426
|
+
memorySummarizeBusy: "A consolidation covering the same span is already queued.",
|
|
427
|
+
memorySummarizeDisabled: "Consolidation is turned off in the settings.",
|
|
428
|
+
memorySummarizeUnavailable: "Consolidation is unavailable: this composition mounted no model route for it.",
|
|
429
|
+
memorySummarizeUnknown: "That session is no longer in memory, so it cannot be consolidated.",
|
|
430
|
+
memorySummarizeUnknownReason: "Could not consolidate: {reason}",
|
|
431
|
+
memorySummarizeFailed: "Could not start consolidation: {message}",
|
|
279
432
|
memoryPending: "Waiting to summarise",
|
|
280
433
|
memoryPendingValue: "~{tokens} tokens",
|
|
281
434
|
memorySessionNode: "Session node",
|
|
@@ -447,6 +600,7 @@ window.__ModuleLoader__.load({
|
|
|
447
600
|
const models = consolidation.models ?? [];
|
|
448
601
|
return consolidationModelCandidates(catalogGroups, models, new Set(models.map(consolidationModelKey)));
|
|
449
602
|
}, [catalogGroups, consolidation.models]);
|
|
603
|
+
const selectedModels = (0, react.useMemo)(() => orderedConsolidationModels(consolidation.models ?? [], candidates), [consolidation.models, candidates]);
|
|
450
604
|
const toggleModel = (0, react.useCallback)((candidate) => {
|
|
451
605
|
if (disabled || saving) return;
|
|
452
606
|
setDraft((current) => {
|
|
@@ -466,6 +620,77 @@ window.__ModuleLoader__.load({
|
|
|
466
620
|
};
|
|
467
621
|
});
|
|
468
622
|
}, [disabled, saving]);
|
|
623
|
+
/**
|
|
624
|
+
* Reorder one entry of the priority list. A no-op move leaves the draft
|
|
625
|
+
* untouched (the mover returns the original array), so the "unsaved" badge
|
|
626
|
+
* does not light up for a click that changed nothing.
|
|
627
|
+
*/
|
|
628
|
+
const moveModel = (0, react.useCallback)((from, to) => {
|
|
629
|
+
if (disabled || saving) return;
|
|
630
|
+
setDraft((current) => {
|
|
631
|
+
const currentConsolidation = current.consolidation ?? DEFAULT_CONSOLIDATION;
|
|
632
|
+
const currentModels = currentConsolidation.models ?? [];
|
|
633
|
+
const models = moveConsolidationModel(currentModels, from, to);
|
|
634
|
+
if (models === currentModels) return current;
|
|
635
|
+
return {
|
|
636
|
+
...current,
|
|
637
|
+
consolidation: {
|
|
638
|
+
...currentConsolidation,
|
|
639
|
+
models
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
});
|
|
643
|
+
}, [disabled, saving]);
|
|
644
|
+
/**
|
|
645
|
+
* Set or clear one route's thinking level. Clearing REMOVES the key rather
|
|
646
|
+
* than storing `undefined`, so the saved patch carries only what the user
|
|
647
|
+
* chose and nothing reading the profile can confuse "not configured" with
|
|
648
|
+
* "configured as nothing".
|
|
649
|
+
*/
|
|
650
|
+
const setModelEffort = (0, react.useCallback)((index, effort) => {
|
|
651
|
+
if (disabled || saving) return;
|
|
652
|
+
setDraft((current) => {
|
|
653
|
+
const currentConsolidation = current.consolidation ?? DEFAULT_CONSOLIDATION;
|
|
654
|
+
const currentModels = currentConsolidation.models ?? [];
|
|
655
|
+
if (index < 0 || index >= currentModels.length) return current;
|
|
656
|
+
const models = currentModels.map((route, at) => {
|
|
657
|
+
if (at !== index) return route;
|
|
658
|
+
if (effort === void 0) {
|
|
659
|
+
const cleared = { ...route };
|
|
660
|
+
delete cleared.reasoningEffort;
|
|
661
|
+
return cleared;
|
|
662
|
+
}
|
|
663
|
+
return {
|
|
664
|
+
...route,
|
|
665
|
+
reasoningEffort: effort
|
|
666
|
+
};
|
|
667
|
+
});
|
|
668
|
+
return {
|
|
669
|
+
...current,
|
|
670
|
+
consolidation: {
|
|
671
|
+
...currentConsolidation,
|
|
672
|
+
models
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
});
|
|
676
|
+
}, [disabled, saving]);
|
|
677
|
+
/** Display label for one thinking-level option, in the card's own words. */
|
|
678
|
+
const effortChoiceLabel = (0, react.useCallback)((choice) => {
|
|
679
|
+
if (choice.effort === void 0) return t("reasoningFollowPurpose");
|
|
680
|
+
const name = choice.name ?? choice.effort;
|
|
681
|
+
return choice.stale === true ? `${name} · ${t("reasoningStale")}` : name;
|
|
682
|
+
}, [t]);
|
|
683
|
+
/**
|
|
684
|
+
* What the adapter does when no effort is sent. Only a hint: this plugin
|
|
685
|
+
* cannot pin it, because omitting the field is what lets its own per-purpose
|
|
686
|
+
* policy apply.
|
|
687
|
+
*/
|
|
688
|
+
const routeDefaultLabel = (0, react.useCallback)((candidate) => {
|
|
689
|
+
const reasoning = candidate.reasoning;
|
|
690
|
+
const fallback = reasoning?.defaultEffort;
|
|
691
|
+
if (reasoning === void 0 || fallback === void 0) return void 0;
|
|
692
|
+
return t("reasoningDefault", { effort: reasoning.efforts.find((effort) => effort.id === fallback)?.name ?? fallback });
|
|
693
|
+
}, [t]);
|
|
469
694
|
const discard = (0, react.useCallback)(() => {
|
|
470
695
|
setDraft(resolved);
|
|
471
696
|
setError(null);
|
|
@@ -756,10 +981,82 @@ window.__ModuleLoader__.load({
|
|
|
756
981
|
className: SettingsCard_module_css_default.notice,
|
|
757
982
|
children: t("modelCatalogPartial")
|
|
758
983
|
}) : null,
|
|
759
|
-
candidates.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
984
|
+
candidates.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
985
|
+
className: SettingsCard_module_css_default.models,
|
|
986
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: t("selectedModels") }), selectedModels.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
987
|
+
className: SettingsCard_module_css_default.notice,
|
|
988
|
+
children: t("selectedModelsEmpty")
|
|
989
|
+
}) : selectedModels.map((candidate, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
990
|
+
className: SettingsCard_module_css_default.selectedRow,
|
|
991
|
+
children: [
|
|
992
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
993
|
+
className: SettingsCard_module_css_default.order,
|
|
994
|
+
children: index + 1
|
|
995
|
+
}),
|
|
996
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
997
|
+
className: SettingsCard_module_css_default.selectedText,
|
|
998
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
999
|
+
className: SettingsCard_module_css_default.modelName,
|
|
1000
|
+
children: candidate.modelName
|
|
1001
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1002
|
+
className: SettingsCard_module_css_default.route,
|
|
1003
|
+
children: `${candidate.providerName} · ${candidate.provider}/${candidate.model}`
|
|
1004
|
+
})]
|
|
1005
|
+
}),
|
|
1006
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
1007
|
+
className: SettingsCard_module_css_default.effort,
|
|
1008
|
+
"aria-label": `${t("reasoningEffort")}: ${candidate.modelName}`,
|
|
1009
|
+
title: candidate.reasoning === void 0 ? t("reasoningUnavailable") : routeDefaultLabel(candidate) ?? t("reasoningFollowPurpose"),
|
|
1010
|
+
value: candidate.reasoningEffort ?? "",
|
|
1011
|
+
disabled: disabled || saving || candidate.reasoning === void 0,
|
|
1012
|
+
onChange: (event) => setModelEffort(index, event.target.value === "" ? void 0 : event.target.value),
|
|
1013
|
+
children: reasoningEffortChoices(candidate.reasoning, candidate.reasoningEffort).map((choice) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
1014
|
+
value: choice.effort ?? "",
|
|
1015
|
+
children: effortChoiceLabel(choice)
|
|
1016
|
+
}, choice.key))
|
|
1017
|
+
}),
|
|
1018
|
+
!candidate.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1019
|
+
className: SettingsCard_module_css_default.unavailable,
|
|
1020
|
+
children: t("modelUnavailable")
|
|
1021
|
+
}) : null,
|
|
1022
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1023
|
+
className: SettingsCard_module_css_default.rowActions,
|
|
1024
|
+
children: [
|
|
1025
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1026
|
+
type: "button",
|
|
1027
|
+
className: SettingsCard_module_css_default.move,
|
|
1028
|
+
title: t("moveUp"),
|
|
1029
|
+
"aria-label": `${t("moveUp")}: ${candidate.modelName}`,
|
|
1030
|
+
disabled: disabled || saving || index === 0,
|
|
1031
|
+
onClick: () => moveModel(index, index - 1),
|
|
1032
|
+
children: "↑"
|
|
1033
|
+
}),
|
|
1034
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1035
|
+
type: "button",
|
|
1036
|
+
className: SettingsCard_module_css_default.move,
|
|
1037
|
+
title: t("moveDown"),
|
|
1038
|
+
"aria-label": `${t("moveDown")}: ${candidate.modelName}`,
|
|
1039
|
+
disabled: disabled || saving || index === selectedModels.length - 1,
|
|
1040
|
+
onClick: () => moveModel(index, index + 1),
|
|
1041
|
+
children: "↓"
|
|
1042
|
+
}),
|
|
1043
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1044
|
+
type: "button",
|
|
1045
|
+
className: SettingsCard_module_css_default.move,
|
|
1046
|
+
title: t("remove"),
|
|
1047
|
+
"aria-label": `${t("remove")}: ${candidate.modelName}`,
|
|
1048
|
+
disabled: disabled || saving,
|
|
1049
|
+
onClick: () => toggleModel(candidate),
|
|
1050
|
+
children: "×"
|
|
1051
|
+
})
|
|
1052
|
+
]
|
|
1053
|
+
})
|
|
1054
|
+
]
|
|
1055
|
+
}, candidate.key))]
|
|
1056
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
|
|
760
1057
|
className: SettingsCard_module_css_default.models,
|
|
761
1058
|
children: [
|
|
762
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: t("
|
|
1059
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", { children: t("availableModels") }),
|
|
763
1060
|
[...availableGroups].map(([provider, group]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
764
1061
|
className: SettingsCard_module_css_default.modelGroup,
|
|
765
1062
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -775,10 +1072,14 @@ window.__ModuleLoader__.load({
|
|
|
775
1072
|
}), unavailable.map(renderCandidate)]
|
|
776
1073
|
}) : null
|
|
777
1074
|
]
|
|
778
|
-
}) : catalogStatus === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1075
|
+
})] }) : catalogStatus === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
779
1076
|
className: SettingsCard_module_css_default.notice,
|
|
780
1077
|
children: t("modelCatalogEmpty")
|
|
781
|
-
}) : null
|
|
1078
|
+
}) : null,
|
|
1079
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1080
|
+
className: SettingsCard_module_css_default.hint,
|
|
1081
|
+
children: t("reasoningBudgetHint")
|
|
1082
|
+
})
|
|
782
1083
|
]
|
|
783
1084
|
}),
|
|
784
1085
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1062,6 +1363,22 @@ window.__ModuleLoader__.load({
|
|
|
1062
1363
|
/** Route prefix the Host claims; the tab appends `/session`. */
|
|
1063
1364
|
const MEMORY_API_PREFIX = "/api/dsh-hypatia-auto-memory";
|
|
1064
1365
|
/**
|
|
1366
|
+
* Why an explicit consolidation request queued nothing, matching the Host's
|
|
1367
|
+
* `SUMMARIZE_REASON` vocabulary (src/consolidator.js). Kept as named constants
|
|
1368
|
+
* so the view's switch and the Host's answers cannot drift apart on a typo.
|
|
1369
|
+
*/
|
|
1370
|
+
const SUMMARIZE_REASON = {
|
|
1371
|
+
disabled: "disabled",
|
|
1372
|
+
empty: "empty",
|
|
1373
|
+
busy: "busy",
|
|
1374
|
+
unknownSession: "unknown-session",
|
|
1375
|
+
unavailable: "unavailable"
|
|
1376
|
+
};
|
|
1377
|
+
/** Whether "summarise now" has work to do for this session. */
|
|
1378
|
+
function canSummarize(session) {
|
|
1379
|
+
return session.known && !session.caughtUp;
|
|
1380
|
+
}
|
|
1381
|
+
/**
|
|
1065
1382
|
* The URL one refresh reads.
|
|
1066
1383
|
* @param sessionId - the session the tab is bound to.
|
|
1067
1384
|
* @param content - whether to ask the Host to read the shelf as well.
|
|
@@ -1089,6 +1406,59 @@ window.__ModuleLoader__.load({
|
|
|
1089
1406
|
return payload;
|
|
1090
1407
|
};
|
|
1091
1408
|
/**
|
|
1409
|
+
* The URL one explicit consolidation request POSTs to.
|
|
1410
|
+
* @param sessionId - the session to consolidate.
|
|
1411
|
+
* @returns a same-origin path with its query.
|
|
1412
|
+
*/
|
|
1413
|
+
function summarizeUrl(sessionId) {
|
|
1414
|
+
const query = new URLSearchParams({ session: sessionId });
|
|
1415
|
+
return `${MEMORY_API_PREFIX}/consolidate?${query.toString()}`;
|
|
1416
|
+
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Ask the Host to consolidate one session now.
|
|
1419
|
+
*
|
|
1420
|
+
* `POST`, never `GET`: a write must not be reachable from a link or an image
|
|
1421
|
+
* tag. The Host keeps the same self-authentication on this route as on the read
|
|
1422
|
+
* ones (loopback socket, loopback `Host`, same-origin), and the body is empty —
|
|
1423
|
+
* everything the request means is in the URL.
|
|
1424
|
+
*/
|
|
1425
|
+
const summarizeMemory = async (sessionId) => {
|
|
1426
|
+
const response = await fetch(summarizeUrl(sessionId), {
|
|
1427
|
+
method: "POST",
|
|
1428
|
+
credentials: "same-origin",
|
|
1429
|
+
headers: { accept: "application/json" }
|
|
1430
|
+
});
|
|
1431
|
+
const body = await response.json().catch(() => void 0);
|
|
1432
|
+
if (!response.ok) {
|
|
1433
|
+
const message = isRecord(body) && typeof body.error === "string" ? body.error : `HTTP ${String(response.status)}`;
|
|
1434
|
+
throw new Error(message);
|
|
1435
|
+
}
|
|
1436
|
+
const result = parseSummarizeResult(body, sessionId);
|
|
1437
|
+
if (result === void 0) throw new Error("the Host returned an unreadable consolidation answer");
|
|
1438
|
+
return result;
|
|
1439
|
+
};
|
|
1440
|
+
/**
|
|
1441
|
+
* Read the Host's consolidation answer, defensively.
|
|
1442
|
+
*
|
|
1443
|
+
* A body without a boolean `queued` is unreadable rather than "not queued": the
|
|
1444
|
+
* whole point of the answer is to distinguish "accepted" from "nothing to do",
|
|
1445
|
+
* and guessing would put a wrong sentence in front of the user.
|
|
1446
|
+
*
|
|
1447
|
+
* @param value - the parsed response body.
|
|
1448
|
+
* @param sessionId - the session that was asked for.
|
|
1449
|
+
* @returns the result, or undefined when the body is not one.
|
|
1450
|
+
*/
|
|
1451
|
+
function parseSummarizeResult(value, sessionId) {
|
|
1452
|
+
if (!isRecord(value) || typeof value.queued !== "boolean") return void 0;
|
|
1453
|
+
return {
|
|
1454
|
+
sessionId: str(value.sessionId) || sessionId,
|
|
1455
|
+
queued: value.queued,
|
|
1456
|
+
reason: str(value.reason),
|
|
1457
|
+
fromSeq: num(value.fromSeq),
|
|
1458
|
+
toSeq: num(value.toSeq)
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1092
1462
|
* Fetch the shelf listing for the settings card.
|
|
1093
1463
|
*
|
|
1094
1464
|
* Rides the same self-authenticating route family as the Memory tab — the
|
|
@@ -1252,7 +1622,7 @@ window.__ModuleLoader__.load({
|
|
|
1252
1622
|
}
|
|
1253
1623
|
//#endregion
|
|
1254
1624
|
//#region \0dsh-hypatia-css:/home/runner/work/dsh-hypatia-auto-memory/dsh-hypatia-auto-memory/src/client/MemoryView.module.css.mjs
|
|
1255
|
-
const css = ".LBuOzG_panel{box-sizing:border-box;width:100%;height:100%;min-height:0;padding:16px;padding-bottom:calc(var(--dsh-composer-height,152px) + 16px);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);flex-direction:column;align-items:center;display:flex;overflow-y:auto}.LBuOzG_column{flex-direction:column;gap:20px;width:100%;min-width:0;max-width:880px;display:flex}.LBuOzG_head{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.LBuOzG_title{color:var(--dsw-alias-label-primary);margin:0;font-size:15px;font-weight:600;line-height:1.4}.LBuOzG_headMeta{align-items:center;gap:10px;display:flex}.LBuOzG_updated{color:var(--dsw-alias-label-tertiary);font-size:12px}.LBuOzG_refresh{appearance:none;border:.5px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border-radius:8px;padding:2px 8px;font-size:12px}.LBuOzG_refresh:hover{border-color:var(--dsw-alias-label-dimmed);color:var(--dsw-alias-label-primary)}.LBuOzG_refresh:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.LBuOzG_status,.LBuOzG_hint,.LBuOzG_error{margin:0;font-size:12px;line-height:1.6}.LBuOzG_status,.LBuOzG_hint{color:var(--dsw-alias-label-tertiary)}.LBuOzG_error{color:var(--dsw-alias-label-error)}.LBuOzG_group{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:16px;display:flex}.LBuOzG_groupTitle{color:var(--dsw-alias-label-primary);align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;display:flex}.LBuOzG_count{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.LBuOzG_rows{grid-template-columns:max-content minmax(0,1fr);align-items:baseline;gap:7px 20px;margin:0;display:grid}.LBuOzG_row{display:contents}.LBuOzG_label{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px}.LBuOzG_value{min-width:0;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;align-items:baseline;gap:8px;margin:0;font-size:12px;display:flex}.LBuOzG_calm,.LBuOzG_busy{white-space:nowrap;border-radius:6px;padding:1px 6px;font-size:11px}.LBuOzG_calm{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}.LBuOzG_busy{color:var(--dsw-alias-label-primary);border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3)}.LBuOzG_failures{gap:6px;margin:0;padding:0;list-style:none;display:grid}.LBuOzG_failure{background:var(--dsw-alias-bg-layer-2);border-radius:8px;gap:2px;padding:8px 10px;display:grid}.LBuOzG_failureKind{color:var(--dsw-alias-label-primary);font-size:12px}.LBuOzG_failureError{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:11px;line-height:1.5}.LBuOzG_entries{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}.LBuOzG_entry{border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;overflow:hidden}.LBuOzG_entryHead{appearance:none;width:100%;color:inherit;cursor:pointer;font:inherit;text-align:left;background:0 0;border:0;justify-content:space-between;align-items:baseline;gap:12px;padding:8px 12px;display:flex}.LBuOzG_entryHead:hover{background:var(--dsw-alias-bg-layer-3)}.LBuOzG_entryHead:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.LBuOzG_entryName{min-width:0;color:var(--dsw-alias-label-secondary);font-family:var(--dsw-font-family-mono,ui-monospace, SFMono-Regular, Menlo, monospace);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.LBuOzG_entryMeta{flex:none;align-items:baseline;gap:8px;display:flex}.LBuOzG_badge{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;padding:1px 6px;font-size:11px}.LBuOzG_entryTime{color:var(--dsw-alias-label-quaternary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}.LBuOzG_entryToggle{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:11px}.LBuOzG_entryBody{border-top:.5px solid var(--dsw-alias-border-l2);overflow-wrap:anywhere;padding:10px 12px;font-size:13px;line-height:1.65}.LBuOzG_entryPreview{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;margin:0;padding:0 12px 10px;font-size:12px;line-height:1.5;overflow:hidden}.LBuOzG_hint{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:12px}";
|
|
1625
|
+
const css = ".LBuOzG_panel{box-sizing:border-box;width:100%;height:100%;min-height:0;padding:16px;padding-bottom:calc(var(--dsh-composer-height,152px) + 16px);color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-1);flex-direction:column;align-items:center;display:flex;overflow-y:auto}.LBuOzG_column{flex-direction:column;gap:20px;width:100%;min-width:0;max-width:880px;display:flex}.LBuOzG_head{justify-content:space-between;align-items:baseline;gap:12px;display:flex}.LBuOzG_title{color:var(--dsw-alias-label-primary);margin:0;font-size:15px;font-weight:600;line-height:1.4}.LBuOzG_headMeta{align-items:center;gap:10px;display:flex}.LBuOzG_updated{color:var(--dsw-alias-label-tertiary);font-size:12px}.LBuOzG_refresh{appearance:none;border:.5px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;background:0 0;border-radius:8px;padding:2px 8px;font-size:12px}.LBuOzG_refresh:hover{border-color:var(--dsw-alias-label-dimmed);color:var(--dsw-alias-label-primary)}.LBuOzG_refresh:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.LBuOzG_status,.LBuOzG_hint,.LBuOzG_error,.LBuOzG_notice{margin:0;font-size:12px;line-height:1.6}.LBuOzG_status,.LBuOzG_hint{color:var(--dsw-alias-label-tertiary)}.LBuOzG_error{color:var(--dsw-alias-label-error)}.LBuOzG_notice{color:var(--dsw-alias-label-secondary)}.LBuOzG_group{border-top:.5px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:16px;display:flex}.LBuOzG_groupTitle{color:var(--dsw-alias-label-primary);align-items:baseline;gap:8px;margin:0;font-size:13px;font-weight:600;display:flex}.LBuOzG_count{color:var(--dsw-alias-label-tertiary);font-variant-numeric:tabular-nums;font-size:11px;font-weight:400}.LBuOzG_rows{grid-template-columns:max-content minmax(0,1fr);align-items:baseline;gap:7px 20px;margin:0;display:grid}.LBuOzG_row{display:contents}.LBuOzG_label{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:12px}.LBuOzG_value{min-width:0;color:var(--dsw-alias-label-primary);font-variant-numeric:tabular-nums;align-items:baseline;gap:8px;margin:0;font-size:12px;display:flex}.LBuOzG_calm,.LBuOzG_busy{white-space:nowrap;border-radius:6px;padding:1px 6px;font-size:11px}.LBuOzG_calm{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-bg-layer-2)}.LBuOzG_busy{color:var(--dsw-alias-label-primary);border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3)}.LBuOzG_summarize{appearance:none;border:.5px solid var(--dsw-alias-border-l4);color:var(--dsw-alias-label-secondary);cursor:pointer;font:inherit;white-space:nowrap;background:0 0;border-radius:6px;padding:1px 8px;font-size:11px}.LBuOzG_summarize:hover:enabled{border-color:var(--dsw-alias-label-dimmed);color:var(--dsw-alias-label-primary)}.LBuOzG_summarize:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.LBuOzG_summarize:disabled{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-dimmed);cursor:default}.LBuOzG_failures{gap:6px;margin:0;padding:0;list-style:none;display:grid}.LBuOzG_failure{background:var(--dsw-alias-bg-layer-2);border-radius:8px;gap:2px;padding:8px 10px;display:grid}.LBuOzG_failureKind{color:var(--dsw-alias-label-primary);font-size:12px}.LBuOzG_failureError{color:var(--dsw-alias-label-tertiary);overflow-wrap:anywhere;font-size:11px;line-height:1.5}.LBuOzG_entries{flex-direction:column;gap:8px;margin:0;padding:0;list-style:none;display:flex}.LBuOzG_entry{border:.5px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);border-radius:10px;overflow:hidden}.LBuOzG_entryHead{appearance:none;width:100%;color:inherit;cursor:pointer;font:inherit;text-align:left;background:0 0;border:0;justify-content:space-between;align-items:baseline;gap:12px;padding:8px 12px;display:flex}.LBuOzG_entryHead:hover{background:var(--dsw-alias-bg-layer-3)}.LBuOzG_entryHead:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.LBuOzG_entryName{min-width:0;color:var(--dsw-alias-label-secondary);font-family:var(--dsw-font-family-mono,ui-monospace, SFMono-Regular, Menlo, monospace);text-overflow:ellipsis;white-space:nowrap;font-size:11px;overflow:hidden}.LBuOzG_entryMeta{flex:none;align-items:baseline;gap:8px;display:flex}.LBuOzG_badge{background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-tertiary);white-space:nowrap;border-radius:6px;padding:1px 6px;font-size:11px}.LBuOzG_entryTime{color:var(--dsw-alias-label-quaternary);font-variant-numeric:tabular-nums;white-space:nowrap;font-size:11px}.LBuOzG_entryToggle{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-size:11px}.LBuOzG_entryBody{border-top:.5px solid var(--dsw-alias-border-l2);overflow-wrap:anywhere;padding:10px 12px;font-size:13px;line-height:1.65}.LBuOzG_entryPreview{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;margin:0;padding:0 12px 10px;font-size:12px;line-height:1.5;overflow:hidden}.LBuOzG_hint{border-top:.5px solid var(--dsw-alias-border-l2);padding-top:12px}";
|
|
1256
1626
|
const tagId = "dsh-hypatia-auto-memory/MemoryView.module.css";
|
|
1257
1627
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1258
1628
|
const tag = document.createElement("style");
|
|
@@ -1287,11 +1657,13 @@ window.__ModuleLoader__.load({
|
|
|
1287
1657
|
"headMeta": "LBuOzG_headMeta",
|
|
1288
1658
|
"hint": "LBuOzG_hint",
|
|
1289
1659
|
"label": "LBuOzG_label",
|
|
1660
|
+
"notice": "LBuOzG_notice",
|
|
1290
1661
|
"panel": "LBuOzG_panel",
|
|
1291
1662
|
"refresh": "LBuOzG_refresh",
|
|
1292
1663
|
"row": "LBuOzG_row",
|
|
1293
1664
|
"rows": "LBuOzG_rows",
|
|
1294
1665
|
"status": "LBuOzG_status",
|
|
1666
|
+
"summarize": "LBuOzG_summarize",
|
|
1295
1667
|
"title": "LBuOzG_title",
|
|
1296
1668
|
"updated": "LBuOzG_updated",
|
|
1297
1669
|
"value": "LBuOzG_value"
|
|
@@ -1319,6 +1691,48 @@ window.__ModuleLoader__.load({
|
|
|
1319
1691
|
*/
|
|
1320
1692
|
/** How often the tab re-reads the status half while it is on screen. */
|
|
1321
1693
|
const POLL_MS = 5e3;
|
|
1694
|
+
/**
|
|
1695
|
+
* Render the Host's answer to one request.
|
|
1696
|
+
*
|
|
1697
|
+
* "Nothing to do" and "already queued" are not failures and do not read as one:
|
|
1698
|
+
* the request was answered truthfully. Only a refusal — the feature is off, the
|
|
1699
|
+
* route is missing, the session is gone — is shown as an error.
|
|
1700
|
+
*/
|
|
1701
|
+
function noticeFor(result, t) {
|
|
1702
|
+
if (result.queued) return {
|
|
1703
|
+
kind: "ok",
|
|
1704
|
+
text: t("memorySummarizeQueued", {
|
|
1705
|
+
from: String(result.fromSeq),
|
|
1706
|
+
to: String(result.toSeq)
|
|
1707
|
+
})
|
|
1708
|
+
};
|
|
1709
|
+
switch (result.reason) {
|
|
1710
|
+
case SUMMARIZE_REASON.empty: return {
|
|
1711
|
+
kind: "ok",
|
|
1712
|
+
text: t("memorySummarizeEmpty")
|
|
1713
|
+
};
|
|
1714
|
+
case SUMMARIZE_REASON.busy: return {
|
|
1715
|
+
kind: "ok",
|
|
1716
|
+
text: t("memorySummarizeBusy")
|
|
1717
|
+
};
|
|
1718
|
+
case SUMMARIZE_REASON.disabled: return {
|
|
1719
|
+
kind: "error",
|
|
1720
|
+
text: t("memorySummarizeDisabled")
|
|
1721
|
+
};
|
|
1722
|
+
case SUMMARIZE_REASON.unavailable: return {
|
|
1723
|
+
kind: "error",
|
|
1724
|
+
text: t("memorySummarizeUnavailable")
|
|
1725
|
+
};
|
|
1726
|
+
case SUMMARIZE_REASON.unknownSession: return {
|
|
1727
|
+
kind: "error",
|
|
1728
|
+
text: t("memorySummarizeUnknown")
|
|
1729
|
+
};
|
|
1730
|
+
default: return {
|
|
1731
|
+
kind: "error",
|
|
1732
|
+
text: t("memorySummarizeUnknownReason", { reason: result.reason })
|
|
1733
|
+
};
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1322
1736
|
/** Local wall-clock stamp for the freshness line. */
|
|
1323
1737
|
function formatTime(epochMs) {
|
|
1324
1738
|
if (!Number.isFinite(epochMs) || epochMs <= 0) return "—";
|
|
@@ -1385,10 +1799,14 @@ window.__ModuleLoader__.load({
|
|
|
1385
1799
|
})]
|
|
1386
1800
|
});
|
|
1387
1801
|
}
|
|
1388
|
-
function MemoryView({ sessionId, fetch, t }) {
|
|
1802
|
+
function MemoryView({ sessionId, fetch, summarize, t }) {
|
|
1389
1803
|
const [payload, setPayload] = (0, react.useState)(void 0);
|
|
1390
1804
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
1391
1805
|
const [error, setError] = (0, react.useState)("");
|
|
1806
|
+
/** True while the Host is being asked to queue a consolidation. */
|
|
1807
|
+
const [summarizing, setSummarizing] = (0, react.useState)(false);
|
|
1808
|
+
/** The last explicit request's answer, cleared when the session switches. */
|
|
1809
|
+
const [notice, setNotice] = (0, react.useState)(void 0);
|
|
1392
1810
|
/**
|
|
1393
1811
|
* The entries the reader opened, by name. Absent means closed, which is the
|
|
1394
1812
|
* default: a toggle survives the poll that replaces the payload, and an entry
|
|
@@ -1437,6 +1855,7 @@ window.__ModuleLoader__.load({
|
|
|
1437
1855
|
setPayload(void 0);
|
|
1438
1856
|
setOverrides({});
|
|
1439
1857
|
setContentAt(void 0);
|
|
1858
|
+
setNotice(void 0);
|
|
1440
1859
|
setLoading(true);
|
|
1441
1860
|
load(true);
|
|
1442
1861
|
}, [load, sessionId]);
|
|
@@ -1462,6 +1881,39 @@ window.__ModuleLoader__.load({
|
|
|
1462
1881
|
[name]: !open
|
|
1463
1882
|
}));
|
|
1464
1883
|
}, []);
|
|
1884
|
+
/**
|
|
1885
|
+
* Ask the Host to consolidate this session now.
|
|
1886
|
+
*
|
|
1887
|
+
* Only queues: the summary is written in the background, so the answer says
|
|
1888
|
+
* what was accepted rather than what was written. A queued request refreshes
|
|
1889
|
+
* the status so the "behind" figure catches up with the next poll; the
|
|
1890
|
+
* watermark watch above then re-reads the shelf when consolidation lands.
|
|
1891
|
+
*/
|
|
1892
|
+
const onSummarize = (0, react.useCallback)(() => {
|
|
1893
|
+
setSummarizing(true);
|
|
1894
|
+
setNotice(void 0);
|
|
1895
|
+
(async () => {
|
|
1896
|
+
try {
|
|
1897
|
+
const result = await summarize(sessionId);
|
|
1898
|
+
if (activeSession.current !== sessionId) return;
|
|
1899
|
+
setNotice(noticeFor(result, t));
|
|
1900
|
+
if (result.queued) load(false);
|
|
1901
|
+
} catch (failure) {
|
|
1902
|
+
if (activeSession.current !== sessionId) return;
|
|
1903
|
+
setNotice({
|
|
1904
|
+
kind: "error",
|
|
1905
|
+
text: t("memorySummarizeFailed", { message: failure instanceof Error ? failure.message : String(failure) })
|
|
1906
|
+
});
|
|
1907
|
+
} finally {
|
|
1908
|
+
if (activeSession.current === sessionId) setSummarizing(false);
|
|
1909
|
+
}
|
|
1910
|
+
})();
|
|
1911
|
+
}, [
|
|
1912
|
+
load,
|
|
1913
|
+
sessionId,
|
|
1914
|
+
summarize,
|
|
1915
|
+
t
|
|
1916
|
+
]);
|
|
1465
1917
|
const session = payload?.session;
|
|
1466
1918
|
const content = payload?.content;
|
|
1467
1919
|
const summaries = content?.summaries ?? [];
|
|
@@ -1511,6 +1963,11 @@ window.__ModuleLoader__.load({
|
|
|
1511
1963
|
role: "status",
|
|
1512
1964
|
children: t("memoryLoadFailed", { message: error })
|
|
1513
1965
|
}) : null,
|
|
1966
|
+
notice !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1967
|
+
className: notice.kind === "ok" ? MemoryView_module_css_default.notice : MemoryView_module_css_default.error,
|
|
1968
|
+
role: "status",
|
|
1969
|
+
children: notice.text
|
|
1970
|
+
}) : null,
|
|
1514
1971
|
loading && payload === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1515
1972
|
className: MemoryView_module_css_default.status,
|
|
1516
1973
|
children: t("memoryLoading")
|
|
@@ -1543,10 +2000,21 @@ window.__ModuleLoader__.load({
|
|
|
1543
2000
|
children: t("memoryConsolidated")
|
|
1544
2001
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dd", {
|
|
1545
2002
|
className: MemoryView_module_css_default.value,
|
|
1546
|
-
children: [
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
2003
|
+
children: [
|
|
2004
|
+
t("memorySeqValue", { seq: String(session.consolidatedSeq) }),
|
|
2005
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2006
|
+
className: session.caughtUp ? MemoryView_module_css_default.calm : MemoryView_module_css_default.busy,
|
|
2007
|
+
children: session.caughtUp ? t("memoryCaughtUp") : t("memoryBehind", { behind: String(consolidationGap(session)) })
|
|
2008
|
+
}),
|
|
2009
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2010
|
+
type: "button",
|
|
2011
|
+
className: MemoryView_module_css_default.summarize,
|
|
2012
|
+
disabled: summarizing || !canSummarize(session),
|
|
2013
|
+
title: t("memorySummarizeHint"),
|
|
2014
|
+
onClick: onSummarize,
|
|
2015
|
+
children: summarizing ? t("memorySummarizing") : t("memorySummarize")
|
|
2016
|
+
})
|
|
2017
|
+
]
|
|
1550
2018
|
})]
|
|
1551
2019
|
}),
|
|
1552
2020
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1867,7 +2335,10 @@ window.__ModuleLoader__.load({
|
|
|
1867
2335
|
order: 20,
|
|
1868
2336
|
locale: NS,
|
|
1869
2337
|
label: () => t("viewMemory"),
|
|
1870
|
-
inject: () => ({
|
|
2338
|
+
inject: () => ({
|
|
2339
|
+
fetch: fetchMemory,
|
|
2340
|
+
summarize: summarizeMemory
|
|
2341
|
+
})
|
|
1871
2342
|
}, MemoryView));
|
|
1872
2343
|
ctx.effect(() => () => {
|
|
1873
2344
|
unregister();
|