dsh-loci 3.0.1 → 3.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/CHANGELOG.md +36 -0
- package/client.js +69 -6
- package/package.json +1 -1
- package/src/host/index.js +1 -1
- package/src/mcp/store-facade.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project follows
|
|
5
5
|
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) from 1.0.0.
|
|
6
6
|
|
|
7
|
+
## [3.1.0] — 2026-11-15
|
|
8
|
+
|
|
9
|
+
Four things from looking at the live sidebar and panel.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **The sidebar icon now has the same weight as the menu it sits in.** Native
|
|
14
|
+
`sidebar.panellist` entries receive `{ size, active }`; this one used `size` correctly but
|
|
15
|
+
drew inside 4–20 of a 24 viewBox with a small off-centre dot, so beside the shipped icons
|
|
16
|
+
it read thinner and smaller. Redrawn as a symmetric **layers** glyph spanning 3–21 at
|
|
17
|
+
stroke 1.8 — the optical weight of its neighbours, and a shape that says layered memory.
|
|
18
|
+
|
|
19
|
+
- **The panel is a readable column, not edge-to-edge text.** The scroll container's
|
|
20
|
+
horizontal padding became `max(16px, calc((100% - 860px) / 2))`, so rows sit in a centred
|
|
21
|
+
measure on a wide window without touching any tab component.
|
|
22
|
+
|
|
23
|
+
- **"N item(s) waiting for you in Review" is now a way in.** It was a sentence telling a
|
|
24
|
+
person to go somewhere else and find the tab. It is a button: it opens the panel already
|
|
25
|
+
on Review. The card and the panel mount separately, so the request travels through module
|
|
26
|
+
scope and is consumed once.
|
|
27
|
+
|
|
28
|
+
- **The card says what it knows, not only how much.** Commit `4065c98` (2026-11-13) recorded
|
|
29
|
+
the brief for this surface: *"not a dashboard … read the card and think 'it got that, it
|
|
30
|
+
recorded that, we agreed on that'"*, with one short paragraph of what the agent
|
|
31
|
+
understands in this project and pending work as one quiet line, never a number. 3.0.0
|
|
32
|
+
replaced that paragraph with counters. The counters stay — they were asked for and they
|
|
33
|
+
are useful — and one line of content goes back under them: a `Latest` row showing the most
|
|
34
|
+
recently trusted entry, from `GET /api/loci/knowledge?limit=1`.
|
|
35
|
+
|
|
36
|
+
### Not changed
|
|
37
|
+
|
|
38
|
+
- `docs/PRIOR-ART.md` still records that the card should be scoped to **this project's read
|
|
39
|
+
set** rather than every scope. It currently is not: `/stats` and `/knowledge` called
|
|
40
|
+
without parameters enumerate the whole store, and the sidebar card has no access to the
|
|
41
|
+
session's cwd. Scoping the card is a real open item, not something to assert.
|
|
42
|
+
|
|
7
43
|
## [3.0.1] — 2026-11-15
|
|
8
44
|
|
|
9
45
|
The 3.0.0 card was correct markup with no stylesheet, and the screenshot showed it.
|
package/client.js
CHANGED
|
@@ -27,6 +27,9 @@ window.__ModuleLoader__.load({
|
|
|
27
27
|
const API = '/api/loci';
|
|
28
28
|
const ENTRY_ID = 'dsh-loci';
|
|
29
29
|
const PANEL_ID = 'loci';
|
|
30
|
+
// The card and the panel mount separately, so a request to open a specific tab has
|
|
31
|
+
// to travel through module scope. The panel consumes it once, then clears it.
|
|
32
|
+
let requestedTab;
|
|
30
33
|
|
|
31
34
|
const EN = {
|
|
32
35
|
'meta.title': 'Loci memory palace',
|
|
@@ -44,6 +47,7 @@ window.__ModuleLoader__.load({
|
|
|
44
47
|
'card.opened': 'Opened',
|
|
45
48
|
'card.dead': 'Dead',
|
|
46
49
|
'card.pendingHint': '{n} item(s) waiting for you in Review.',
|
|
50
|
+
'card.latest': 'Latest',
|
|
47
51
|
'card.updated': 'updated',
|
|
48
52
|
'card.pendingSentence': '{n} still need your review',
|
|
49
53
|
'card.pinned': 'pinned',
|
|
@@ -194,6 +198,11 @@ window.__ModuleLoader__.load({
|
|
|
194
198
|
.loci-card-foot-grow { flex: 1 1 auto; }
|
|
195
199
|
.loci-card-empty { font-size: 9.5px; color: var(--dsw-alias-label-secondary); }
|
|
196
200
|
.loci-card-hint { font-size: 9px; color: var(--dsw-alias-label-secondary); }
|
|
201
|
+
.loci-card-known { display: flex; gap: 5px; align-items: baseline; min-width: 0; }
|
|
202
|
+
.loci-card-known-tag { flex: 0 0 auto; font-size: 8px; letter-spacing: .05em; text-transform: uppercase; color: var(--dsw-alias-label-secondary); }
|
|
203
|
+
.loci-card-known-text { min-width: 0; font-size: 9.5px; color: var(--dsw-alias-label-primary); overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
|
204
|
+
.loci-card-hint-action { align-self: flex-start; margin: 0; padding: 2px 6px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 6px; background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); font: inherit; font-size: 9px; line-height: 1.2; cursor: pointer; text-align: left; }
|
|
205
|
+
.loci-card-hint-action:hover { border-color: var(--dsw-alias-brand-primary); color: var(--dsw-alias-brand-primary); }
|
|
197
206
|
.loci-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--dsw-alias-brand-primary); flex: 0 0 auto; }
|
|
198
207
|
|
|
199
208
|
.loci-bar { display: flex; align-items: center; gap: 12px; padding: 10px 16px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
|
|
@@ -203,7 +212,7 @@ window.__ModuleLoader__.load({
|
|
|
203
212
|
.loci-tab:hover { background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); }
|
|
204
213
|
.loci-tab.is-active { background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); font-weight: 600; }
|
|
205
214
|
.loci-grow { flex: 1 1 auto; }
|
|
206
|
-
.loci-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding:
|
|
215
|
+
.loci-body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 14px max(16px, calc((100% - 860px) / 2)) 28px; }
|
|
207
216
|
.loci-btn { border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-bg-layer-1); color: var(--dsw-alias-label-primary); border-radius: 6px; padding: 3px 9px; font: inherit; cursor: pointer; }
|
|
208
217
|
.loci-btn:hover { border-color: var(--dsw-alias-border-l2); background: var(--dsw-alias-bg-layer-2); }
|
|
209
218
|
.loci-btn:disabled { opacity: .5; cursor: default; }
|
|
@@ -438,6 +447,7 @@ window.__ModuleLoader__.load({
|
|
|
438
447
|
// read as bare text inside the sidebar rather than as a card — and the audit's own
|
|
439
448
|
// "no dashboard" objection was about refusing to interrupt, not about hiding data.
|
|
440
449
|
const stats = useJson('/stats', { intervalMs: 30_000 });
|
|
450
|
+
const knowledge = useJson('/knowledge?limit=1', { intervalMs: 30_000 });
|
|
441
451
|
const config = useJson('/config');
|
|
442
452
|
const update = useJson('/update');
|
|
443
453
|
const [checking, setChecking] = useState(false);
|
|
@@ -468,6 +478,10 @@ window.__ModuleLoader__.load({
|
|
|
468
478
|
const injected = usage.reduce((total, row) => total + Number(row.injections ?? 0), 0);
|
|
469
479
|
const opened = usage.reduce((total, row) => total + Number(row.reads ?? 0), 0);
|
|
470
480
|
const dead = usage.filter((row) => Number(row.injections ?? 0) > 0 && Number(row.reads ?? 0) === 0).length;
|
|
481
|
+
// What the agent currently understands, newest first. Numbers answer "how much";
|
|
482
|
+
// this answers "what".
|
|
483
|
+
const top = knowledge.data?.items?.[0];
|
|
484
|
+
const latest = typeof top?.title === 'string' && top.title.length > 0 ? top.title : null;
|
|
471
485
|
|
|
472
486
|
if (!wide) {
|
|
473
487
|
return h(
|
|
@@ -548,6 +562,14 @@ window.__ModuleLoader__.load({
|
|
|
548
562
|
metric(t('card.pending'), pending, pending > 0 ? 'is-warn' : ''),
|
|
549
563
|
metric(t('card.disputed'), disputed, disputed > 0 ? 'is-error' : ''),
|
|
550
564
|
),
|
|
565
|
+
data !== undefined && latest !== null
|
|
566
|
+
? h(
|
|
567
|
+
'div',
|
|
568
|
+
{ className: 'loci-card-known' },
|
|
569
|
+
h('span', { className: 'loci-card-known-tag' }, t('card.latest')),
|
|
570
|
+
h('span', { className: 'loci-card-known-text' }, latest),
|
|
571
|
+
)
|
|
572
|
+
: null,
|
|
551
573
|
data !== undefined && entries > 0 && budget > 0
|
|
552
574
|
? h(
|
|
553
575
|
'div',
|
|
@@ -573,7 +595,21 @@ window.__ModuleLoader__.load({
|
|
|
573
595
|
h('span', { className: 'loci-card-foot-grow' }),
|
|
574
596
|
)
|
|
575
597
|
: null,
|
|
576
|
-
pending > 0
|
|
598
|
+
pending > 0
|
|
599
|
+
? h(
|
|
600
|
+
'button',
|
|
601
|
+
{
|
|
602
|
+
type: 'button',
|
|
603
|
+
className: 'loci-card-hint loci-card-hint-action',
|
|
604
|
+
onClick: (event) => {
|
|
605
|
+
event.stopPropagation();
|
|
606
|
+
requestedTab = 'review';
|
|
607
|
+
open?.();
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
t('card.pendingHint').replace('{n}', String(pending)),
|
|
611
|
+
)
|
|
612
|
+
: null,
|
|
577
613
|
);
|
|
578
614
|
}
|
|
579
615
|
// ---------------------------------------------------------------- panel
|
|
@@ -1136,7 +1172,12 @@ window.__ModuleLoader__.load({
|
|
|
1136
1172
|
}
|
|
1137
1173
|
|
|
1138
1174
|
function MemoryPanel({ t }) {
|
|
1139
|
-
const [tab, setTab] = useState(
|
|
1175
|
+
const [tab, setTab] = useState(() => {
|
|
1176
|
+
// "N item(s) waiting for you in Review" in the card is a way in, not a remark.
|
|
1177
|
+
const wanted = requestedTab;
|
|
1178
|
+
requestedTab = undefined;
|
|
1179
|
+
return wanted ?? 'feed';
|
|
1180
|
+
});
|
|
1140
1181
|
const [selected, setSelected] = useState(undefined);
|
|
1141
1182
|
const [nonce, setNonce] = useState(0);
|
|
1142
1183
|
const tabs = useMemo(
|
|
@@ -1180,6 +1221,10 @@ window.__ModuleLoader__.load({
|
|
|
1180
1221
|
}
|
|
1181
1222
|
|
|
1182
1223
|
function PanelIcon({ size, active }) {
|
|
1224
|
+
// Three stacked sheets: the product is a layered index, and the glyph has to read at
|
|
1225
|
+
// the same optical weight as the shipped icons beside it. The previous drawing lived
|
|
1226
|
+
// in 4–20 of the viewBox with a small off-centre dot, which made it look thinner and
|
|
1227
|
+
// smaller than its neighbours in the sidebar.
|
|
1183
1228
|
return h(
|
|
1184
1229
|
'svg',
|
|
1185
1230
|
{
|
|
@@ -1188,10 +1233,28 @@ window.__ModuleLoader__.load({
|
|
|
1188
1233
|
viewBox: '0 0 24 24',
|
|
1189
1234
|
fill: 'none',
|
|
1190
1235
|
'aria-hidden': true,
|
|
1191
|
-
style: { opacity: active ? 1 : 0.
|
|
1236
|
+
style: { opacity: active ? 1 : 0.72, display: 'block' },
|
|
1192
1237
|
},
|
|
1193
|
-
h('path', {
|
|
1194
|
-
|
|
1238
|
+
h('path', {
|
|
1239
|
+
d: 'M12 3 21 7.5 12 12 3 7.5 12 3Z',
|
|
1240
|
+
stroke: 'currentColor',
|
|
1241
|
+
strokeWidth: 1.8,
|
|
1242
|
+
strokeLinejoin: 'round',
|
|
1243
|
+
}),
|
|
1244
|
+
h('path', {
|
|
1245
|
+
d: 'M3.6 12.2 12 16.4l8.4-4.2',
|
|
1246
|
+
stroke: 'currentColor',
|
|
1247
|
+
strokeWidth: 1.8,
|
|
1248
|
+
strokeLinecap: 'round',
|
|
1249
|
+
strokeLinejoin: 'round',
|
|
1250
|
+
}),
|
|
1251
|
+
h('path', {
|
|
1252
|
+
d: 'M3.6 16.8 12 21l8.4-4.2',
|
|
1253
|
+
stroke: 'currentColor',
|
|
1254
|
+
strokeWidth: 1.8,
|
|
1255
|
+
strokeLinecap: 'round',
|
|
1256
|
+
strokeLinejoin: 'round',
|
|
1257
|
+
}),
|
|
1195
1258
|
);
|
|
1196
1259
|
}
|
|
1197
1260
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-loci",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Layered long-term memory for DeepSeek Harness: the prompt sees only a stable index, full text is one lookup away. Bi-temporal facts, strict scope isolation, verified-only injection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/host/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import { registerCommands } from './commands.js';
|
|
|
23
23
|
import { runDoctor } from './doctor.js';
|
|
24
24
|
|
|
25
25
|
/** Version stamped into the database and diagnostics. */
|
|
26
|
-
export const VERSION = '3.0
|
|
26
|
+
export const VERSION = '3.1.0';
|
|
27
27
|
|
|
28
28
|
/** Optional collaborators; without them the plugin still serves tools. */
|
|
29
29
|
export const inject = ['systemPrompt', 'tools'];
|