dsh-memoir 0.5.1 → 0.5.2
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.en.md +11 -4
- package/README.md +11 -4
- package/cordis.patch.yml +3 -0
- package/lib/autodistill.d.ts +23 -7
- package/lib/autodistill.js +44 -20
- package/lib/client.js.map +2 -2
- package/lib/index.d.ts +9 -0
- package/lib/index.js +17 -1
- package/lib/routes.d.ts +3 -0
- package/package.json +10 -10
package/README.en.md
CHANGED
|
@@ -81,9 +81,10 @@ need long-tail history? memoir_read (local relevance-ranked recall)
|
|
|
81
81
|
|
|
82
82
|
**Session Snapshot freezing semantics**: one session's injected text is built once and frozen (stable prompt prefix, maximizing prompt-prefix cache hits); the current session does not re-consume memory it just wrote, and a new session rebuilds and sees the latest memory. Since v0.4.2, when there is no unique session identity (session.id / agent.id), freezing is skipped — a cache miss beats wrongly reusing another session's snapshot.
|
|
83
83
|
|
|
84
|
-
## v0.5.
|
|
84
|
+
## v0.5.2 configurable auto-distill and rc2 compatibility
|
|
85
85
|
|
|
86
|
-
- The development and peer-dependency baseline is `@deepseek-ai/dsh-* 0.1.
|
|
86
|
+
- The development and peer-dependency baseline is `@deepseek-ai/dsh-* 0.1.1-rc.2`.
|
|
87
|
+
- Auto-distill now supports per-agent worked-turn intervals, time cooldowns, and tool-call thresholds; defaults `1 / 0 / 1` preserve prior behavior.
|
|
87
88
|
- Store format v3 migrates v2 entries without changing their `id`, content, or timestamp. The first mutation materializes `importance`, `pinned`, `status`, `supersedes`, and `tags`; startup reads do not rewrite old files.
|
|
88
89
|
- Retrieval defaults to `active`. Archived and superseded history is retained and can be inspected from the Web panel. Explicit `supersedes` marks its targets as superseded; history is never deleted automatically.
|
|
89
90
|
- Agents can use `memoir_update` to edit an entry's section, title, content, and lifecycle in place; the Web panel also supports editing, pinning, marking superseded, archiving, and restoring.
|
|
@@ -167,6 +168,9 @@ Add a `config` block on the plugin row in `cordis.patch.yml` (all optional; defa
|
|
|
167
168
|
enabled: true # master switch (tools, routes, prompt section)
|
|
168
169
|
announceToAgent: true # system-prompt announcement section
|
|
169
170
|
autoDistill: true # auto distill reminder after each worked turn
|
|
171
|
+
autoDistillEvery: 1 # remind at most once per N worked turns
|
|
172
|
+
autoDistillCooldownMin: 0 # require M minutes between successful reminders
|
|
173
|
+
autoDistillMinTools: 1 # triggering turn must contain at least K tool calls
|
|
170
174
|
hotMemoryTokens: 900 # Hot Memory target tokens
|
|
171
175
|
hotMemoryMaxTokens: 1200 # Hot Memory hard ceiling (never exceeded)
|
|
172
176
|
readDefaultLimit: 8 # memoir_read default result count
|
|
@@ -175,6 +179,8 @@ Add a `config` block on the plugin row in `cordis.patch.yml` (all optional; defa
|
|
|
175
179
|
queryCacheSize: 128 # ranked-query LRU cache size
|
|
176
180
|
```
|
|
177
181
|
|
|
182
|
+
The three auto-distill frequency conditions are combined with AND and isolated per agent. Idle, aborted, subagent, and prior-`memoir_record` turns do not advance the interval. A worked turn below `autoDistillMinTools` advances the interval but cannot trigger by itself. Cooldown changes only after a successful steer.
|
|
183
|
+
|
|
178
184
|
## Design Trade-offs
|
|
179
185
|
|
|
180
186
|
- **Bounded vs full injection**: v0.3 injected the full history into the prompt and it kept growing; v0.4+ injects only budgeted Hot Memory, with long-tail history recalled on demand. Token benchmarks below.
|
|
@@ -183,6 +189,7 @@ Add a `config` block on the plugin row in `cordis.patch.yml` (all optional; defa
|
|
|
183
189
|
- **Multi-process safety**: store record/remove runs inside a cross-process critical section on `~/.dsh/dsh-memoir.lock` (exclusive O_EXCL creation with timeout); the section force-reloads from disk before mutating, so two interleaved DSH processes lose no updates (v0.4.2).
|
|
184
190
|
- **Windows paths**: canonical keys are fully lowercased (`C:\A` / `c:\a\` / `C:/A` share one bucket) while display paths keep the original casing (v0.4.2).
|
|
185
191
|
- **GUI and Agent share one engine**: panel search and `memoir_read` use the same RetrievalEngine instead of separate filter logic (v0.4.2).
|
|
192
|
+
- **Auto-distill cadence**: the default still reminds after every worked turn; research-heavy sessions can combine interval, cooldown, and activity thresholds to reduce interruptions (v0.5.2).
|
|
186
193
|
|
|
187
194
|
## Use Cases
|
|
188
195
|
|
|
@@ -213,7 +220,7 @@ Each plugin has its own focus — pick per need; no "which is stronger" narrativ
|
|
|
213
220
|
pnpm install # install devDeps (typescript, esbuild, @deepseek-ai/* type packages)
|
|
214
221
|
pnpm run build # tsc builds the host + esbuild builds the client bundle
|
|
215
222
|
pnpm run typecheck # full type check (src + test)
|
|
216
|
-
pnpm test #
|
|
223
|
+
pnpm test # 147 tests: store (incl. multi-process lock) / snapshot / selector / retrieval / tools / routes / auto-distill / integration / client pure logic / bundle protocol & purity / release notes
|
|
217
224
|
npm run bench # benchmark (100/1k/10k/100k entries); results written to bench/report.md
|
|
218
225
|
```
|
|
219
226
|
|
|
@@ -247,7 +254,7 @@ Bug reports must include screenshot / log evidence, a smoke test, code reference
|
|
|
247
254
|
|
|
248
255
|
## Release
|
|
249
256
|
|
|
250
|
-
Current stable release: **v0.5.
|
|
257
|
+
Current stable release: **v0.5.2** (2026-08-22) · [GitHub Release](https://github.com/Qinling-Melon-Farmers/dsh-memoir/releases/tag/v0.5.2) · [npm](https://www.npmjs.com/package/dsh-memoir/v/0.5.2). Full history is in [CHANGELOG.md](./CHANGELOG.md).
|
|
251
258
|
|
|
252
259
|
Every version keeps Chinese and English release notes in sync. GitHub Releases show Chinese by default and place the English notes in a collapsible `English` section.
|
|
253
260
|
|
package/README.md
CHANGED
|
@@ -81,9 +81,10 @@ memoir_record 沉淀工作 / 教训 / 下一步
|
|
|
81
81
|
|
|
82
82
|
**Session Snapshot 冻结语义**:同一 session 的注入文本只构建一次并冻结(prompt 前缀稳定,最大化 prompt-prefix cache 命中);当前 session 不重新消费自己刚写的记忆,新 session 重建并看到最新记忆。v0.4.2 起,没有唯一会话身份(session.id / agent.id)时**不做冻结**——宁可 cache miss,不可跨 session 错复用旧快照。
|
|
83
83
|
|
|
84
|
-
## v0.5.
|
|
84
|
+
## v0.5.2 可调自动收尾与 rc2 兼容性
|
|
85
85
|
|
|
86
|
-
- 当前开发基线为 `@deepseek-ai/dsh-* 0.1.
|
|
86
|
+
- 当前开发基线为 `@deepseek-ai/dsh-* 0.1.1-rc.2`;peer dependency 与开发依赖已统一到 rc2。
|
|
87
|
+
- 自动收尾支持按 agent 配置 worked-turn 间隔、时间冷却和工具调用阈值;默认值 `1 / 0 / 1` 与旧版行为一致。
|
|
87
88
|
- 存储格式从 v2 迁移到 v3:旧条目保持原有 `id`、内容和时间,首次变更时补齐 `importance`、`pinned`、`status`、`supersedes` 与 `tags`;启动读取不会重写旧文件。
|
|
88
89
|
- 默认只召回 `active` 条目;归档和被替代条目保留在历史中,可在 Web 面板切换状态查看。显式 `supersedes` 会把目标条目标记为 `superseded`,不会自动删除历史。
|
|
89
90
|
- Agent 可用 `memoir_update` 原地编辑条目的分类、标题、正文和生命周期;Web 面板也支持编辑、置顶、标记过时、归档与恢复。
|
|
@@ -167,6 +168,9 @@ JSON 是 source of truth,Markdown 是 generated projection:面板、工具
|
|
|
167
168
|
enabled: true # 总开关(工具、路由、注入段)
|
|
168
169
|
announceToAgent: true # system prompt 公告段
|
|
169
170
|
autoDistill: true # 每轮有实际工作的回合结束自动提醒归纳
|
|
171
|
+
autoDistillEvery: 1 # 每 N 个 worked turn 最多提醒一次
|
|
172
|
+
autoDistillCooldownMin: 0 # 两次成功提醒之间至少间隔 M 分钟
|
|
173
|
+
autoDistillMinTools: 1 # 触发提醒的 turn 至少包含 K 次工具调用
|
|
170
174
|
hotMemoryTokens: 900 # Hot Memory 目标 token 数
|
|
171
175
|
hotMemoryMaxTokens: 1200 # Hot Memory 硬上限(永不超过)
|
|
172
176
|
readDefaultLimit: 8 # memoir_read 默认返回条数
|
|
@@ -175,6 +179,8 @@ JSON 是 source of truth,Markdown 是 generated projection:面板、工具
|
|
|
175
179
|
queryCacheSize: 128 # 排序查询的 LRU 缓存大小
|
|
176
180
|
```
|
|
177
181
|
|
|
182
|
+
三个 auto-distill 频率条件按 AND 关系判定并按 agent 隔离。idle、aborted、subagent、已调用 `memoir_record` 的 turn 不推进间隔计数;低于 `autoDistillMinTools` 的 worked turn 会推进间隔,但自身不能触发提醒。冷却只在 steer 成功后更新。
|
|
183
|
+
|
|
178
184
|
## Design Trade-offs
|
|
179
185
|
|
|
180
186
|
- **有界注入 vs 全量注入**:v0.3 把完整历史注入 prompt,越用越膨胀;v0.4+ 只注入预算内的 Hot Memory,长尾历史按需召回。token 基准见下方 Benchmark。
|
|
@@ -183,6 +189,7 @@ JSON 是 source of truth,Markdown 是 generated projection:面板、工具
|
|
|
183
189
|
- **多进程安全**:store 的 record/remove 走 `~/.dsh/dsh-memoir.lock` 跨进程临界区(O_EXCL 独占创建 + 超时),临界区内强制从磁盘重读再改,两个 DSH 进程交错写入不丢更新(v0.4.2)。
|
|
184
190
|
- **Windows 路径**:canonical key 全小写(`C:\A` / `c:\a\` / `C:/A` 一个桶),display path 保留原始大小写(v0.4.2)。
|
|
185
191
|
- **GUI 与 Agent 同源**:面板搜索与 `memoir_read` 共用 RetrievalEngine,不再各写一套过滤逻辑(v0.4.2)。
|
|
192
|
+
- **自动收尾节奏**:默认仍逐 worked turn 提醒;研究型会话可组合轮次间隔、冷却与活动阈值降低打扰(v0.5.2)。
|
|
186
193
|
|
|
187
194
|
## Use Cases
|
|
188
195
|
|
|
@@ -213,7 +220,7 @@ JSON 是 source of truth,Markdown 是 generated projection:面板、工具
|
|
|
213
220
|
pnpm install # 安装 devDeps(typescript、esbuild、@deepseek-ai/* 类型包)
|
|
214
221
|
pnpm run build # tsc 构建 host + esbuild 构建 client bundle
|
|
215
222
|
pnpm run typecheck # 全量类型检查(src + test)
|
|
216
|
-
pnpm test #
|
|
223
|
+
pnpm test # 147 项测试:store(含多进程锁) / snapshot / selector / retrieval / tools / routes / 自动收尾 / 集成 / client 纯逻辑 / bundle 协议与纯净性 / 发布说明
|
|
217
224
|
npm run bench # benchmark(100/1k/10k/100k 条目),结果写入 bench/report.md
|
|
218
225
|
```
|
|
219
226
|
|
|
@@ -248,7 +255,7 @@ PR 请先提 Issue 讨论。
|
|
|
248
255
|
|
|
249
256
|
## Release
|
|
250
257
|
|
|
251
|
-
当前稳定版:**v0.5.
|
|
258
|
+
当前稳定版:**v0.5.2**(2026-08-22) · [GitHub Release](https://github.com/Qinling-Melon-Farmers/dsh-memoir/releases/tag/v0.5.2) · [npm](https://www.npmjs.com/package/dsh-memoir/v/0.5.2)。完整历史见 [CHANGELOG.md](./CHANGELOG.md)。
|
|
252
259
|
|
|
253
260
|
每个版本的更新日志均同步维护中英文;GitHub Release 默认展开中文,英文说明收纳在可折叠的 `English` 区域。
|
|
254
261
|
|
package/cordis.patch.yml
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
# enabled: true # master switch (tools, routes, prompt section)
|
|
17
17
|
# announceToAgent: true # system-prompt announcement
|
|
18
18
|
# autoDistill: true # steer a distill prompt after each worked turn
|
|
19
|
+
# autoDistillEvery: 1 # remind at most once per N worked turns
|
|
20
|
+
# autoDistillCooldownMin: 0 # minimum minutes between successful reminders
|
|
21
|
+
# autoDistillMinTools: 1 # minimum tool calls on the triggering turn
|
|
19
22
|
- insert:
|
|
20
23
|
- id: memoir
|
|
21
24
|
name: dsh-memoir
|
package/lib/autodistill.d.ts
CHANGED
|
@@ -19,11 +19,13 @@ export interface TurnEventLike {
|
|
|
19
19
|
type: string;
|
|
20
20
|
data?: unknown;
|
|
21
21
|
}
|
|
22
|
-
|
|
23
|
-
export declare function turnActivity(events: readonly TurnEventLike[], turn: number): {
|
|
22
|
+
export interface TurnActivity {
|
|
24
23
|
worked: boolean;
|
|
25
24
|
recorded: boolean;
|
|
26
|
-
|
|
25
|
+
toolCalls: number;
|
|
26
|
+
}
|
|
27
|
+
/** Scan the tail of a session log for one turn's tool activity. */
|
|
28
|
+
export declare function turnActivity(events: readonly TurnEventLike[], turn: number): TurnActivity;
|
|
27
29
|
/** The agent surface the turn-stopping listener needs. */
|
|
28
30
|
export interface AutoDistillAgentLike {
|
|
29
31
|
id: string;
|
|
@@ -38,11 +40,21 @@ export interface AutoDistillAgentLike {
|
|
|
38
40
|
}
|
|
39
41
|
/** Subagent sessions (and any nested delegation) never get distilled. */
|
|
40
42
|
export declare function isSubagentSession(agent: AutoDistillAgentLike): boolean;
|
|
41
|
-
|
|
43
|
+
export interface AutoDistillPolicy {
|
|
44
|
+
every: number;
|
|
45
|
+
cooldownMs: number;
|
|
46
|
+
minTools: number;
|
|
47
|
+
}
|
|
48
|
+
/** Per-agent frequency, cooldown, and duplicate-turn state (with pruning). */
|
|
42
49
|
export declare class AutoDistillGate {
|
|
43
|
-
private
|
|
44
|
-
/**
|
|
45
|
-
|
|
50
|
+
private states;
|
|
51
|
+
/**
|
|
52
|
+
* Consume one eligible worked turn and decide whether all policy conditions
|
|
53
|
+
* are ready. Duplicate events never advance the worked-turn counter.
|
|
54
|
+
*/
|
|
55
|
+
consume(agentId: string, turn: number, toolCalls: number, policy: AutoDistillPolicy, now: number): boolean;
|
|
56
|
+
/** Record a successful steer; failed steer attempts do not start cooldown. */
|
|
57
|
+
recordSteer(agentId: string, now: number): void;
|
|
46
58
|
/** Drop all state for one agent (disposal hygiene). */
|
|
47
59
|
forget(agentId: string): void;
|
|
48
60
|
}
|
|
@@ -62,4 +74,8 @@ export interface AutoDistillWire {
|
|
|
62
74
|
*/
|
|
63
75
|
export declare function installAutoDistill(wire: AutoDistillWire, options: {
|
|
64
76
|
enabled: () => boolean;
|
|
77
|
+
every?: number;
|
|
78
|
+
cooldownMin?: number;
|
|
79
|
+
minTools?: number;
|
|
80
|
+
now?: () => number;
|
|
65
81
|
}): () => void;
|
package/lib/autodistill.js
CHANGED
|
@@ -19,8 +19,8 @@ export const DISTILL_PROMPT = '(dsh-memoir 自动收尾)本轮工作已结
|
|
|
19
19
|
export const AUTO_DISTILL_PLUGIN = 'dsh-memoir';
|
|
20
20
|
/** Scan the tail of a session log for one turn's tool activity. */
|
|
21
21
|
export function turnActivity(events, turn) {
|
|
22
|
-
let worked = false;
|
|
23
22
|
let recorded = false;
|
|
23
|
+
let toolCalls = 0;
|
|
24
24
|
for (let i = events.length - 1; i >= 0; i--) {
|
|
25
25
|
const event = events[i];
|
|
26
26
|
const data = event.data;
|
|
@@ -31,39 +31,54 @@ export function turnActivity(events, turn) {
|
|
|
31
31
|
if (data.turn !== turn)
|
|
32
32
|
continue;
|
|
33
33
|
if (event.type === 'tool/call') {
|
|
34
|
-
|
|
34
|
+
toolCalls += 1;
|
|
35
35
|
if (data.name === 'memoir_record')
|
|
36
36
|
recorded = true;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
return { worked, recorded };
|
|
39
|
+
return { worked: toolCalls > 0, recorded, toolCalls };
|
|
40
40
|
}
|
|
41
41
|
/** Subagent sessions (and any nested delegation) never get distilled. */
|
|
42
42
|
export function isSubagentSession(agent) {
|
|
43
43
|
return agent.session.header.origin === 'subagent' || (agent.session.header.delegationDepth ?? 0) > 0;
|
|
44
44
|
}
|
|
45
|
-
/** Per-agent
|
|
45
|
+
/** Per-agent frequency, cooldown, and duplicate-turn state (with pruning). */
|
|
46
46
|
export class AutoDistillGate {
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
states = new Map();
|
|
48
|
+
/**
|
|
49
|
+
* Consume one eligible worked turn and decide whether all policy conditions
|
|
50
|
+
* are ready. Duplicate events never advance the worked-turn counter.
|
|
51
|
+
*/
|
|
52
|
+
consume(agentId, turn, toolCalls, policy, now) {
|
|
53
|
+
let state = this.states.get(agentId);
|
|
54
|
+
if (state === undefined) {
|
|
55
|
+
state = { processedTurns: new Set(), workedSinceSteer: 0 };
|
|
56
|
+
this.states.set(agentId, state);
|
|
54
57
|
}
|
|
55
|
-
if (
|
|
58
|
+
if (state.processedTurns.has(turn))
|
|
56
59
|
return false;
|
|
57
|
-
|
|
58
|
-
for (const
|
|
59
|
-
if (
|
|
60
|
-
|
|
60
|
+
state.processedTurns.add(turn);
|
|
61
|
+
for (const value of [...state.processedTurns]) {
|
|
62
|
+
if (value < turn - 100)
|
|
63
|
+
state.processedTurns.delete(value);
|
|
61
64
|
}
|
|
62
|
-
|
|
65
|
+
state.workedSinceSteer += 1;
|
|
66
|
+
const intervalReady = state.workedSinceSteer >= policy.every;
|
|
67
|
+
const activityReady = toolCalls >= policy.minTools;
|
|
68
|
+
const cooldownReady = state.lastSteeredAt === undefined || now - state.lastSteeredAt >= policy.cooldownMs;
|
|
69
|
+
return intervalReady && activityReady && cooldownReady;
|
|
70
|
+
}
|
|
71
|
+
/** Record a successful steer; failed steer attempts do not start cooldown. */
|
|
72
|
+
recordSteer(agentId, now) {
|
|
73
|
+
const state = this.states.get(agentId);
|
|
74
|
+
if (state === undefined)
|
|
75
|
+
return;
|
|
76
|
+
state.workedSinceSteer = 0;
|
|
77
|
+
state.lastSteeredAt = now;
|
|
63
78
|
}
|
|
64
79
|
/** Drop all state for one agent (disposal hygiene). */
|
|
65
80
|
forget(agentId) {
|
|
66
|
-
this.
|
|
81
|
+
this.states.delete(agentId);
|
|
67
82
|
}
|
|
68
83
|
}
|
|
69
84
|
/**
|
|
@@ -73,6 +88,13 @@ export class AutoDistillGate {
|
|
|
73
88
|
*/
|
|
74
89
|
export function installAutoDistill(wire, options) {
|
|
75
90
|
const gate = new AutoDistillGate();
|
|
91
|
+
const integerAtLeast = (value, fallback, minimum) => typeof value === 'number' && Number.isFinite(value) ? Math.max(minimum, Math.floor(value)) : fallback;
|
|
92
|
+
const numberAtLeast = (value, fallback, minimum) => typeof value === 'number' && Number.isFinite(value) ? Math.max(minimum, value) : fallback;
|
|
93
|
+
const policy = {
|
|
94
|
+
every: integerAtLeast(options.every, 1, 1),
|
|
95
|
+
cooldownMs: numberAtLeast(options.cooldownMin, 0, 0) * 60_000,
|
|
96
|
+
minTools: integerAtLeast(options.minTools, 1, 1),
|
|
97
|
+
};
|
|
76
98
|
return wire.on('agent/turn-stopping', (payload) => {
|
|
77
99
|
if (!options.enabled())
|
|
78
100
|
return;
|
|
@@ -81,14 +103,16 @@ export function installAutoDistill(wire, options) {
|
|
|
81
103
|
return;
|
|
82
104
|
if (signal.aborted)
|
|
83
105
|
return;
|
|
84
|
-
const { worked, recorded } = turnActivity(agent.session.events, turn);
|
|
106
|
+
const { worked, recorded, toolCalls } = turnActivity(agent.session.events, turn);
|
|
85
107
|
if (!worked || recorded)
|
|
86
108
|
return;
|
|
87
|
-
|
|
109
|
+
const now = options.now?.() ?? Date.now();
|
|
110
|
+
if (!gate.consume(agent.id, turn, toolCalls, policy, now))
|
|
88
111
|
return;
|
|
89
112
|
agent.steer(createUserMessage({
|
|
90
113
|
content: [{ type: 'text', text: DISTILL_PROMPT }],
|
|
91
114
|
source: { kind: 'plugin', plugin: AUTO_DISTILL_PLUGIN },
|
|
92
115
|
}));
|
|
116
|
+
gate.recordSteer(agent.id, now);
|
|
93
117
|
});
|
|
94
118
|
}
|
package/lib/client.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/client/index.tsx", "../src/client/controller.ts", "../src/client/api.ts", "../src/client/cwd.ts", "../src/client/i18n.ts", "../src/client/styles.ts", "../src/client/sidebar-entry.ts", "../src/client/mount.tsx", "../src/client/panel.tsx"],
|
|
4
|
-
"sourcesContent": ["/**\n * Browser-half entry for the dsh-memoir plugin \u2014 runs inside the dsh web GUI.\n * Mounts the sidebar entry row (toggles the panel) and the memoir panel in\n * the center column, bound to the active session's workspace. Failure policy:\n * DOM mounting problems are logged, never thrown \u2014 the web shell fails the\n * whole boot when a plugin apply throws, and an external plugin must not take\n * the GUI down.\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport { PanelController } from './controller.js'\nimport { MemoirApi } from './api.js'\nimport { createCwdTracker } from './cwd.js'\nimport { makeT } from './i18n.js'\nimport { PANEL_CSS } from './styles.js'\nimport { mountSidebarEntry } from './sidebar-entry.js'\nimport { mountPanel } from './mount.jsx'\n\n/** Required services (fiber inject waiting \u2014 the runtime must be up first). */\nexport const inject = ['sessions']\n\n/** Inject the panel stylesheet once (the loader removes plugin-owned tags on unload). */\nfunction injectStyles(): void {\n if (document.querySelector('style[data-plugin=\"dsh-memoir\"]') !== null) return\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-memoir'\n tag.textContent = PANEL_CSS\n document.head.appendChild(tag)\n}\n\n/**\n * Mount the memoir panel.\n * @param ctx - client root context (sessions service).\n */\nexport function apply(ctx: ClientContext): void {\n const t = makeT(document)\n const disposers: Array<() => void> = []\n try {\n const controller = new PanelController()\n const api = new MemoirApi()\n const cwdTracker = createCwdTracker(ctx.sessions)\n injectStyles()\n disposers.push(mountSidebarEntry(controller, t))\n disposers.push(mountPanel(controller, api, cwdTracker, t))\n } catch (error) {\n // DOM failures degrade the panel, never the GUI.\n console.warn('[dsh-memoir] mount failed:', error)\n }\n ctx.effect(() => () => {\n for (const dispose of disposers.splice(0)) dispose()\n }, 'dsh-memoir: ui mounts')\n}\n", "/**\n * Panel open/close state machine \u2014 pure, no DOM, unit-testable. Mirrors the\n * sibling panel controllers: a snapshot + subscribe contract drives the\n * sidebar entry highlight and the panel visibility attributes.\n */\n\nexport interface PanelSnapshot {\n panelOpen: boolean\n}\n\nexport class PanelController {\n private state: PanelSnapshot = { panelOpen: false }\n private listeners = new Set<() => void>()\n\n /** The current immutable snapshot. */\n getSnapshot(): PanelSnapshot {\n return this.state\n }\n\n /** Subscribe to state changes; returns an unsubscribe function. */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n open(): void {\n this.patch({ panelOpen: true })\n }\n\n close(): void {\n this.patch({ panelOpen: false })\n }\n\n toggle(): void {\n this.patch({ panelOpen: !this.state.panelOpen })\n }\n\n /** Replace the snapshot; notifies only when the observable state changed. */\n private patch(next: Partial<PanelSnapshot>): void {\n const prev = this.state\n this.state = { ...prev, ...next }\n if (this.state.panelOpen === prev.panelOpen) return\n for (const listener of [...this.listeners]) listener()\n }\n}\n", "/**\n * Browser API client for the host /api/dsh-memoir routes. The only data path\n * the panel uses \u2014 same-origin fetch, JSON envelope { ok, value | error }.\n * The fetch implementation is injectable for tests.\n */\n\nimport type { MemoirStatus, SectionKey } from './types.ts'\n\n/** Error carrying the route's JSON error message. */\nexport class MemoirApiError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MemoirApiError'\n }\n}\n\ninterface EnvelopeResponse {\n status: number\n ok: boolean\n json(): Promise<unknown>\n}\n\n/** Parse the envelope or throw a MemoirApiError (transport or route error). */\nexport async function readEnvelope(response: EnvelopeResponse): Promise<unknown> {\n let body: unknown\n try {\n body = await response.json()\n } catch {\n throw new MemoirApiError(`HTTP ${response.status}: invalid JSON response`)\n }\n if (typeof body !== 'object' || body === null || (body as { ok?: boolean }).ok !== true) {\n const record = body as { error?: { message?: string } } | null\n const message = record && typeof record.error === 'object' && record.error !== null && typeof record.error.message === 'string'\n ? record.error.message\n : `HTTP ${response.status}`\n throw new MemoirApiError(message)\n }\n return (body as { value: unknown }).value\n}\n\n/** Query-string helper (omits undefined/empty values). */\nexport function query(params: Record<string, string | undefined>): string {\n const search = new URLSearchParams()\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== '') search.set(key, String(value))\n }\n const text = search.toString()\n return text === '' ? '' : '?' + text\n}\n\n/** One memory entry as the panel sees it over the wire. */\nexport interface WireEntry {\n id: string\n section: SectionKey\n title?: string\n content: string\n time: number\n sessionId?: string\n importance?: number\n pinned?: boolean\n status?: MemoirStatus\n supersedes?: string[]\n tags?: string[]\n}\n\nexport interface WireProject {\n key: string\n path: string\n title: string\n updatedAt: number\n entries: WireEntry[]\n}\n\n/** The host /diagnostics payload (v0.4 observability, v0.4.2 extended). */\nexport interface WireDiagnostics {\n storeRevision: number\n snapshotEpoch: number\n cache: {\n revision: number\n epoch: number\n loads: number\n hits: number\n misses: number\n hitRate: number\n fileReads: number\n statProbes: number\n corruptBackups: number\n renders: number\n renderComputes: number\n renderHitRate: number\n lastLoadMs?: number\n }\n snapshotCount: number\n snapshotMax: number\n hotMemory: { selected: number; total: number; estimatedTokens: number } | null\n /** v0.4.2: retrieval index / query cache / last query. */\n retrieval: {\n index: { docs: number; terms: number; epoch: number } | null\n cache: { hits: number; misses: number; evictions: number; hitRate: number; size: number; capacity: number }\n lastQuery: { query: string; latencyMs: number; candidates: number; returned: number; at: number } | null\n }\n /** v0.4.2: the most recently frozen session snapshot. */\n snapshot: { hash: string; createdAt: number; storeRevision: number } | null\n config: {\n hotMemoryTokens: number\n hotMemoryMaxTokens: number\n readDefaultLimit: number\n readMaxLimit: number\n sessionSnapshotMax: number\n queryCacheSize: number\n }\n}\n\n/** One ranked /search hit (v0.4.2: shared RetrievalEngine order). */\nexport interface WireSearchResult {\n projectPath: string\n entry: WireEntry\n score: number\n}\n\n/** The /hot-memory inspector payload (v0.4.2). */\nexport interface WireHotMemory {\n text: string\n selected: WireEntry[]\n total: number\n estimatedTokens: number\n}\n\nexport interface RecordPayload {\n path: string\n section: SectionKey\n title?: string\n content: string\n importance?: number\n pinned?: boolean\n supersedes?: string[]\n tags?: string[]\n}\n\ntype FetchLike = (input: string, init?: RequestInit) => Promise<EnvelopeResponse>\n\n/** The browser half's only data entry point. */\nexport class MemoirApi {\n private readonly fetchImpl: FetchLike\n\n /**\n * @param fetchImpl - injectable fetch (defaults to globalThis.fetch).\n */\n constructor(fetchImpl?: FetchLike) {\n this.fetchImpl = fetchImpl ?? ((...args) => globalThis.fetch(...args) as Promise<EnvelopeResponse>)\n }\n\n /** Read one project's memory (empty project shape when unknown). */\n async project(path: string, options: { section?: SectionKey; query?: string; status?: MemoirStatus | 'all' } = {}): Promise<{ project: WireProject }> {\n const response = await this.fetchImpl('/api/dsh-memoir/project' + query({ path, ...options }))\n return readEnvelope(response) as Promise<{ project: WireProject }>\n }\n\n /** Read the cross-project global index. */\n async global(options: { section?: SectionKey; query?: string; status?: MemoirStatus | 'all' } = {}): Promise<{ projects: WireProject[] }> {\n const response = await this.fetchImpl('/api/dsh-memoir/global' + query({ ...options }))\n return readEnvelope(response) as Promise<{ projects: WireProject[] }>\n }\n\n /** Read runtime diagnostics (cache hit rates, snapshot/hot-memory stats). */\n async diagnostics(path?: string): Promise<WireDiagnostics> {\n const response = await this.fetchImpl('/api/dsh-memoir/diagnostics' + query({ path }))\n return readEnvelope(response) as Promise<WireDiagnostics>\n }\n\n /**\n * Ranked search over the host RetrievalEngine (v0.4.2) \u2014 the same ranking\n * memoir_read uses, so the GUI search and the agent recall never diverge.\n */\n async search(options: { scope: 'project' | 'global' | 'all'; path?: string; section?: SectionKey; query: string; limit?: number; status?: MemoirStatus | 'all' }): Promise<{ results: WireSearchResult[] }> {\n const response = await this.fetchImpl('/api/dsh-memoir/search' + query({\n scope: options.scope,\n path: options.path,\n section: options.section,\n query: options.query,\n limit: options.limit === undefined ? undefined : String(options.limit),\n status: options.status,\n }))\n return readEnvelope(response) as Promise<{ results: WireSearchResult[] }>\n }\n\n /** Hot-memory preview for one workspace (the inspector). */\n async hotMemory(path: string): Promise<{ hotMemory: WireHotMemory | null }> {\n const response = await this.fetchImpl('/api/dsh-memoir/hot-memory' + query({ path }))\n return readEnvelope(response) as Promise<{ hotMemory: WireHotMemory | null }>\n }\n\n /** Record one entry (host regenerates PROJECT_MEMORY.md). */\n async record(payload: RecordPayload): Promise<{ entry: WireEntry }> {\n const response = await this.fetchImpl('/api/dsh-memoir/entries', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n return readEnvelope(response) as Promise<{ entry: WireEntry }>\n }\n\n /** Delete one entry by id. */\n async remove(payload: { path: string; id: string }): Promise<{ removed: boolean }> {\n const response = await this.fetchImpl('/api/dsh-memoir/entries', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n return readEnvelope(response) as Promise<{ removed: boolean }>\n }\n\n /** Update entry content or lifecycle metadata without deleting. */\n async update(payload: { path: string; id: string; section?: SectionKey; title?: string | null; content?: string; importance?: number; pinned?: boolean; status?: MemoirStatus; supersedes?: string[]; tags?: string[] }): Promise<{ entry: WireEntry | null; updated: boolean }> {\n const response = await this.fetchImpl('/api/dsh-memoir/entries', {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n return readEnvelope(response) as Promise<{ entry: WireEntry | null; updated: boolean }>\n }\n}\n", "/**\n * Workspace tracker: projects the client sessions service's active session\n * cwd into a useSyncExternalStore-compatible subscription. Pure logic \u2014 the\n * sessions service is injected, unit-testable with a mock.\n */\n\nexport interface SessionRecordLike {\n cwd?: string\n}\n\nexport interface SessionsLike {\n list: {\n getSnapshot(): { current: string | undefined; byId: Record<string, SessionRecordLike> }\n subscribe(listener: () => void): () => void\n }\n}\n\n/** Read the active session's cwd from a sessions service snapshot ('' when none). */\nexport function readCwd(sessions: SessionsLike): string {\n const snapshot = sessions.list.getSnapshot()\n const id = snapshot.current\n const cwd = id === undefined ? undefined : snapshot.byId[id]?.cwd\n return typeof cwd === 'string' && cwd !== '' ? cwd : ''\n}\n\n/** A useSyncExternalStore-compatible tracker over the sessions service. */\nexport interface CwdTracker {\n getSnapshot(): string\n subscribe(listener: () => void): () => void\n}\n\n/**\n * Build a { getSnapshot, subscribe } tracker over the sessions service.\n * @param sessions - the client sessions service (with `list.getSnapshot` and\n * `list.subscribe`).\n */\nexport function createCwdTracker(sessions: SessionsLike): CwdTracker {\n let current = readCwd(sessions)\n const listeners = new Set<() => void>()\n const tracker: CwdTracker = {\n getSnapshot: () => current,\n subscribe: (listener) => {\n listeners.add(listener)\n const dispose = sessions.list.subscribe(() => {\n const next = readCwd(sessions)\n if (next === current) return\n current = next\n for (const l of [...listeners]) l()\n })\n return () => {\n listeners.delete(listener)\n dispose()\n }\n },\n }\n return tracker\n}\n", "/**\n * Bilingual surface copy (zh-first). Pure \u2014 no DOM, unit-testable.\n * The shell owns <html lang>; the panel reads it at render time.\n */\n\nexport type Lang = 'zh' | 'en'\n\nexport const dictionaries: Record<Lang, Record<string, string>> = {\n zh: {\n 'entry.label': '\u8BB0\u5FC6',\n 'entry.tooltip': '\u6253\u5F00\u8BB0\u5FC6\u9762\u677F\uFF08\u9879\u76EE\u8BB0\u5FC6 / \u5168\u5C40\u8BB0\u5FC6\uFF09',\n 'panel.title': '\u8BB0\u5FC6 Memoir',\n 'panel.close': '\u5173\u95ED',\n 'panel.refresh': '\u5237\u65B0',\n 'tab.project': '\u9879\u76EE\u8BB0\u5FC6',\n 'tab.global': '\u5168\u5C40\u8BB0\u5FC6',\n 'toolbar.search': '\u641C\u7D22\u6807\u9898\u4E0E\u6B63\u6587\u2026',\n 'toolbar.add': '\u6DFB\u52A0\u8BB0\u5FC6',\n 'toolbar.cancel': '\u53D6\u6D88',\n 'form.section': '\u5206\u7C7B',\n 'form.title': '\u6807\u9898\uFF08\u53EF\u9009\uFF09',\n 'form.content': '\u5185\u5BB9',\n 'form.submit': '\u8BB0\u5F55',\n 'form.placeholder.title': '\u4E00\u53E5\u8BDD\u6807\u9898\uFF0C\u5982\u300C\u4FEE\u590D pet \u60AC\u505C\u95EA\u9000\u300D',\n 'form.placeholder.content': '\u5177\u4F53\u505A\u4E86\u4EC0\u4E48\u3001\u7ED3\u8BBA\u3001\u6559\u8BAD\u6216\u4E0B\u4E00\u6B65\u600E\u4E48\u505A\u3002\u5EFA\u8BAE\u7CBE\u70BC\u3001\u53EF\u6267\u884C\u3002',\n 'empty.project': '\u672C\u9879\u76EE\u6682\u65E0\u6301\u4E45\u8BB0\u5FC6',\n 'empty.projectHint': '\u8BA9 agent \u7528 memoir_record \u6C89\u6DC0\u7ECF\u9A8C\uFF0C\u6216\u70B9\u300C\u6DFB\u52A0\u8BB0\u5FC6\u300D\u624B\u52A8\u8BB0\u5F55\u3002',\n 'empty.workspace': '\u672A\u6253\u5F00\u9879\u76EE\u4F1A\u8BDD',\n 'empty.workspaceHint': '\u6253\u5F00\u4E00\u4E2A\u9879\u76EE\u4F1A\u8BDD\u540E\uFF0C\u8FD9\u91CC\u663E\u793A\u8BE5\u9879\u76EE\u7684\u6301\u4E45\u8BB0\u5FC6\u3002',\n 'empty.global': '\u5168\u5C40\u7D22\u5F15\u4E3A\u7A7A',\n 'empty.globalHint': '\u5728\u4EFB\u4F55\u9879\u76EE\u91CC\u7528 memoir_record \u8BB0\u5F55\u540E\uFF0C\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u4F9B\u8DE8\u9879\u76EE\u68C0\u7D22\u3002',\n 'empty.search': '\u6CA1\u6709\u5339\u914D\u7684\u6761\u76EE',\n 'load.failed': '\u52A0\u8F7D\u5931\u8D25',\n 'record.failed': '\u8BB0\u5F55\u5931\u8D25',\n 'update.failed': '\u66F4\u65B0\u5931\u8D25',\n 'delete.failed': '\u5220\u9664\u5931\u8D25',\n 'delete.confirm': '\u5220\u9664\u8FD9\u6761\u8BB0\u5FC6\uFF1F',\n 'updated': '\u66F4\u65B0\u4E8E',\n 'entries': '\u6761',\n 'session': '\u4F1A\u8BDD',\n 'sections.work': '\u5DE5\u4F5C\u8BB0\u5F55',\n 'sections.lessons': '\u7ECF\u9A8C\u6559\u8BAD',\n 'sections.actions': '\u884C\u52A8\u6307\u5357',\n 'sections.note': '\u5907\u6CE8',\n 'diag.title': 'Memory Diagnostics',\n 'diag.revision': 'Store revision',\n 'diag.snapshot': 'Session snapshots',\n 'diag.cache': 'Store cache',\n 'diag.render': 'Render cache',\n 'diag.hot': 'Hot memory',\n 'diag.retrieval': 'Retrieval \u7D22\u5F15',\n 'diag.qcache': 'Query cache',\n 'diag.lastQuery': '\u6700\u8FD1\u67E5\u8BE2',\n 'diag.snapshotInfo': '\u4F1A\u8BDD\u5FEB\u7167',\n 'inspector.title': 'Hot Memory \u9884\u89C8',\n 'inspector.empty': '\u672C\u5DE5\u4F5C\u533A\u6682\u65E0 Hot Memory',\n 'search.ranked': '\u6309\u76F8\u5173\u6027\u6392\u5E8F',\n 'filter.status': '\u72B6\u6001',\n 'status.active': '\u6D3B\u8DC3',\n 'status.superseded': '\u5DF2\u88AB\u66FF\u4EE3',\n 'status.archived': '\u5DF2\u5F52\u6863',\n 'lifecycle.pin': '\u7F6E\u9876',\n 'lifecycle.unpin': '\u53D6\u6D88\u7F6E\u9876',\n 'lifecycle.edit': '\u7F16\u8F91',\n 'lifecycle.save': '\u4FDD\u5B58\u66F4\u65B0',\n 'lifecycle.supersede': '\u6807\u8BB0\u5DF2\u66FF\u4EE3',\n 'lifecycle.unsupersede': '\u6062\u590D\u6D3B\u8DC3',\n 'lifecycle.archive': '\u5F52\u6863',\n 'lifecycle.restore': '\u6062\u590D',\n },\n en: {\n 'entry.label': 'Memory',\n 'entry.tooltip': 'Open the memory panel (project / global)',\n 'panel.title': 'Memory Memoir',\n 'panel.close': 'Close',\n 'panel.refresh': 'Refresh',\n 'tab.project': 'Project',\n 'tab.global': 'Global',\n 'toolbar.search': 'Search titles and content\u2026',\n 'toolbar.add': 'Add entry',\n 'toolbar.cancel': 'Cancel',\n 'form.section': 'Section',\n 'form.title': 'Title (optional)',\n 'form.content': 'Content',\n 'form.submit': 'Record',\n 'form.placeholder.title': 'A one-line title, e.g. \"Fix pet hover crash\"',\n 'form.placeholder.content': 'What was done, the conclusion, the lesson, or the next step. Concise and actionable.',\n 'empty.project': 'No persistent memory in this project yet',\n 'empty.projectHint': 'Ask the agent to distill via memoir_record, or add an entry manually.',\n 'empty.workspace': 'No project session open',\n 'empty.workspaceHint': 'Open a project session to see its persistent memory here.',\n 'empty.global': 'The global index is empty',\n 'empty.globalHint': 'Entries recorded via memoir_record in any project appear here for cross-project search.',\n 'empty.search': 'No matching entries',\n 'load.failed': 'Failed to load',\n 'record.failed': 'Failed to record',\n 'update.failed': 'Failed to update',\n 'delete.failed': 'Failed to delete',\n 'delete.confirm': 'Delete this entry?',\n 'updated': 'Updated',\n 'entries': 'entries',\n 'session': 'session',\n 'sections.work': 'Work Log',\n 'sections.lessons': 'Lessons Learned',\n 'sections.actions': 'Action Guide',\n 'sections.note': 'Notes',\n 'diag.title': 'Memory Diagnostics',\n 'diag.revision': 'Store revision',\n 'diag.snapshot': 'Session snapshots',\n 'diag.cache': 'Store cache',\n 'diag.render': 'Render cache',\n 'diag.hot': 'Hot memory',\n 'diag.retrieval': 'Retrieval index',\n 'diag.qcache': 'Query cache',\n 'diag.lastQuery': 'Last query',\n 'diag.snapshotInfo': 'Session snapshot',\n 'inspector.title': 'Hot Memory Inspector',\n 'inspector.empty': 'No hot memory in this workspace yet',\n 'search.ranked': 'Ranked by relevance',\n 'filter.status': 'Status',\n 'status.active': 'Active',\n 'status.superseded': 'Superseded',\n 'status.archived': 'Archived',\n 'lifecycle.pin': 'Pin',\n 'lifecycle.unpin': 'Unpin',\n 'lifecycle.edit': 'Edit',\n 'lifecycle.save': 'Save update',\n 'lifecycle.supersede': 'Mark superseded',\n 'lifecycle.unsupersede': 'Restore active',\n 'lifecycle.archive': 'Archive',\n 'lifecycle.restore': 'Restore',\n },\n}\n\n/** Section keys in canonical display order. */\nexport const SECTION_KEYS: string[] = ['work', 'lessons', 'actions', 'note']\n\n/** Minimal document surface for language detection. */\nexport interface DocumentLike {\n documentElement?: { lang?: string }\n}\n\n/** Current UI language: zh when the shell's <html lang> starts with zh. */\nexport function detectLanguage(documentLike: DocumentLike | undefined): Lang {\n const lang = documentLike?.documentElement?.lang\n return typeof lang === 'string' && lang.startsWith('zh') ? 'zh' : 'en'\n}\n\n/** Pure translation lookup: `translate(lang, key)` with en fallback. */\nexport function translate(lang: string, key: string): string {\n const dict = dictionaries[lang as Lang] ?? dictionaries.en\n return Object.prototype.hasOwnProperty.call(dict, key) ? dict[key] : (dictionaries.en[key] ?? key)\n}\n\n/** Bound translator reading the live <html lang> at each call. */\nexport function makeT(documentLike: DocumentLike): (key: string) => string {\n return (key) => translate(detectLanguage(documentLike), key)\n}\n", "/**\n * Panel stylesheet (plain string, injected as <style data-plugin=\"dsh-memoir\">).\n * Class names are literal memoir-* prefixes (no CSS-module hashing) so the\n * plain-DOM sidebar entry and the React panel share them. Dark mode follows\n * the shell's body[data-ds-dark-theme] marker via CSS only.\n */\n\nexport const PANEL_CSS = `\n[data-dsh-memoir-view] {\n display: none;\n position: absolute;\n inset: 0;\n z-index: 20;\n background: var(--bg-panel, #ffffff);\n color: var(--text-primary, #1f2328);\n font-size: 13px;\n}\nhtml[data-dsh-memoir-active] [data-dsh-memoir-view] {\n display: flex;\n flex-direction: column;\n}\nhtml[data-dsh-memoir-active] [data-pane=\"conversation\"] > *:not([data-dsh-memoir-view]) {\n display: none;\n}\n/* Current shell renders the center column as [hash]_centerCol (CSS modules). */\nhtml[data-dsh-memoir-active] [class*=\"centerCol\"] > *:not([data-dsh-memoir-view]) {\n display: none;\n}\n\n.memoir-panel { display: flex; flex-direction: column; height: 100%; overflow: hidden; }\n.memoir-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px 8px; }\n.memoir-title { font-size: 15px; font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.memoir-subtitle { font-size: 11px; opacity: .65; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.memoir-iconbtn {\n border: 1px solid transparent; background: transparent; color: inherit;\n border-radius: 6px; padding: 4px 8px; cursor: pointer; font-size: 12px;\n display: inline-flex; align-items: center; gap: 4px;\n}\n.memoir-iconbtn:hover { background: var(--bg-hover, rgba(0,0,0,.06)); }\n\n.memoir-tabs { display: flex; gap: 4px; padding: 0 14px; border-bottom: 1px solid var(--border, rgba(0,0,0,.1)); }\n.memoir-tab {\n border: none; background: transparent; color: inherit; padding: 7px 12px;\n cursor: pointer; font-size: 13px; border-bottom: 2px solid transparent; opacity: .75;\n}\n.memoir-tab:hover { opacity: 1; }\n.memoir-tab[data-active=\"true\"] { opacity: 1; border-bottom-color: var(--accent, #3b82f6); font-weight: 600; }\n\n.memoir-toolbar { display: flex; gap: 8px; padding: 8px 14px; }\n.memoir-search {\n flex: 1; border: 1px solid var(--border, rgba(0,0,0,.15)); background: transparent;\n color: inherit; border-radius: 6px; padding: 6px 10px; font-size: 13px; outline: none;\n}\n.memoir-search:focus { border-color: var(--accent, #3b82f6); }\n.memoir-primary {\n border: 1px solid transparent; background: var(--accent, #3b82f6); color: #fff;\n border-radius: 6px; padding: 6px 12px; cursor: pointer; font-size: 13px;\n}\n.memoir-primary:hover { filter: brightness(1.05); }\n\n.memoir-body { flex: 1; overflow-y: auto; padding: 4px 14px 16px; }\n.memoir-section-title {\n font-size: 12px; font-weight: 600; opacity: .8; margin: 14px 0 6px;\n display: flex; align-items: center; gap: 6px;\n}\n.memoir-count { font-weight: 400; opacity: .6; }\n.memoir-entry {\n border: 1px solid var(--border, rgba(0,0,0,.08)); border-radius: 8px;\n padding: 8px 10px; margin-bottom: 8px; background: var(--bg-card, rgba(0,0,0,.02));\n}\n.memoir-entry-meta { display: flex; align-items: center; gap: 6px; font-size: 11px; opacity: .7; margin-bottom: 3px; }\n.memoir-chip {\n border-radius: 999px; padding: 0 7px; font-size: 11px; line-height: 18px;\n background: var(--chip-bg, rgba(59,130,246,.12)); color: var(--chip-fg, #2563eb);\n}\n.memoir-entry-title { font-weight: 600; margin-bottom: 2px; }\n.memoir-entry-content { white-space: pre-wrap; word-break: break-word; line-height: 1.5; }\n.memoir-entry-actions { display: flex; gap: 4px; margin: 4px 0; }\n.memoir-status-filter { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; white-space: nowrap; }\n.memoir-status-filter select { max-width: 110px; }\n.memoir-delete {\n float: right; border: none; background: transparent; color: inherit; opacity: .45;\n cursor: pointer; border-radius: 4px; padding: 2px 6px; font-size: 12px;\n}\n.memoir-delete:hover { opacity: 1; background: var(--danger-bg, rgba(220,38,38,.12)); color: var(--danger, #dc2626); }\n\n.memoir-project-card { border: 1px solid var(--border, rgba(0,0,0,.1)); border-radius: 10px; padding: 10px 12px; margin-bottom: 12px; }\n.memoir-project-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 2px; }\n.memoir-project-title { font-weight: 600; font-size: 14px; }\n.memoir-project-path { font-size: 11px; opacity: .6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.memoir-project-meta { font-size: 11px; opacity: .6; margin-bottom: 6px; }\n\n.memoir-empty { text-align: center; padding: 48px 20px; opacity: .7; }\n.memoir-empty-title { font-size: 14px; font-weight: 600; margin-bottom: 6px; }\n.memoir-empty-hint { font-size: 12px; }\n.memoir-error { color: var(--danger, #dc2626); padding: 8px 14px; font-size: 12px; }\n\n.memoir-form { border: 1px solid var(--border, rgba(0,0,0,.1)); border-radius: 10px; padding: 12px; margin: 4px 14px 14px; }\n.memoir-form-row { display: flex; gap: 8px; margin-bottom: 8px; }\n.memoir-field { flex: 1; display: flex; flex-direction: column; gap: 4px; }\n.memoir-field label { font-size: 11px; opacity: .7; }\n.memoir-field input, .memoir-field select, .memoir-field textarea {\n border: 1px solid var(--border, rgba(0,0,0,.15)); background: transparent; color: inherit;\n border-radius: 6px; padding: 6px 8px; font-size: 13px; font-family: inherit; outline: none;\n}\n.memoir-field textarea { min-height: 72px; resize: vertical; }\n.memoir-form-actions { display: flex; justify-content: flex-end; gap: 8px; }\n\n/* Sidebar entry row (plain DOM, matches the shell's nav rows). */\n.memoir-entry-row {\n display: flex; align-items: center; gap: 8px; width: 100%;\n border: none; background: transparent; color: inherit; cursor: pointer;\n padding: 7px 10px; border-radius: 6px; font-size: 13px; text-align: left;\n}\n.memoir-entry-row:hover { background: var(--bg-hover, rgba(0,0,0,.06)); }\n.memoir-entry-row[data-active=\"true\"] { background: var(--bg-hover, rgba(0,0,0,.08)); }\n.memoir-entry-icon { display: inline-flex; }\n\n/* Collapsed (rail) state: the shell flags the rail with data-sidebar-collapsed\n on the frame root (AppFrame.tsx). The plain-DOM entry gets no wide prop, so\n it mirrors the shell's rail treatment via ancestry \u2014 a centered 36x36\n control box with an 18px icon (matching the New Session / Search rail\n icons) and the text label hidden. The entry is a direct flex child of the\n sidebar root, so it sits inline with the shell's own rail controls. */\n[data-sidebar-collapsed] [data-dsh-memoir-entry].memoir-entry-row {\n justify-content: center;\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0 0 12px;\n}\n[data-sidebar-collapsed] [data-dsh-memoir-entry] .memoir-entry-icon svg {\n width: 18px;\n height: 18px;\n}\n[data-sidebar-collapsed] [data-dsh-memoir-entry] .memoir-entry-label {\n display: none;\n}\n\n/* v0.4.2 ranked search */\n.memoir-ranked-note { font-size: 11px; opacity: .65; margin: 4px 0 8px; }\n.memoir-score {\n float: right; border-radius: 999px; padding: 0 7px; font-size: 10px; line-height: 16px;\n background: var(--chip-bg, rgba(59,130,246,.12)); color: var(--chip-fg, #2563eb); opacity: .8;\n}\n\n/* v0.4.2 Hot Memory Inspector */\n.memoir-inspector { margin: 0 14px; border-top: 1px solid var(--border, rgba(0,0,0,.1)); padding-top: 8px; }\n.memoir-inspector-body {\n font-size: 11px; opacity: .85; margin: 6px 0 0; padding: 8px 10px;\n border: 1px solid var(--border, rgba(0,0,0,.1)); border-radius: 8px;\n background: var(--bg-card, rgba(0,0,0,.02)); white-space: pre-wrap; word-break: break-word;\n font-family: inherit; max-height: 220px; overflow-y: auto;\n}\n\nbody[data-ds-dark-theme] [data-dsh-memoir-view] {\n background: var(--bg-panel, #16181d);\n color: var(--text-primary, #e6e6e6);\n}\n\n/* Memory Diagnostics (observability strip, v0.4). */\n.memoir-diagnostics { margin: 0 14px 14px; border-top: 1px solid var(--border, rgba(0,0,0,.1)); padding-top: 8px; }\n.memoir-diagnostics-toggle {\n border: none; background: transparent; color: inherit; cursor: pointer;\n font-size: 12px; opacity: .75; padding: 2px 0;\n}\n.memoir-diagnostics-toggle:hover { opacity: 1; }\n.memoir-diagnostics-body { font-size: 11px; opacity: .8; display: flex; flex-direction: column; gap: 3px; margin-top: 6px; }\n`\n", "/**\n * Sidebar entry injection (the dsh-ssh / task-board DOM-level precedent: the\n * shipped sidebar shell exposes no registration slot for these family\n * plugins). The row is plain DOM \u2014 React never manages it \u2014 and self-heals\n * through a MutationObserver when the shell re-renders. It is placed after\n * the family block ([data-dsh-taskboard-entry], [data-dsh-ssh-entry]) so the\n * three sibling entries keep a stable order.\n */\n\nimport type { PanelController } from './controller.js'\n\n/** Stable data attribute identifying the injected entry row. */\nexport const ENTRY_SELECTOR = '[data-dsh-memoir-entry]'\n\n/** Inline icon (matches the shell's 16px nav-icon look): an open-book glyph. */\nconst ICON = '<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M8 3.5c-1.6-1-3.6-1-5.2-.2-.5.3-.8.9-.8 1.4v8.6c0 .5.5.9 1 .7C4.6 13.4 6.4 13.5 8 14.5c1.6-1 3.4-1.1 5-.5.5.2 1-.2 1-.7V4.7c0-.5-.3-1.1-.8-1.4-1.6-.8-3.6-.8-5.2.2z\"/><path d=\"M8 3.5v11\"/></svg>'\n\nconst FAMILY_SELECTOR = '[data-dsh-taskboard-entry], [data-dsh-ssh-entry], [data-dsh-memoir-entry]'\n\n/** Find the sidebar shell root element, or undefined while not yet mounted. */\nfunction sidebarRoot(): HTMLElement | undefined {\n const column = document.querySelector<HTMLElement>('[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]')\n if (column === null) return undefined\n const logoOwner = column.querySelector<HTMLElement>('[class*=\"logoRow\"]')?.parentElement\n return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)\n}\n\n/** The New Session button: nested in the logo row on current shells. */\nfunction newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {\n const nested = root.querySelector<HTMLButtonElement>('button[class*=\"newSession\"]')\n if (nested !== null) return nested\n for (const child of root.children) {\n if (child.tagName === 'BUTTON') return child as HTMLButtonElement\n }\n return undefined\n}\n\n/** Build the entry row (a detached button; inserted once the shell is up). */\nfunction createEntry(controller: PanelController, t: (key: string) => string): HTMLButtonElement {\n const entry = document.createElement('button')\n entry.type = 'button'\n entry.dataset.dshMemoirEntry = ''\n entry.className = 'memoir-entry-row'\n entry.setAttribute('aria-label', t('entry.label'))\n entry.setAttribute('title', t('entry.tooltip'))\n entry.innerHTML = '<span class=\"memoir-entry-icon\">' + ICON + '</span><span class=\"memoir-entry-label\">' + t('entry.label') + '</span>'\n entry.addEventListener('click', () => {\n controller.toggle()\n })\n return entry\n}\n\n/**\n * Re-insert the entry after the sibling family block (stable relative order).\n * @returns true when the entry is placed.\n */\nfunction placeEntry(root: HTMLElement, entry: HTMLButtonElement): boolean {\n const button = newSessionButton(root)\n if (button === undefined) return false\n if (entry.parentElement !== root) {\n const row = button.closest('[class*=\"logoRow\"]')\n const base = row !== null && row.parentElement === root ? row : button\n const family = Array.from(root.children).filter(\n (el): el is HTMLElement => el instanceof HTMLElement && el.matches(FAMILY_SELECTOR),\n )\n // memoir sits after the whole family block.\n const anchor = family.length > 0 ? family[family.length - 1].nextElementSibling : base.nextElementSibling\n root.insertBefore(entry, anchor)\n }\n return true\n}\n\n/**\n * Mount the sidebar entry, waiting for the shell and self-healing on re-renders.\n * @param controller - the panel controller the entry toggles.\n * @param t - the bound translator.\n * @returns disposer removing the entry and its observers.\n */\nexport function mountSidebarEntry(controller: PanelController, t: (key: string) => string): () => void {\n const entry = createEntry(controller, t)\n let root: HTMLElement | undefined\n let placed = false\n\n const tryPlace = (): void => {\n if (placed) return\n if (root !== undefined && !root.isConnected) {\n rootObserver.disconnect()\n root = undefined\n }\n root ??= sidebarRoot()\n if (root === undefined) return\n placed = placeEntry(root, entry)\n if (placed) {\n rootObserver.observe(root, { childList: true, subtree: true })\n waitObserver.disconnect()\n }\n }\n\n const waitObserver = new MutationObserver(() => {\n tryPlace()\n })\n waitObserver.observe(document.body, { childList: true, subtree: true })\n\n const rootObserver = new MutationObserver(() => {\n if (root === undefined || !root.isConnected) {\n placed = false\n tryPlace()\n return\n }\n if (!root.contains(entry)) {\n placed = placeEntry(root, entry)\n }\n })\n\n const unsubscribe = controller.subscribe(() => {\n entry.dataset.active = controller.getSnapshot().panelOpen ? 'true' : undefined\n })\n entry.dataset.active = controller.getSnapshot().panelOpen ? 'true' : undefined\n\n tryPlace()\n\n return () => {\n waitObserver.disconnect()\n rootObserver.disconnect()\n unsubscribe()\n entry.remove()\n }\n}\n", "/**\n * Panel view mounting (the dsh-ssh / task-board center-column precedent).\n * The panel takes over the center column at the DOM level: a container is\n * appended inside the center-column grid item and a stylesheet rule hides\n * the conversation content while the panel is active. Toggling is a data\n * attribute on <html> \u2014 the conversation subtree stays mounted.\n *\n * Center-column selector: the legacy shell exposed\n * [data-pane=\"conversation\"]; current shells render the frame with CSS\n * modules ([hash]_centerCol). Both are matched so the panel mounts on the\n * source-run shell and older builds alike.\n *\n * Cross-plugin coordination: opening this panel evicts the sibling panels'\n * visibility attributes (data-dsh-ssh-active / data-dsh-taskboard-active)\n * and dispatches the shared `dsh-panel-activate` event; it closes itself when\n * a sibling dispatches its own activation.\n */\n\nimport { createRoot, type Root } from 'react-dom/client'\nimport { MemoirPanel } from './panel.jsx'\nimport type { MemoirApi } from './api.js'\nimport type { CwdTracker } from './cwd.js'\nimport type { PanelController } from './controller.js'\n\n/** The injected panel container (kept in the DOM, hidden when inactive). */\nexport const PANEL_VIEW_SELECTOR = '[data-dsh-memoir-view]'\n\nconst CONVERSATION_COLUMN_SELECTOR = '[data-pane=\"conversation\"], [class*=\"centerCol\"]'\nconst ACTIVE_ATTR = 'data-dsh-memoir-active'\n/** Sibling panels' activation attributes, removed when this panel opens. */\nconst SIBLING_ATTRS = ['data-dsh-ssh-active', 'data-dsh-taskboard-active']\n/** Cross-plugin activation event; detail is the activating panel name. */\nconst ACTIVATE_EVENT = 'dsh-panel-activate'\nconst PANEL_NAME = 'memoir'\n\n/** Find the center column, or undefined while the frame is not mounted. */\nfunction conversationColumn(): HTMLElement | undefined {\n return document.querySelector<HTMLElement>(CONVERSATION_COLUMN_SELECTOR) ?? undefined\n}\n\n/**\n * Mount the panel React tree into the center column and bind its visibility\n * to the controller's panelOpen state.\n * @param controller - the panel controller driving the view.\n * @param api - the memoir API client the panel operates through.\n * @param cwdTracker - the active-workspace tracker (useSyncExternalStore-compatible).\n * @param t - the bound translator.\n * @returns disposer unmounting the tree and restoring the column.\n */\nexport function mountPanel(\n controller: PanelController,\n api: MemoirApi,\n cwdTracker: CwdTracker,\n t: (key: string) => string,\n): () => void {\n let root: Root | undefined\n let container: HTMLDivElement | undefined\n\n const ensure = (): void => {\n if (container !== undefined) {\n if (container.isConnected) return\n root?.unmount()\n root = undefined\n container.remove()\n container = undefined\n }\n const column = conversationColumn()\n if (column === undefined) return\n container = document.createElement('div')\n container.dataset.dshMemoirView = ''\n column.appendChild(container)\n root = createRoot(container)\n root.render(<MemoirPanel controller={controller} api={api} cwdTracker={cwdTracker} t={t} />)\n }\n\n // The frame mounts after boot settlement; watch for the column's arrival.\n const waitObserver = new MutationObserver(() => {\n ensure()\n })\n waitObserver.observe(document.body, { childList: true, subtree: true })\n\n const applyActive = (): void => {\n if (controller.getSnapshot().panelOpen) {\n for (const attr of SIBLING_ATTRS) document.documentElement.removeAttribute(attr)\n document.documentElement.setAttribute(ACTIVE_ATTR, '')\n document.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL_NAME }))\n } else {\n document.documentElement.removeAttribute(ACTIVE_ATTR)\n }\n }\n\n const onOtherActivate = (event: Event): void => {\n const detail = (event as CustomEvent).detail as string | undefined\n if ((detail === 'ssh' || detail === 'taskboard') && controller.getSnapshot().panelOpen) {\n controller.close()\n }\n }\n\n // Hand the center column back to the conversation on sidebar context clicks.\n const SIDEBAR_ROW_SELECTOR = '[class*=\"sessionRow\"], [class*=\"projectRow\"], [class*=\"searchResultRow\"], [class*=\"searchResultWorkspace\"], [class*=\"newSession\"]'\n const onClickSidebarRow = (event: MouseEvent): void => {\n if (!controller.getSnapshot().panelOpen) return\n const target = event.target as HTMLElement | null\n if (target === null || typeof target.closest !== 'function') return\n if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) controller.close()\n }\n\n document.addEventListener('click', onClickSidebarRow, true)\n document.addEventListener(ACTIVATE_EVENT, onOtherActivate)\n const unsubscribe = controller.subscribe(applyActive)\n applyActive()\n ensure()\n\n return () => {\n document.removeEventListener('click', onClickSidebarRow, true)\n document.removeEventListener(ACTIVATE_EVENT, onOtherActivate)\n waitObserver.disconnect()\n unsubscribe()\n document.documentElement.removeAttribute(ACTIVE_ATTR)\n root?.unmount()\n root = undefined\n container?.remove()\n container = undefined\n }\n}\n", "/**\n * The memoir panel: project / global memory tabs, client-side search,\n * manual record form, per-entry delete. Rendered into the center-column\n * container by mount.tsx; visibility is CSS-driven (html data attribute).\n */\n\nimport { Fragment, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { SECTION_KEYS } from './i18n.js'\nimport type { MemoirApi, WireDiagnostics, WireEntry, WireHotMemory, WireProject, WireSearchResult } from './api.js'\nimport type { CwdTracker } from './cwd.js'\nimport type { PanelController } from './controller.js'\nimport type { MemoirStatus, SectionKey } from './types.ts'\n\ninterface PanelProps {\n controller: PanelController\n api: MemoirApi\n cwdTracker: CwdTracker\n t: (key: string) => string\n}\n\ntype EntryPatch = {\n section?: SectionKey\n title?: string | null\n content?: string\n importance?: number\n pinned?: boolean\n status?: MemoirStatus\n supersedes?: string[]\n tags?: string[]\n}\n\n/** One entry's meta line: time, section chip, session id tooltip. */\nfunction EntryMeta({ entry, t }: { entry: WireEntry; t: (key: string) => string }) {\n const when = new Date(entry.time)\n const pad = (n: number) => String(n).padStart(2, '0')\n const timeText = `${when.getFullYear()}-${pad(when.getMonth() + 1)}-${pad(when.getDate())} ${pad(when.getHours())}:${pad(when.getMinutes())}`\n return (\n <div className=\"memoir-entry-meta\">\n <span>{timeText}</span>\n <span className=\"memoir-chip\">{t('sections.' + entry.section)}</span>\n {entry.status !== undefined && entry.status !== 'active'\n ? <span className=\"memoir-chip\">{t('status.' + entry.status)}</span>\n : null}\n {entry.pinned === true ? <span className=\"memoir-chip\">{t('lifecycle.pin')}</span> : null}\n {entry.sessionId !== undefined\n ? <span title={`${t('session')}: ${entry.sessionId}`}>{entry.sessionId.slice(0, 12)}</span>\n : null}\n </div>\n )\n}\n\nfunction EntryCard({ entry, t, onDelete, onUpdate, score }: { entry: WireEntry; t: (key: string) => string; onDelete: (entry: WireEntry) => void; onUpdate?: (entry: WireEntry, patch: EntryPatch) => void; score?: number }) {\n const [editing, setEditing] = useState(false)\n const [section, setSection] = useState<SectionKey>(entry.section)\n const [title, setTitle] = useState(entry.title ?? '')\n const [content, setContent] = useState(entry.content)\n const save = () => {\n if (content.trim() === '' || onUpdate === undefined) return\n onUpdate(entry, { section, title: title.trim() === '' ? null : title.trim(), content: content.trim() })\n setEditing(false)\n }\n return (\n <div className=\"memoir-entry\">\n <button type=\"button\" className=\"memoir-delete\" title={t('delete.confirm')} onClick={() => onDelete(entry)}>\u00D7</button>\n {onUpdate !== undefined\n ? <div className=\"memoir-entry-actions\">\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => setEditing((value) => !value)}>{editing ? t('toolbar.cancel') : t('lifecycle.edit')}</button>\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => onUpdate(entry, { pinned: entry.pinned !== true })}>{entry.pinned === true ? t('lifecycle.unpin') : t('lifecycle.pin')}</button>\n {(entry.status ?? 'active') === 'active'\n ? <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => onUpdate(entry, { status: 'superseded' })}>{t('lifecycle.supersede')}</button>\n : <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => onUpdate(entry, { status: 'active' })}>{t('lifecycle.unsupersede')}</button>}\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => onUpdate(entry, { status: entry.status === 'archived' ? 'active' : 'archived' })}>{entry.status === 'archived' ? t('lifecycle.restore') : t('lifecycle.archive')}</button>\n </div>\n : null}\n <EntryMeta entry={entry} t={t} />\n {score !== undefined\n ? <span className=\"memoir-score\" title={t('search.ranked')}>{score.toFixed(3)}</span>\n : null}\n {editing\n ? <div className=\"memoir-form memoir-entry-editor\">\n <div className=\"memoir-field\">\n <label>{t('form.section')}</label>\n <select value={section} onChange={(e) => setSection(e.target.value as SectionKey)}>\n {SECTION_KEYS.map((key) => <option key={key} value={key}>{t('sections.' + key)}</option>)}\n </select>\n </div>\n <div className=\"memoir-field\">\n <label>{t('form.title')}</label>\n <input value={title} onChange={(e) => setTitle(e.target.value)} />\n </div>\n <div className=\"memoir-field\">\n <label>{t('form.content')}</label>\n <textarea value={content} onChange={(e) => setContent(e.target.value)} />\n </div>\n <div className=\"memoir-form-actions\">\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => setEditing(false)}>{t('toolbar.cancel')}</button>\n <button type=\"button\" className=\"memoir-primary\" onClick={save}>{t('lifecycle.save')}</button>\n </div>\n </div>\n : <>\n {entry.title !== undefined ? <div className=\"memoir-entry-title\">{entry.title}</div> : null}\n <div className=\"memoir-entry-content\">{entry.content}</div>\n </>}\n </div>\n )\n}\n\n/** Sectioned entry list in canonical order. */\nfunction SectionedEntries({ entries, t, onDelete, onUpdate }: { entries: WireEntry[]; t: (key: string) => string; onDelete: (entry: WireEntry) => void; onUpdate?: (entry: WireEntry, patch: EntryPatch) => void }) {\n const groups = useMemo(\n () => SECTION_KEYS.map((key) => ({ key, entries: entries.filter((e) => e.section === key) })).filter((g) => g.entries.length > 0),\n [entries],\n )\n if (groups.length === 0) {\n return (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.search')}</div>\n </div>\n )\n }\n return (\n <Fragment>\n {groups.map((group) => (\n <Fragment key={group.key}>\n <div className=\"memoir-section-title\">\n {t('sections.' + group.key)}\n <span className=\"memoir-count\">({group.entries.length})</span>\n </div>\n {group.entries.map((entry) => (\n <EntryCard key={entry.id} entry={entry} t={t} onDelete={onDelete} onUpdate={onUpdate} />\n ))}\n </Fragment>\n ))}\n </Fragment>\n )\n}\n\n/**\n * Ranked search results (v0.4.2): the host RetrievalEngine order, flat for\n * the project tab or grouped by project for the global tab. Scores are\n * shown as chips so the ranking is inspectable.\n */\nfunction RankedResults({ results, pending, grouped, t, onDelete, onUpdate }: {\n results: WireSearchResult[]\n pending: boolean\n grouped: boolean\n t: (key: string) => string\n onDelete: (entry: WireEntry, path: string) => void\n onUpdate?: (entry: WireEntry, path: string, patch: EntryPatch) => void\n}) {\n if (pending) return <div className=\"memoir-empty\">\u2026</div>\n if (results.length === 0) {\n return (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.search')}</div>\n <div className=\"memoir-empty-hint\">{t('search.ranked')}</div>\n </div>\n )\n }\n if (!grouped) {\n return (\n <Fragment>\n <div className=\"memoir-ranked-note\">{t('search.ranked')}</div>\n {results.map((r) => (\n <EntryCard key={r.entry.id} entry={r.entry} score={r.score} t={t} onDelete={(entry) => onDelete(entry, r.projectPath)} onUpdate={(entry, patch) => onUpdate?.(entry, r.projectPath, patch)} />\n ))}\n </Fragment>\n )\n }\n const groups = new Map<string, WireSearchResult[]>()\n for (const result of results) {\n const bucket = groups.get(result.projectPath) ?? []\n bucket.push(result)\n groups.set(result.projectPath, bucket)\n }\n return (\n <Fragment>\n {[...groups.entries()].map(([path, bucket]) => (\n <div className=\"memoir-project-card\" key={path}>\n <div className=\"memoir-project-head\">\n <span className=\"memoir-project-title\">{path.split('/').filter(Boolean).pop() || path}</span>\n <span className=\"memoir-project-path\">{path}</span>\n </div>\n {bucket.map((r) => (\n <EntryCard key={r.entry.id} entry={r.entry} score={r.score} t={t} onDelete={(entry) => onDelete(entry, path)} onUpdate={(entry, patch) => onUpdate?.(entry, path, patch)} />\n ))}\n </div>\n ))}\n </Fragment>\n )\n}\n\nfunction AddForm({ t, onSubmit, onCancel }: { t: (key: string) => string; onSubmit: (payload: { section: SectionKey; title?: string; content: string }) => void; onCancel: () => void }) {\n const [section, setSection] = useState<SectionKey>('lessons')\n const [title, setTitle] = useState('')\n const [content, setContent] = useState('')\n const submit = () => {\n if (content.trim() === '') return\n onSubmit({ section, title: title.trim() === '' ? undefined : title.trim(), content: content.trim() })\n }\n return (\n <div className=\"memoir-form\">\n <div className=\"memoir-form-row\">\n <div className=\"memoir-field\">\n <label>{t('form.section')}</label>\n <select value={section} onChange={(e) => setSection(e.target.value as SectionKey)}>\n {SECTION_KEYS.map((key) => (\n <option key={key} value={key}>{t('sections.' + key)}</option>\n ))}\n </select>\n </div>\n <div className=\"memoir-field\">\n <label>{t('form.title')}</label>\n <input value={title} placeholder={t('form.placeholder.title')} onChange={(e) => setTitle(e.target.value)} />\n </div>\n </div>\n <div className=\"memoir-field\">\n <label>{t('form.content')}</label>\n <textarea value={content} placeholder={t('form.placeholder.content')} onChange={(e) => setContent(e.target.value)} />\n </div>\n <div className=\"memoir-form-actions\">\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={onCancel}>{t('toolbar.cancel')}</button>\n <button type=\"button\" className=\"memoir-primary\" onClick={submit}>{t('form.submit')}</button>\n </div>\n </div>\n )\n}\n\nexport function MemoirPanel({ controller, api, cwdTracker, t }: PanelProps) {\n const cwd = useSyncExternalStore(cwdTracker.subscribe, cwdTracker.getSnapshot)\n const [tab, setTab] = useState<'project' | 'global'>('project')\n const [query, setQuery] = useState('')\n const [statusFilter, setStatusFilter] = useState<MemoirStatus | 'all'>('active')\n const [formOpen, setFormOpen] = useState(false)\n const [refreshKey, setRefreshKey] = useState(0)\n const [project, setProject] = useState<WireProject | null>(null)\n const [projects, setProjects] = useState<WireProject[]>([])\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [diag, setDiag] = useState<WireDiagnostics | null>(null)\n const [diagOpen, setDiagOpen] = useState(false)\n const [searchResults, setSearchResults] = useState<WireSearchResult[] | null>(null)\n const [hotMemory, setHotMemory] = useState<WireHotMemory | null>(null)\n const [inspectorOpen, setInspectorOpen] = useState(false)\n\n const q = query.trim().toLowerCase()\n\n useEffect(() => {\n let cancelled = false\n setError(null)\n if (tab === 'project') {\n if (cwd === '') {\n setProject(null)\n return undefined\n }\n setLoading(true)\n api.project(cwd, { status: statusFilter })\n .then((value) => { if (!cancelled) setProject(value.project) })\n .catch((e: Error) => { if (!cancelled) setError(e.message) })\n .finally(() => { if (!cancelled) setLoading(false) })\n } else {\n setLoading(true)\n api.global({ status: statusFilter })\n .then((value) => { if (!cancelled) setProjects(value.projects) })\n .catch((e: Error) => { if (!cancelled) setError(e.message) })\n .finally(() => { if (!cancelled) setLoading(false) })\n }\n // Diagnostics follow the active workspace (observability, roadmap \u00A74).\n api.diagnostics(cwd === '' ? undefined : cwd)\n .then((value) => { if (!cancelled) setDiag(value) })\n .catch(() => { if (!cancelled) setDiag(null) })\n // Hot-memory inspector preview (what the next session inherits).\n if (cwd === '') {\n setHotMemory(null)\n } else {\n api.hotMemory(cwd)\n .then((value) => { if (!cancelled) setHotMemory(value.hotMemory) })\n .catch(() => { if (!cancelled) setHotMemory(null) })\n }\n return () => { cancelled = true }\n }, [tab, cwd, statusFilter, refreshKey])\n\n // v0.4.2: a non-empty query searches through the host RetrievalEngine \u2014\n // the same BM25 ranking memoir_read uses. Debounced; empty query resets.\n useEffect(() => {\n if (q === '') {\n setSearchResults(null)\n return undefined\n }\n let cancelled = false\n const timer = setTimeout(() => {\n const scope = tab === 'project' ? 'project' : 'global'\n api.search({ scope, path: tab === 'project' && cwd !== '' ? cwd : undefined, query: q, status: statusFilter })\n .then((value) => { if (!cancelled) setSearchResults(value.results) })\n .catch((e: Error) => {\n if (!cancelled) {\n setSearchResults([])\n setError(e.message)\n }\n })\n }, 200)\n return () => { cancelled = true; clearTimeout(timer) }\n }, [q, tab, cwd, statusFilter, refreshKey])\n\n const filterEntries = (entries: WireEntry[]) =>\n q === '' ? entries : entries.filter((e) => `${e.title ?? ''} ${e.content}`.toLowerCase().includes(q))\n\n const reload = () => setRefreshKey((k) => k + 1)\n\n const onDelete = (entry: WireEntry, entryPath: string) => {\n if (!window.confirm(t('delete.confirm'))) return\n setBusy(true)\n api.remove({ path: entryPath, id: entry.id })\n .then(() => reload())\n .catch((e: Error) => setError(`${t('delete.failed')}: ${e.message}`))\n .finally(() => setBusy(false))\n }\n\n const onRecord = (payload: { section: SectionKey; title?: string; content: string }) => {\n setBusy(true)\n api.record({ path: cwd, ...payload })\n .then(() => {\n setFormOpen(false)\n reload()\n })\n .catch((e: Error) => setError(`${t('record.failed')}: ${e.message}`))\n .finally(() => setBusy(false))\n }\n\n const onUpdate = (entry: WireEntry, entryPath: string, patch: EntryPatch) => {\n setBusy(true)\n api.update({ path: entryPath, id: entry.id, ...patch })\n .then(() => reload())\n .catch((e: Error) => setError(`${t('update.failed')}: ${e.message}`))\n .finally(() => setBusy(false))\n }\n\n const projectEntries = project === null ? [] : filterEntries(project.entries)\n\n return (\n <div className=\"memoir-panel\">\n <div className=\"memoir-header\">\n <div className=\"memoir-title\">\n {t('panel.title')}\n <div className=\"memoir-subtitle\">{tab === 'project' ? (cwd === '' ? t('empty.workspace') : cwd) : t('tab.global')}</div>\n </div>\n <button type=\"button\" className=\"memoir-iconbtn\" title={t('panel.refresh')} onClick={reload}>\u27F3</button>\n <button type=\"button\" className=\"memoir-iconbtn\" title={t('panel.close')} onClick={() => controller.close()}>\u00D7</button>\n </div>\n <div className=\"memoir-tabs\">\n <button type=\"button\" className=\"memoir-tab\" data-active={tab === 'project' ? 'true' : undefined} onClick={() => setTab('project')}>{t('tab.project')}</button>\n <button type=\"button\" className=\"memoir-tab\" data-active={tab === 'global' ? 'true' : undefined} onClick={() => setTab('global')}>{t('tab.global')}</button>\n </div>\n <div className=\"memoir-toolbar\">\n <input className=\"memoir-search\" placeholder={t('toolbar.search')} value={query} onChange={(e) => setQuery(e.target.value)} />\n <label className=\"memoir-status-filter\">\n <span>{t('filter.status')}</span>\n <select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value as MemoirStatus | 'all')}>\n <option value=\"active\">{t('status.active')}</option>\n <option value=\"all\">{t('filter.status')}: all</option>\n <option value=\"superseded\">{t('status.superseded')}</option>\n <option value=\"archived\">{t('status.archived')}</option>\n </select>\n </label>\n {tab === 'project' && cwd !== ''\n ? <button type=\"button\" className=\"memoir-primary\" onClick={() => setFormOpen((v) => !v)}>{formOpen ? t('toolbar.cancel') : t('toolbar.add')}</button>\n : null}\n </div>\n {formOpen && tab === 'project' && cwd !== ''\n ? <AddForm t={t} onSubmit={onRecord} onCancel={() => setFormOpen(false)} />\n : null}\n {error !== null ? <div className=\"memoir-error\">{error}</div> : null}\n <div className=\"memoir-body\">\n {loading\n ? <div className=\"memoir-empty\">\u2026</div>\n : q !== ''\n ? (\n <RankedResults\n results={searchResults ?? []}\n pending={searchResults === null}\n grouped={tab === 'global'}\n t={t}\n onDelete={(entry, path) => onDelete(entry, path)}\n onUpdate={onUpdate}\n />\n )\n : tab === 'project'\n ? cwd === ''\n ? (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.workspace')}</div>\n <div className=\"memoir-empty-hint\">{t('empty.workspaceHint')}</div>\n </div>\n )\n : projectEntries.length === 0\n ? (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.project')}</div>\n <div className=\"memoir-empty-hint\">{t('empty.projectHint')}</div>\n </div>\n )\n : <SectionedEntries entries={projectEntries} t={t} onDelete={(entry) => onDelete(entry, project?.path ?? cwd)} onUpdate={(entry, patch) => onUpdate(entry, project?.path ?? cwd, patch)} />\n : projects.length === 0\n ? (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.global')}</div>\n <div className=\"memoir-empty-hint\">{t('empty.globalHint')}</div>\n </div>\n )\n : projects.map((p) => {\n const entries = filterEntries(p.entries)\n if (entries.length === 0) return null\n const when = new Date(p.updatedAt)\n return (\n <div className=\"memoir-project-card\" key={p.key}>\n <div className=\"memoir-project-head\">\n <span className=\"memoir-project-title\">{p.title}</span>\n <span className=\"memoir-project-path\">{p.path}</span>\n </div>\n <div className=\"memoir-project-meta\">\n {`${t('updated')} ${when.toISOString().slice(0, 16).replace('T', ' ')} \u00B7 ${entries.length} ${t('entries')}`}\n </div>\n <SectionedEntries entries={entries} t={t} onDelete={(entry) => onDelete(entry, p.path)} onUpdate={(entry, patch) => onUpdate(entry, p.path, patch)} />\n </div>\n )\n })}\n </div>\n {busy ? <div className=\"memoir-empty\">\u2026</div> : null}\n <div className=\"memoir-inspector\">\n <button type=\"button\" className=\"memoir-diagnostics-toggle\" onClick={() => setInspectorOpen((v) => !v)}>\n {t('inspector.title')} {inspectorOpen ? '\u25BE' : '\u25B8'}\n </button>\n {inspectorOpen\n ? hotMemory === null || hotMemory.text === ''\n ? <div className=\"memoir-inspector-body\">{t('inspector.empty')}</div>\n : <pre className=\"memoir-inspector-body\">{hotMemory.text}</pre>\n : null}\n </div>\n <div className=\"memoir-diagnostics\">\n <button type=\"button\" className=\"memoir-diagnostics-toggle\" onClick={() => setDiagOpen((v) => !v)}>\n {t('diag.title')} {diagOpen ? '\u25BE' : '\u25B8'}\n </button>\n {diagOpen && diag !== null\n ? (\n <div className=\"memoir-diagnostics-body\">\n <div>{t('diag.revision')}: {diag.storeRevision} \u00B7 {t('diag.snapshot')}: {diag.snapshotCount}/{diag.snapshotMax}</div>\n <div>{t('diag.cache')}: {diag.cache.hits}/{diag.cache.loads} \u547D\u4E2D ({Math.round(diag.cache.hitRate * 100)}%) \u00B7 {t('diag.render')}: {Math.round(diag.cache.renderHitRate * 100)}%</div>\n {diag.hotMemory !== null\n ? <div>{t('diag.hot')}: {diag.hotMemory.selected}/{diag.hotMemory.total} \u6761 \u00B7 ~{diag.hotMemory.estimatedTokens} tokens\uFF08\u9884\u7B97 {diag.config.hotMemoryTokens}/{diag.config.hotMemoryMaxTokens}\uFF09</div>\n : null}\n <div>{t('diag.retrieval')}: {diag.retrieval.index === null ? '\u2014' : `${diag.retrieval.index.docs} docs \u00B7 ${diag.retrieval.index.terms} terms \u00B7 epoch ${diag.retrieval.index.epoch}`}</div>\n <div>{t('diag.qcache')}: {diag.retrieval.cache.hits} hits / {diag.retrieval.cache.misses} misses ({Math.round(diag.retrieval.cache.hitRate * 100)}%) \u00B7 {diag.retrieval.cache.size}/{diag.retrieval.cache.capacity} \u00B7 {diag.retrieval.cache.evictions} evicted</div>\n {diag.retrieval.lastQuery !== null\n ? <div>{t('diag.lastQuery')}: {diag.retrieval.lastQuery.latencyMs.toFixed(1)} ms \u00B7 {diag.retrieval.lastQuery.returned}/{diag.retrieval.lastQuery.candidates} returned</div>\n : null}\n {diag.snapshot !== null\n ? <div>{t('diag.snapshotInfo')}: {diag.snapshot.hash} \u00B7 rev {diag.snapshot.storeRevision}</div>\n : null}\n </div>\n )\n : null}\n </div>\n </div>\n )\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * Browser-half entry for the dsh-memoir plugin \u2014 runs inside the dsh web GUI.\n * Mounts the sidebar entry row (toggles the panel) and the memoir panel in\n * the center column, bound to the active session's workspace. Failure policy:\n * DOM mounting problems are logged, never thrown \u2014 the web shell fails the\n * whole boot when a plugin apply throws, and an external plugin must not take\n * the GUI down.\n */\n\nimport type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'\nimport { PanelController } from './controller.js'\nimport { MemoirApi } from './api.js'\nimport { createCwdTracker } from './cwd.js'\nimport { makeT } from './i18n.js'\nimport { PANEL_CSS } from './styles.js'\nimport { mountSidebarEntry } from './sidebar-entry.js'\nimport { mountPanel } from './mount.jsx'\n\n/** Required services (fiber inject waiting \u2014 the runtime must be up first). */\nexport const inject = ['sessions']\n\n/** Inject the panel stylesheet once (the loader removes plugin-owned tags on unload). */\nfunction injectStyles(): void {\n if (document.querySelector('style[data-plugin=\"dsh-memoir\"]') !== null) return\n const tag = document.createElement('style')\n tag.dataset.plugin = 'dsh-memoir'\n tag.textContent = PANEL_CSS\n document.head.appendChild(tag)\n}\n\n/**\n * Mount the memoir panel.\n * @param ctx - client root context (sessions service).\n */\nexport function apply(ctx: ClientContext): void {\n const t = makeT(document)\n const disposers: Array<() => void> = []\n try {\n const controller = new PanelController()\n const api = new MemoirApi()\n const cwdTracker = createCwdTracker(ctx.sessions)\n injectStyles()\n disposers.push(mountSidebarEntry(controller, t))\n disposers.push(mountPanel(controller, api, cwdTracker, t))\n } catch (error) {\n // DOM failures degrade the panel, never the GUI.\n console.warn('[dsh-memoir] mount failed:', error)\n }\n ctx.effect(() => () => {\n for (const dispose of disposers.splice(0)) dispose()\n }, 'dsh-memoir: ui mounts')\n}\n", "/**\n * Panel open/close state machine \u2014 pure, no DOM, unit-testable. Mirrors the\n * sibling panel controllers: a snapshot + subscribe contract drives the\n * sidebar entry highlight and the panel visibility attributes.\n */\n\nexport interface PanelSnapshot {\n panelOpen: boolean\n}\n\nexport class PanelController {\n private state: PanelSnapshot = { panelOpen: false }\n private listeners = new Set<() => void>()\n\n /** The current immutable snapshot. */\n getSnapshot(): PanelSnapshot {\n return this.state\n }\n\n /** Subscribe to state changes; returns an unsubscribe function. */\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener)\n return () => {\n this.listeners.delete(listener)\n }\n }\n\n open(): void {\n this.patch({ panelOpen: true })\n }\n\n close(): void {\n this.patch({ panelOpen: false })\n }\n\n toggle(): void {\n this.patch({ panelOpen: !this.state.panelOpen })\n }\n\n /** Replace the snapshot; notifies only when the observable state changed. */\n private patch(next: Partial<PanelSnapshot>): void {\n const prev = this.state\n this.state = { ...prev, ...next }\n if (this.state.panelOpen === prev.panelOpen) return\n for (const listener of [...this.listeners]) listener()\n }\n}\n", "/**\n * Browser API client for the host /api/dsh-memoir routes. The only data path\n * the panel uses \u2014 same-origin fetch, JSON envelope { ok, value | error }.\n * The fetch implementation is injectable for tests.\n */\n\nimport type { MemoirStatus, SectionKey } from './types.ts'\n\n/** Error carrying the route's JSON error message. */\nexport class MemoirApiError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'MemoirApiError'\n }\n}\n\ninterface EnvelopeResponse {\n status: number\n ok: boolean\n json(): Promise<unknown>\n}\n\n/** Parse the envelope or throw a MemoirApiError (transport or route error). */\nexport async function readEnvelope(response: EnvelopeResponse): Promise<unknown> {\n let body: unknown\n try {\n body = await response.json()\n } catch {\n throw new MemoirApiError(`HTTP ${response.status}: invalid JSON response`)\n }\n if (typeof body !== 'object' || body === null || (body as { ok?: boolean }).ok !== true) {\n const record = body as { error?: { message?: string } } | null\n const message = record && typeof record.error === 'object' && record.error !== null && typeof record.error.message === 'string'\n ? record.error.message\n : `HTTP ${response.status}`\n throw new MemoirApiError(message)\n }\n return (body as { value: unknown }).value\n}\n\n/** Query-string helper (omits undefined/empty values). */\nexport function query(params: Record<string, string | undefined>): string {\n const search = new URLSearchParams()\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== '') search.set(key, String(value))\n }\n const text = search.toString()\n return text === '' ? '' : '?' + text\n}\n\n/** One memory entry as the panel sees it over the wire. */\nexport interface WireEntry {\n id: string\n section: SectionKey\n title?: string\n content: string\n time: number\n sessionId?: string\n importance?: number\n pinned?: boolean\n status?: MemoirStatus\n supersedes?: string[]\n tags?: string[]\n}\n\nexport interface WireProject {\n key: string\n path: string\n title: string\n updatedAt: number\n entries: WireEntry[]\n}\n\n/** The host /diagnostics payload (v0.4 observability, v0.4.2 extended). */\nexport interface WireDiagnostics {\n storeRevision: number\n snapshotEpoch: number\n cache: {\n revision: number\n epoch: number\n loads: number\n hits: number\n misses: number\n hitRate: number\n fileReads: number\n statProbes: number\n corruptBackups: number\n renders: number\n renderComputes: number\n renderHitRate: number\n lastLoadMs?: number\n }\n snapshotCount: number\n snapshotMax: number\n hotMemory: { selected: number; total: number; estimatedTokens: number } | null\n /** v0.4.2: retrieval index / query cache / last query. */\n retrieval: {\n index: { docs: number; terms: number; epoch: number } | null\n cache: { hits: number; misses: number; evictions: number; hitRate: number; size: number; capacity: number }\n lastQuery: { query: string; latencyMs: number; candidates: number; returned: number; at: number } | null\n }\n /** v0.4.2: the most recently frozen session snapshot. */\n snapshot: { hash: string; createdAt: number; storeRevision: number } | null\n config: {\n autoDistillEvery: number\n autoDistillCooldownMin: number\n autoDistillMinTools: number\n hotMemoryTokens: number\n hotMemoryMaxTokens: number\n readDefaultLimit: number\n readMaxLimit: number\n sessionSnapshotMax: number\n queryCacheSize: number\n }\n}\n\n/** One ranked /search hit (v0.4.2: shared RetrievalEngine order). */\nexport interface WireSearchResult {\n projectPath: string\n entry: WireEntry\n score: number\n}\n\n/** The /hot-memory inspector payload (v0.4.2). */\nexport interface WireHotMemory {\n text: string\n selected: WireEntry[]\n total: number\n estimatedTokens: number\n}\n\nexport interface RecordPayload {\n path: string\n section: SectionKey\n title?: string\n content: string\n importance?: number\n pinned?: boolean\n supersedes?: string[]\n tags?: string[]\n}\n\ntype FetchLike = (input: string, init?: RequestInit) => Promise<EnvelopeResponse>\n\n/** The browser half's only data entry point. */\nexport class MemoirApi {\n private readonly fetchImpl: FetchLike\n\n /**\n * @param fetchImpl - injectable fetch (defaults to globalThis.fetch).\n */\n constructor(fetchImpl?: FetchLike) {\n this.fetchImpl = fetchImpl ?? ((...args) => globalThis.fetch(...args) as Promise<EnvelopeResponse>)\n }\n\n /** Read one project's memory (empty project shape when unknown). */\n async project(path: string, options: { section?: SectionKey; query?: string; status?: MemoirStatus | 'all' } = {}): Promise<{ project: WireProject }> {\n const response = await this.fetchImpl('/api/dsh-memoir/project' + query({ path, ...options }))\n return readEnvelope(response) as Promise<{ project: WireProject }>\n }\n\n /** Read the cross-project global index. */\n async global(options: { section?: SectionKey; query?: string; status?: MemoirStatus | 'all' } = {}): Promise<{ projects: WireProject[] }> {\n const response = await this.fetchImpl('/api/dsh-memoir/global' + query({ ...options }))\n return readEnvelope(response) as Promise<{ projects: WireProject[] }>\n }\n\n /** Read runtime diagnostics (cache hit rates, snapshot/hot-memory stats). */\n async diagnostics(path?: string): Promise<WireDiagnostics> {\n const response = await this.fetchImpl('/api/dsh-memoir/diagnostics' + query({ path }))\n return readEnvelope(response) as Promise<WireDiagnostics>\n }\n\n /**\n * Ranked search over the host RetrievalEngine (v0.4.2) \u2014 the same ranking\n * memoir_read uses, so the GUI search and the agent recall never diverge.\n */\n async search(options: { scope: 'project' | 'global' | 'all'; path?: string; section?: SectionKey; query: string; limit?: number; status?: MemoirStatus | 'all' }): Promise<{ results: WireSearchResult[] }> {\n const response = await this.fetchImpl('/api/dsh-memoir/search' + query({\n scope: options.scope,\n path: options.path,\n section: options.section,\n query: options.query,\n limit: options.limit === undefined ? undefined : String(options.limit),\n status: options.status,\n }))\n return readEnvelope(response) as Promise<{ results: WireSearchResult[] }>\n }\n\n /** Hot-memory preview for one workspace (the inspector). */\n async hotMemory(path: string): Promise<{ hotMemory: WireHotMemory | null }> {\n const response = await this.fetchImpl('/api/dsh-memoir/hot-memory' + query({ path }))\n return readEnvelope(response) as Promise<{ hotMemory: WireHotMemory | null }>\n }\n\n /** Record one entry (host regenerates PROJECT_MEMORY.md). */\n async record(payload: RecordPayload): Promise<{ entry: WireEntry }> {\n const response = await this.fetchImpl('/api/dsh-memoir/entries', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n return readEnvelope(response) as Promise<{ entry: WireEntry }>\n }\n\n /** Delete one entry by id. */\n async remove(payload: { path: string; id: string }): Promise<{ removed: boolean }> {\n const response = await this.fetchImpl('/api/dsh-memoir/entries', {\n method: 'DELETE',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n return readEnvelope(response) as Promise<{ removed: boolean }>\n }\n\n /** Update entry content or lifecycle metadata without deleting. */\n async update(payload: { path: string; id: string; section?: SectionKey; title?: string | null; content?: string; importance?: number; pinned?: boolean; status?: MemoirStatus; supersedes?: string[]; tags?: string[] }): Promise<{ entry: WireEntry | null; updated: boolean }> {\n const response = await this.fetchImpl('/api/dsh-memoir/entries', {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n return readEnvelope(response) as Promise<{ entry: WireEntry | null; updated: boolean }>\n }\n}\n", "/**\n * Workspace tracker: projects the client sessions service's active session\n * cwd into a useSyncExternalStore-compatible subscription. Pure logic \u2014 the\n * sessions service is injected, unit-testable with a mock.\n */\n\nexport interface SessionRecordLike {\n cwd?: string\n}\n\nexport interface SessionsLike {\n list: {\n getSnapshot(): { current: string | undefined; byId: Record<string, SessionRecordLike> }\n subscribe(listener: () => void): () => void\n }\n}\n\n/** Read the active session's cwd from a sessions service snapshot ('' when none). */\nexport function readCwd(sessions: SessionsLike): string {\n const snapshot = sessions.list.getSnapshot()\n const id = snapshot.current\n const cwd = id === undefined ? undefined : snapshot.byId[id]?.cwd\n return typeof cwd === 'string' && cwd !== '' ? cwd : ''\n}\n\n/** A useSyncExternalStore-compatible tracker over the sessions service. */\nexport interface CwdTracker {\n getSnapshot(): string\n subscribe(listener: () => void): () => void\n}\n\n/**\n * Build a { getSnapshot, subscribe } tracker over the sessions service.\n * @param sessions - the client sessions service (with `list.getSnapshot` and\n * `list.subscribe`).\n */\nexport function createCwdTracker(sessions: SessionsLike): CwdTracker {\n let current = readCwd(sessions)\n const listeners = new Set<() => void>()\n const tracker: CwdTracker = {\n getSnapshot: () => current,\n subscribe: (listener) => {\n listeners.add(listener)\n const dispose = sessions.list.subscribe(() => {\n const next = readCwd(sessions)\n if (next === current) return\n current = next\n for (const l of [...listeners]) l()\n })\n return () => {\n listeners.delete(listener)\n dispose()\n }\n },\n }\n return tracker\n}\n", "/**\n * Bilingual surface copy (zh-first). Pure \u2014 no DOM, unit-testable.\n * The shell owns <html lang>; the panel reads it at render time.\n */\n\nexport type Lang = 'zh' | 'en'\n\nexport const dictionaries: Record<Lang, Record<string, string>> = {\n zh: {\n 'entry.label': '\u8BB0\u5FC6',\n 'entry.tooltip': '\u6253\u5F00\u8BB0\u5FC6\u9762\u677F\uFF08\u9879\u76EE\u8BB0\u5FC6 / \u5168\u5C40\u8BB0\u5FC6\uFF09',\n 'panel.title': '\u8BB0\u5FC6 Memoir',\n 'panel.close': '\u5173\u95ED',\n 'panel.refresh': '\u5237\u65B0',\n 'tab.project': '\u9879\u76EE\u8BB0\u5FC6',\n 'tab.global': '\u5168\u5C40\u8BB0\u5FC6',\n 'toolbar.search': '\u641C\u7D22\u6807\u9898\u4E0E\u6B63\u6587\u2026',\n 'toolbar.add': '\u6DFB\u52A0\u8BB0\u5FC6',\n 'toolbar.cancel': '\u53D6\u6D88',\n 'form.section': '\u5206\u7C7B',\n 'form.title': '\u6807\u9898\uFF08\u53EF\u9009\uFF09',\n 'form.content': '\u5185\u5BB9',\n 'form.submit': '\u8BB0\u5F55',\n 'form.placeholder.title': '\u4E00\u53E5\u8BDD\u6807\u9898\uFF0C\u5982\u300C\u4FEE\u590D pet \u60AC\u505C\u95EA\u9000\u300D',\n 'form.placeholder.content': '\u5177\u4F53\u505A\u4E86\u4EC0\u4E48\u3001\u7ED3\u8BBA\u3001\u6559\u8BAD\u6216\u4E0B\u4E00\u6B65\u600E\u4E48\u505A\u3002\u5EFA\u8BAE\u7CBE\u70BC\u3001\u53EF\u6267\u884C\u3002',\n 'empty.project': '\u672C\u9879\u76EE\u6682\u65E0\u6301\u4E45\u8BB0\u5FC6',\n 'empty.projectHint': '\u8BA9 agent \u7528 memoir_record \u6C89\u6DC0\u7ECF\u9A8C\uFF0C\u6216\u70B9\u300C\u6DFB\u52A0\u8BB0\u5FC6\u300D\u624B\u52A8\u8BB0\u5F55\u3002',\n 'empty.workspace': '\u672A\u6253\u5F00\u9879\u76EE\u4F1A\u8BDD',\n 'empty.workspaceHint': '\u6253\u5F00\u4E00\u4E2A\u9879\u76EE\u4F1A\u8BDD\u540E\uFF0C\u8FD9\u91CC\u663E\u793A\u8BE5\u9879\u76EE\u7684\u6301\u4E45\u8BB0\u5FC6\u3002',\n 'empty.global': '\u5168\u5C40\u7D22\u5F15\u4E3A\u7A7A',\n 'empty.globalHint': '\u5728\u4EFB\u4F55\u9879\u76EE\u91CC\u7528 memoir_record \u8BB0\u5F55\u540E\uFF0C\u4F1A\u51FA\u73B0\u5728\u8FD9\u91CC\u4F9B\u8DE8\u9879\u76EE\u68C0\u7D22\u3002',\n 'empty.search': '\u6CA1\u6709\u5339\u914D\u7684\u6761\u76EE',\n 'load.failed': '\u52A0\u8F7D\u5931\u8D25',\n 'record.failed': '\u8BB0\u5F55\u5931\u8D25',\n 'update.failed': '\u66F4\u65B0\u5931\u8D25',\n 'delete.failed': '\u5220\u9664\u5931\u8D25',\n 'delete.confirm': '\u5220\u9664\u8FD9\u6761\u8BB0\u5FC6\uFF1F',\n 'updated': '\u66F4\u65B0\u4E8E',\n 'entries': '\u6761',\n 'session': '\u4F1A\u8BDD',\n 'sections.work': '\u5DE5\u4F5C\u8BB0\u5F55',\n 'sections.lessons': '\u7ECF\u9A8C\u6559\u8BAD',\n 'sections.actions': '\u884C\u52A8\u6307\u5357',\n 'sections.note': '\u5907\u6CE8',\n 'diag.title': 'Memory Diagnostics',\n 'diag.revision': 'Store revision',\n 'diag.snapshot': 'Session snapshots',\n 'diag.cache': 'Store cache',\n 'diag.render': 'Render cache',\n 'diag.hot': 'Hot memory',\n 'diag.retrieval': 'Retrieval \u7D22\u5F15',\n 'diag.qcache': 'Query cache',\n 'diag.lastQuery': '\u6700\u8FD1\u67E5\u8BE2',\n 'diag.snapshotInfo': '\u4F1A\u8BDD\u5FEB\u7167',\n 'inspector.title': 'Hot Memory \u9884\u89C8',\n 'inspector.empty': '\u672C\u5DE5\u4F5C\u533A\u6682\u65E0 Hot Memory',\n 'search.ranked': '\u6309\u76F8\u5173\u6027\u6392\u5E8F',\n 'filter.status': '\u72B6\u6001',\n 'status.active': '\u6D3B\u8DC3',\n 'status.superseded': '\u5DF2\u88AB\u66FF\u4EE3',\n 'status.archived': '\u5DF2\u5F52\u6863',\n 'lifecycle.pin': '\u7F6E\u9876',\n 'lifecycle.unpin': '\u53D6\u6D88\u7F6E\u9876',\n 'lifecycle.edit': '\u7F16\u8F91',\n 'lifecycle.save': '\u4FDD\u5B58\u66F4\u65B0',\n 'lifecycle.supersede': '\u6807\u8BB0\u5DF2\u66FF\u4EE3',\n 'lifecycle.unsupersede': '\u6062\u590D\u6D3B\u8DC3',\n 'lifecycle.archive': '\u5F52\u6863',\n 'lifecycle.restore': '\u6062\u590D',\n },\n en: {\n 'entry.label': 'Memory',\n 'entry.tooltip': 'Open the memory panel (project / global)',\n 'panel.title': 'Memory Memoir',\n 'panel.close': 'Close',\n 'panel.refresh': 'Refresh',\n 'tab.project': 'Project',\n 'tab.global': 'Global',\n 'toolbar.search': 'Search titles and content\u2026',\n 'toolbar.add': 'Add entry',\n 'toolbar.cancel': 'Cancel',\n 'form.section': 'Section',\n 'form.title': 'Title (optional)',\n 'form.content': 'Content',\n 'form.submit': 'Record',\n 'form.placeholder.title': 'A one-line title, e.g. \"Fix pet hover crash\"',\n 'form.placeholder.content': 'What was done, the conclusion, the lesson, or the next step. Concise and actionable.',\n 'empty.project': 'No persistent memory in this project yet',\n 'empty.projectHint': 'Ask the agent to distill via memoir_record, or add an entry manually.',\n 'empty.workspace': 'No project session open',\n 'empty.workspaceHint': 'Open a project session to see its persistent memory here.',\n 'empty.global': 'The global index is empty',\n 'empty.globalHint': 'Entries recorded via memoir_record in any project appear here for cross-project search.',\n 'empty.search': 'No matching entries',\n 'load.failed': 'Failed to load',\n 'record.failed': 'Failed to record',\n 'update.failed': 'Failed to update',\n 'delete.failed': 'Failed to delete',\n 'delete.confirm': 'Delete this entry?',\n 'updated': 'Updated',\n 'entries': 'entries',\n 'session': 'session',\n 'sections.work': 'Work Log',\n 'sections.lessons': 'Lessons Learned',\n 'sections.actions': 'Action Guide',\n 'sections.note': 'Notes',\n 'diag.title': 'Memory Diagnostics',\n 'diag.revision': 'Store revision',\n 'diag.snapshot': 'Session snapshots',\n 'diag.cache': 'Store cache',\n 'diag.render': 'Render cache',\n 'diag.hot': 'Hot memory',\n 'diag.retrieval': 'Retrieval index',\n 'diag.qcache': 'Query cache',\n 'diag.lastQuery': 'Last query',\n 'diag.snapshotInfo': 'Session snapshot',\n 'inspector.title': 'Hot Memory Inspector',\n 'inspector.empty': 'No hot memory in this workspace yet',\n 'search.ranked': 'Ranked by relevance',\n 'filter.status': 'Status',\n 'status.active': 'Active',\n 'status.superseded': 'Superseded',\n 'status.archived': 'Archived',\n 'lifecycle.pin': 'Pin',\n 'lifecycle.unpin': 'Unpin',\n 'lifecycle.edit': 'Edit',\n 'lifecycle.save': 'Save update',\n 'lifecycle.supersede': 'Mark superseded',\n 'lifecycle.unsupersede': 'Restore active',\n 'lifecycle.archive': 'Archive',\n 'lifecycle.restore': 'Restore',\n },\n}\n\n/** Section keys in canonical display order. */\nexport const SECTION_KEYS: string[] = ['work', 'lessons', 'actions', 'note']\n\n/** Minimal document surface for language detection. */\nexport interface DocumentLike {\n documentElement?: { lang?: string }\n}\n\n/** Current UI language: zh when the shell's <html lang> starts with zh. */\nexport function detectLanguage(documentLike: DocumentLike | undefined): Lang {\n const lang = documentLike?.documentElement?.lang\n return typeof lang === 'string' && lang.startsWith('zh') ? 'zh' : 'en'\n}\n\n/** Pure translation lookup: `translate(lang, key)` with en fallback. */\nexport function translate(lang: string, key: string): string {\n const dict = dictionaries[lang as Lang] ?? dictionaries.en\n return Object.prototype.hasOwnProperty.call(dict, key) ? dict[key] : (dictionaries.en[key] ?? key)\n}\n\n/** Bound translator reading the live <html lang> at each call. */\nexport function makeT(documentLike: DocumentLike): (key: string) => string {\n return (key) => translate(detectLanguage(documentLike), key)\n}\n", "/**\n * Panel stylesheet (plain string, injected as <style data-plugin=\"dsh-memoir\">).\n * Class names are literal memoir-* prefixes (no CSS-module hashing) so the\n * plain-DOM sidebar entry and the React panel share them. Dark mode follows\n * the shell's body[data-ds-dark-theme] marker via CSS only.\n */\n\nexport const PANEL_CSS = `\n[data-dsh-memoir-view] {\n display: none;\n position: absolute;\n inset: 0;\n z-index: 20;\n background: var(--bg-panel, #ffffff);\n color: var(--text-primary, #1f2328);\n font-size: 13px;\n}\nhtml[data-dsh-memoir-active] [data-dsh-memoir-view] {\n display: flex;\n flex-direction: column;\n}\nhtml[data-dsh-memoir-active] [data-pane=\"conversation\"] > *:not([data-dsh-memoir-view]) {\n display: none;\n}\n/* Current shell renders the center column as [hash]_centerCol (CSS modules). */\nhtml[data-dsh-memoir-active] [class*=\"centerCol\"] > *:not([data-dsh-memoir-view]) {\n display: none;\n}\n\n.memoir-panel { display: flex; flex-direction: column; height: 100%; overflow: hidden; }\n.memoir-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px 8px; }\n.memoir-title { font-size: 15px; font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.memoir-subtitle { font-size: 11px; opacity: .65; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.memoir-iconbtn {\n border: 1px solid transparent; background: transparent; color: inherit;\n border-radius: 6px; padding: 4px 8px; cursor: pointer; font-size: 12px;\n display: inline-flex; align-items: center; gap: 4px;\n}\n.memoir-iconbtn:hover { background: var(--bg-hover, rgba(0,0,0,.06)); }\n\n.memoir-tabs { display: flex; gap: 4px; padding: 0 14px; border-bottom: 1px solid var(--border, rgba(0,0,0,.1)); }\n.memoir-tab {\n border: none; background: transparent; color: inherit; padding: 7px 12px;\n cursor: pointer; font-size: 13px; border-bottom: 2px solid transparent; opacity: .75;\n}\n.memoir-tab:hover { opacity: 1; }\n.memoir-tab[data-active=\"true\"] { opacity: 1; border-bottom-color: var(--accent, #3b82f6); font-weight: 600; }\n\n.memoir-toolbar { display: flex; gap: 8px; padding: 8px 14px; }\n.memoir-search {\n flex: 1; border: 1px solid var(--border, rgba(0,0,0,.15)); background: transparent;\n color: inherit; border-radius: 6px; padding: 6px 10px; font-size: 13px; outline: none;\n}\n.memoir-search:focus { border-color: var(--accent, #3b82f6); }\n.memoir-primary {\n border: 1px solid transparent; background: var(--accent, #3b82f6); color: #fff;\n border-radius: 6px; padding: 6px 12px; cursor: pointer; font-size: 13px;\n}\n.memoir-primary:hover { filter: brightness(1.05); }\n\n.memoir-body { flex: 1; overflow-y: auto; padding: 4px 14px 16px; }\n.memoir-section-title {\n font-size: 12px; font-weight: 600; opacity: .8; margin: 14px 0 6px;\n display: flex; align-items: center; gap: 6px;\n}\n.memoir-count { font-weight: 400; opacity: .6; }\n.memoir-entry {\n border: 1px solid var(--border, rgba(0,0,0,.08)); border-radius: 8px;\n padding: 8px 10px; margin-bottom: 8px; background: var(--bg-card, rgba(0,0,0,.02));\n}\n.memoir-entry-meta { display: flex; align-items: center; gap: 6px; font-size: 11px; opacity: .7; margin-bottom: 3px; }\n.memoir-chip {\n border-radius: 999px; padding: 0 7px; font-size: 11px; line-height: 18px;\n background: var(--chip-bg, rgba(59,130,246,.12)); color: var(--chip-fg, #2563eb);\n}\n.memoir-entry-title { font-weight: 600; margin-bottom: 2px; }\n.memoir-entry-content { white-space: pre-wrap; word-break: break-word; line-height: 1.5; }\n.memoir-entry-actions { display: flex; gap: 4px; margin: 4px 0; }\n.memoir-status-filter { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; white-space: nowrap; }\n.memoir-status-filter select { max-width: 110px; }\n.memoir-delete {\n float: right; border: none; background: transparent; color: inherit; opacity: .45;\n cursor: pointer; border-radius: 4px; padding: 2px 6px; font-size: 12px;\n}\n.memoir-delete:hover { opacity: 1; background: var(--danger-bg, rgba(220,38,38,.12)); color: var(--danger, #dc2626); }\n\n.memoir-project-card { border: 1px solid var(--border, rgba(0,0,0,.1)); border-radius: 10px; padding: 10px 12px; margin-bottom: 12px; }\n.memoir-project-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 2px; }\n.memoir-project-title { font-weight: 600; font-size: 14px; }\n.memoir-project-path { font-size: 11px; opacity: .6; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.memoir-project-meta { font-size: 11px; opacity: .6; margin-bottom: 6px; }\n\n.memoir-empty { text-align: center; padding: 48px 20px; opacity: .7; }\n.memoir-empty-title { font-size: 14px; font-weight: 600; margin-bottom: 6px; }\n.memoir-empty-hint { font-size: 12px; }\n.memoir-error { color: var(--danger, #dc2626); padding: 8px 14px; font-size: 12px; }\n\n.memoir-form { border: 1px solid var(--border, rgba(0,0,0,.1)); border-radius: 10px; padding: 12px; margin: 4px 14px 14px; }\n.memoir-form-row { display: flex; gap: 8px; margin-bottom: 8px; }\n.memoir-field { flex: 1; display: flex; flex-direction: column; gap: 4px; }\n.memoir-field label { font-size: 11px; opacity: .7; }\n.memoir-field input, .memoir-field select, .memoir-field textarea {\n border: 1px solid var(--border, rgba(0,0,0,.15)); background: transparent; color: inherit;\n border-radius: 6px; padding: 6px 8px; font-size: 13px; font-family: inherit; outline: none;\n}\n.memoir-field textarea { min-height: 72px; resize: vertical; }\n.memoir-form-actions { display: flex; justify-content: flex-end; gap: 8px; }\n\n/* Sidebar entry row (plain DOM, matches the shell's nav rows). */\n.memoir-entry-row {\n display: flex; align-items: center; gap: 8px; width: 100%;\n border: none; background: transparent; color: inherit; cursor: pointer;\n padding: 7px 10px; border-radius: 6px; font-size: 13px; text-align: left;\n}\n.memoir-entry-row:hover { background: var(--bg-hover, rgba(0,0,0,.06)); }\n.memoir-entry-row[data-active=\"true\"] { background: var(--bg-hover, rgba(0,0,0,.08)); }\n.memoir-entry-icon { display: inline-flex; }\n\n/* Collapsed (rail) state: the shell flags the rail with data-sidebar-collapsed\n on the frame root (AppFrame.tsx). The plain-DOM entry gets no wide prop, so\n it mirrors the shell's rail treatment via ancestry \u2014 a centered 36x36\n control box with an 18px icon (matching the New Session / Search rail\n icons) and the text label hidden. The entry is a direct flex child of the\n sidebar root, so it sits inline with the shell's own rail controls. */\n[data-sidebar-collapsed] [data-dsh-memoir-entry].memoir-entry-row {\n justify-content: center;\n width: 36px;\n height: 36px;\n padding: 0;\n margin: 0 0 12px;\n}\n[data-sidebar-collapsed] [data-dsh-memoir-entry] .memoir-entry-icon svg {\n width: 18px;\n height: 18px;\n}\n[data-sidebar-collapsed] [data-dsh-memoir-entry] .memoir-entry-label {\n display: none;\n}\n\n/* v0.4.2 ranked search */\n.memoir-ranked-note { font-size: 11px; opacity: .65; margin: 4px 0 8px; }\n.memoir-score {\n float: right; border-radius: 999px; padding: 0 7px; font-size: 10px; line-height: 16px;\n background: var(--chip-bg, rgba(59,130,246,.12)); color: var(--chip-fg, #2563eb); opacity: .8;\n}\n\n/* v0.4.2 Hot Memory Inspector */\n.memoir-inspector { margin: 0 14px; border-top: 1px solid var(--border, rgba(0,0,0,.1)); padding-top: 8px; }\n.memoir-inspector-body {\n font-size: 11px; opacity: .85; margin: 6px 0 0; padding: 8px 10px;\n border: 1px solid var(--border, rgba(0,0,0,.1)); border-radius: 8px;\n background: var(--bg-card, rgba(0,0,0,.02)); white-space: pre-wrap; word-break: break-word;\n font-family: inherit; max-height: 220px; overflow-y: auto;\n}\n\nbody[data-ds-dark-theme] [data-dsh-memoir-view] {\n background: var(--bg-panel, #16181d);\n color: var(--text-primary, #e6e6e6);\n}\n\n/* Memory Diagnostics (observability strip, v0.4). */\n.memoir-diagnostics { margin: 0 14px 14px; border-top: 1px solid var(--border, rgba(0,0,0,.1)); padding-top: 8px; }\n.memoir-diagnostics-toggle {\n border: none; background: transparent; color: inherit; cursor: pointer;\n font-size: 12px; opacity: .75; padding: 2px 0;\n}\n.memoir-diagnostics-toggle:hover { opacity: 1; }\n.memoir-diagnostics-body { font-size: 11px; opacity: .8; display: flex; flex-direction: column; gap: 3px; margin-top: 6px; }\n`\n", "/**\n * Sidebar entry injection (the dsh-ssh / task-board DOM-level precedent: the\n * shipped sidebar shell exposes no registration slot for these family\n * plugins). The row is plain DOM \u2014 React never manages it \u2014 and self-heals\n * through a MutationObserver when the shell re-renders. It is placed after\n * the family block ([data-dsh-taskboard-entry], [data-dsh-ssh-entry]) so the\n * three sibling entries keep a stable order.\n */\n\nimport type { PanelController } from './controller.js'\n\n/** Stable data attribute identifying the injected entry row. */\nexport const ENTRY_SELECTOR = '[data-dsh-memoir-entry]'\n\n/** Inline icon (matches the shell's 16px nav-icon look): an open-book glyph. */\nconst ICON = '<svg viewBox=\"0 0 16 16\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.3\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><path d=\"M8 3.5c-1.6-1-3.6-1-5.2-.2-.5.3-.8.9-.8 1.4v8.6c0 .5.5.9 1 .7C4.6 13.4 6.4 13.5 8 14.5c1.6-1 3.4-1.1 5-.5.5.2 1-.2 1-.7V4.7c0-.5-.3-1.1-.8-1.4-1.6-.8-3.6-.8-5.2.2z\"/><path d=\"M8 3.5v11\"/></svg>'\n\nconst FAMILY_SELECTOR = '[data-dsh-taskboard-entry], [data-dsh-ssh-entry], [data-dsh-memoir-entry]'\n\n/** Find the sidebar shell root element, or undefined while not yet mounted. */\nfunction sidebarRoot(): HTMLElement | undefined {\n const column = document.querySelector<HTMLElement>('[data-pane=\"sidebar\"], [class*=\"sidebarCol\"]')\n if (column === null) return undefined\n const logoOwner = column.querySelector<HTMLElement>('[class*=\"logoRow\"]')?.parentElement\n return logoOwner ?? (column.firstElementChild as HTMLElement | undefined)\n}\n\n/** The New Session button: nested in the logo row on current shells. */\nfunction newSessionButton(root: HTMLElement): HTMLButtonElement | undefined {\n const nested = root.querySelector<HTMLButtonElement>('button[class*=\"newSession\"]')\n if (nested !== null) return nested\n for (const child of root.children) {\n if (child.tagName === 'BUTTON') return child as HTMLButtonElement\n }\n return undefined\n}\n\n/** Build the entry row (a detached button; inserted once the shell is up). */\nfunction createEntry(controller: PanelController, t: (key: string) => string): HTMLButtonElement {\n const entry = document.createElement('button')\n entry.type = 'button'\n entry.dataset.dshMemoirEntry = ''\n entry.className = 'memoir-entry-row'\n entry.setAttribute('aria-label', t('entry.label'))\n entry.setAttribute('title', t('entry.tooltip'))\n entry.innerHTML = '<span class=\"memoir-entry-icon\">' + ICON + '</span><span class=\"memoir-entry-label\">' + t('entry.label') + '</span>'\n entry.addEventListener('click', () => {\n controller.toggle()\n })\n return entry\n}\n\n/**\n * Re-insert the entry after the sibling family block (stable relative order).\n * @returns true when the entry is placed.\n */\nfunction placeEntry(root: HTMLElement, entry: HTMLButtonElement): boolean {\n const button = newSessionButton(root)\n if (button === undefined) return false\n if (entry.parentElement !== root) {\n const row = button.closest('[class*=\"logoRow\"]')\n const base = row !== null && row.parentElement === root ? row : button\n const family = Array.from(root.children).filter(\n (el): el is HTMLElement => el instanceof HTMLElement && el.matches(FAMILY_SELECTOR),\n )\n // memoir sits after the whole family block.\n const anchor = family.length > 0 ? family[family.length - 1].nextElementSibling : base.nextElementSibling\n root.insertBefore(entry, anchor)\n }\n return true\n}\n\n/**\n * Mount the sidebar entry, waiting for the shell and self-healing on re-renders.\n * @param controller - the panel controller the entry toggles.\n * @param t - the bound translator.\n * @returns disposer removing the entry and its observers.\n */\nexport function mountSidebarEntry(controller: PanelController, t: (key: string) => string): () => void {\n const entry = createEntry(controller, t)\n let root: HTMLElement | undefined\n let placed = false\n\n const tryPlace = (): void => {\n if (placed) return\n if (root !== undefined && !root.isConnected) {\n rootObserver.disconnect()\n root = undefined\n }\n root ??= sidebarRoot()\n if (root === undefined) return\n placed = placeEntry(root, entry)\n if (placed) {\n rootObserver.observe(root, { childList: true, subtree: true })\n waitObserver.disconnect()\n }\n }\n\n const waitObserver = new MutationObserver(() => {\n tryPlace()\n })\n waitObserver.observe(document.body, { childList: true, subtree: true })\n\n const rootObserver = new MutationObserver(() => {\n if (root === undefined || !root.isConnected) {\n placed = false\n tryPlace()\n return\n }\n if (!root.contains(entry)) {\n placed = placeEntry(root, entry)\n }\n })\n\n const unsubscribe = controller.subscribe(() => {\n entry.dataset.active = controller.getSnapshot().panelOpen ? 'true' : undefined\n })\n entry.dataset.active = controller.getSnapshot().panelOpen ? 'true' : undefined\n\n tryPlace()\n\n return () => {\n waitObserver.disconnect()\n rootObserver.disconnect()\n unsubscribe()\n entry.remove()\n }\n}\n", "/**\n * Panel view mounting (the dsh-ssh / task-board center-column precedent).\n * The panel takes over the center column at the DOM level: a container is\n * appended inside the center-column grid item and a stylesheet rule hides\n * the conversation content while the panel is active. Toggling is a data\n * attribute on <html> \u2014 the conversation subtree stays mounted.\n *\n * Center-column selector: the legacy shell exposed\n * [data-pane=\"conversation\"]; current shells render the frame with CSS\n * modules ([hash]_centerCol). Both are matched so the panel mounts on the\n * source-run shell and older builds alike.\n *\n * Cross-plugin coordination: opening this panel evicts the sibling panels'\n * visibility attributes (data-dsh-ssh-active / data-dsh-taskboard-active)\n * and dispatches the shared `dsh-panel-activate` event; it closes itself when\n * a sibling dispatches its own activation.\n */\n\nimport { createRoot, type Root } from 'react-dom/client'\nimport { MemoirPanel } from './panel.jsx'\nimport type { MemoirApi } from './api.js'\nimport type { CwdTracker } from './cwd.js'\nimport type { PanelController } from './controller.js'\n\n/** The injected panel container (kept in the DOM, hidden when inactive). */\nexport const PANEL_VIEW_SELECTOR = '[data-dsh-memoir-view]'\n\nconst CONVERSATION_COLUMN_SELECTOR = '[data-pane=\"conversation\"], [class*=\"centerCol\"]'\nconst ACTIVE_ATTR = 'data-dsh-memoir-active'\n/** Sibling panels' activation attributes, removed when this panel opens. */\nconst SIBLING_ATTRS = ['data-dsh-ssh-active', 'data-dsh-taskboard-active']\n/** Cross-plugin activation event; detail is the activating panel name. */\nconst ACTIVATE_EVENT = 'dsh-panel-activate'\nconst PANEL_NAME = 'memoir'\n\n/** Find the center column, or undefined while the frame is not mounted. */\nfunction conversationColumn(): HTMLElement | undefined {\n return document.querySelector<HTMLElement>(CONVERSATION_COLUMN_SELECTOR) ?? undefined\n}\n\n/**\n * Mount the panel React tree into the center column and bind its visibility\n * to the controller's panelOpen state.\n * @param controller - the panel controller driving the view.\n * @param api - the memoir API client the panel operates through.\n * @param cwdTracker - the active-workspace tracker (useSyncExternalStore-compatible).\n * @param t - the bound translator.\n * @returns disposer unmounting the tree and restoring the column.\n */\nexport function mountPanel(\n controller: PanelController,\n api: MemoirApi,\n cwdTracker: CwdTracker,\n t: (key: string) => string,\n): () => void {\n let root: Root | undefined\n let container: HTMLDivElement | undefined\n\n const ensure = (): void => {\n if (container !== undefined) {\n if (container.isConnected) return\n root?.unmount()\n root = undefined\n container.remove()\n container = undefined\n }\n const column = conversationColumn()\n if (column === undefined) return\n container = document.createElement('div')\n container.dataset.dshMemoirView = ''\n column.appendChild(container)\n root = createRoot(container)\n root.render(<MemoirPanel controller={controller} api={api} cwdTracker={cwdTracker} t={t} />)\n }\n\n // The frame mounts after boot settlement; watch for the column's arrival.\n const waitObserver = new MutationObserver(() => {\n ensure()\n })\n waitObserver.observe(document.body, { childList: true, subtree: true })\n\n const applyActive = (): void => {\n if (controller.getSnapshot().panelOpen) {\n for (const attr of SIBLING_ATTRS) document.documentElement.removeAttribute(attr)\n document.documentElement.setAttribute(ACTIVE_ATTR, '')\n document.dispatchEvent(new CustomEvent(ACTIVATE_EVENT, { detail: PANEL_NAME }))\n } else {\n document.documentElement.removeAttribute(ACTIVE_ATTR)\n }\n }\n\n const onOtherActivate = (event: Event): void => {\n const detail = (event as CustomEvent).detail as string | undefined\n if ((detail === 'ssh' || detail === 'taskboard') && controller.getSnapshot().panelOpen) {\n controller.close()\n }\n }\n\n // Hand the center column back to the conversation on sidebar context clicks.\n const SIDEBAR_ROW_SELECTOR = '[class*=\"sessionRow\"], [class*=\"projectRow\"], [class*=\"searchResultRow\"], [class*=\"searchResultWorkspace\"], [class*=\"newSession\"]'\n const onClickSidebarRow = (event: MouseEvent): void => {\n if (!controller.getSnapshot().panelOpen) return\n const target = event.target as HTMLElement | null\n if (target === null || typeof target.closest !== 'function') return\n if (target.closest(SIDEBAR_ROW_SELECTOR) !== null) controller.close()\n }\n\n document.addEventListener('click', onClickSidebarRow, true)\n document.addEventListener(ACTIVATE_EVENT, onOtherActivate)\n const unsubscribe = controller.subscribe(applyActive)\n applyActive()\n ensure()\n\n return () => {\n document.removeEventListener('click', onClickSidebarRow, true)\n document.removeEventListener(ACTIVATE_EVENT, onOtherActivate)\n waitObserver.disconnect()\n unsubscribe()\n document.documentElement.removeAttribute(ACTIVE_ATTR)\n root?.unmount()\n root = undefined\n container?.remove()\n container = undefined\n }\n}\n", "/**\n * The memoir panel: project / global memory tabs, client-side search,\n * manual record form, per-entry delete. Rendered into the center-column\n * container by mount.tsx; visibility is CSS-driven (html data attribute).\n */\n\nimport { Fragment, useEffect, useMemo, useState, useSyncExternalStore } from 'react'\nimport { SECTION_KEYS } from './i18n.js'\nimport type { MemoirApi, WireDiagnostics, WireEntry, WireHotMemory, WireProject, WireSearchResult } from './api.js'\nimport type { CwdTracker } from './cwd.js'\nimport type { PanelController } from './controller.js'\nimport type { MemoirStatus, SectionKey } from './types.ts'\n\ninterface PanelProps {\n controller: PanelController\n api: MemoirApi\n cwdTracker: CwdTracker\n t: (key: string) => string\n}\n\ntype EntryPatch = {\n section?: SectionKey\n title?: string | null\n content?: string\n importance?: number\n pinned?: boolean\n status?: MemoirStatus\n supersedes?: string[]\n tags?: string[]\n}\n\n/** One entry's meta line: time, section chip, session id tooltip. */\nfunction EntryMeta({ entry, t }: { entry: WireEntry; t: (key: string) => string }) {\n const when = new Date(entry.time)\n const pad = (n: number) => String(n).padStart(2, '0')\n const timeText = `${when.getFullYear()}-${pad(when.getMonth() + 1)}-${pad(when.getDate())} ${pad(when.getHours())}:${pad(when.getMinutes())}`\n return (\n <div className=\"memoir-entry-meta\">\n <span>{timeText}</span>\n <span className=\"memoir-chip\">{t('sections.' + entry.section)}</span>\n {entry.status !== undefined && entry.status !== 'active'\n ? <span className=\"memoir-chip\">{t('status.' + entry.status)}</span>\n : null}\n {entry.pinned === true ? <span className=\"memoir-chip\">{t('lifecycle.pin')}</span> : null}\n {entry.sessionId !== undefined\n ? <span title={`${t('session')}: ${entry.sessionId}`}>{entry.sessionId.slice(0, 12)}</span>\n : null}\n </div>\n )\n}\n\nfunction EntryCard({ entry, t, onDelete, onUpdate, score }: { entry: WireEntry; t: (key: string) => string; onDelete: (entry: WireEntry) => void; onUpdate?: (entry: WireEntry, patch: EntryPatch) => void; score?: number }) {\n const [editing, setEditing] = useState(false)\n const [section, setSection] = useState<SectionKey>(entry.section)\n const [title, setTitle] = useState(entry.title ?? '')\n const [content, setContent] = useState(entry.content)\n const save = () => {\n if (content.trim() === '' || onUpdate === undefined) return\n onUpdate(entry, { section, title: title.trim() === '' ? null : title.trim(), content: content.trim() })\n setEditing(false)\n }\n return (\n <div className=\"memoir-entry\">\n <button type=\"button\" className=\"memoir-delete\" title={t('delete.confirm')} onClick={() => onDelete(entry)}>\u00D7</button>\n {onUpdate !== undefined\n ? <div className=\"memoir-entry-actions\">\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => setEditing((value) => !value)}>{editing ? t('toolbar.cancel') : t('lifecycle.edit')}</button>\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => onUpdate(entry, { pinned: entry.pinned !== true })}>{entry.pinned === true ? t('lifecycle.unpin') : t('lifecycle.pin')}</button>\n {(entry.status ?? 'active') === 'active'\n ? <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => onUpdate(entry, { status: 'superseded' })}>{t('lifecycle.supersede')}</button>\n : <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => onUpdate(entry, { status: 'active' })}>{t('lifecycle.unsupersede')}</button>}\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => onUpdate(entry, { status: entry.status === 'archived' ? 'active' : 'archived' })}>{entry.status === 'archived' ? t('lifecycle.restore') : t('lifecycle.archive')}</button>\n </div>\n : null}\n <EntryMeta entry={entry} t={t} />\n {score !== undefined\n ? <span className=\"memoir-score\" title={t('search.ranked')}>{score.toFixed(3)}</span>\n : null}\n {editing\n ? <div className=\"memoir-form memoir-entry-editor\">\n <div className=\"memoir-field\">\n <label>{t('form.section')}</label>\n <select value={section} onChange={(e) => setSection(e.target.value as SectionKey)}>\n {SECTION_KEYS.map((key) => <option key={key} value={key}>{t('sections.' + key)}</option>)}\n </select>\n </div>\n <div className=\"memoir-field\">\n <label>{t('form.title')}</label>\n <input value={title} onChange={(e) => setTitle(e.target.value)} />\n </div>\n <div className=\"memoir-field\">\n <label>{t('form.content')}</label>\n <textarea value={content} onChange={(e) => setContent(e.target.value)} />\n </div>\n <div className=\"memoir-form-actions\">\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={() => setEditing(false)}>{t('toolbar.cancel')}</button>\n <button type=\"button\" className=\"memoir-primary\" onClick={save}>{t('lifecycle.save')}</button>\n </div>\n </div>\n : <>\n {entry.title !== undefined ? <div className=\"memoir-entry-title\">{entry.title}</div> : null}\n <div className=\"memoir-entry-content\">{entry.content}</div>\n </>}\n </div>\n )\n}\n\n/** Sectioned entry list in canonical order. */\nfunction SectionedEntries({ entries, t, onDelete, onUpdate }: { entries: WireEntry[]; t: (key: string) => string; onDelete: (entry: WireEntry) => void; onUpdate?: (entry: WireEntry, patch: EntryPatch) => void }) {\n const groups = useMemo(\n () => SECTION_KEYS.map((key) => ({ key, entries: entries.filter((e) => e.section === key) })).filter((g) => g.entries.length > 0),\n [entries],\n )\n if (groups.length === 0) {\n return (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.search')}</div>\n </div>\n )\n }\n return (\n <Fragment>\n {groups.map((group) => (\n <Fragment key={group.key}>\n <div className=\"memoir-section-title\">\n {t('sections.' + group.key)}\n <span className=\"memoir-count\">({group.entries.length})</span>\n </div>\n {group.entries.map((entry) => (\n <EntryCard key={entry.id} entry={entry} t={t} onDelete={onDelete} onUpdate={onUpdate} />\n ))}\n </Fragment>\n ))}\n </Fragment>\n )\n}\n\n/**\n * Ranked search results (v0.4.2): the host RetrievalEngine order, flat for\n * the project tab or grouped by project for the global tab. Scores are\n * shown as chips so the ranking is inspectable.\n */\nfunction RankedResults({ results, pending, grouped, t, onDelete, onUpdate }: {\n results: WireSearchResult[]\n pending: boolean\n grouped: boolean\n t: (key: string) => string\n onDelete: (entry: WireEntry, path: string) => void\n onUpdate?: (entry: WireEntry, path: string, patch: EntryPatch) => void\n}) {\n if (pending) return <div className=\"memoir-empty\">\u2026</div>\n if (results.length === 0) {\n return (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.search')}</div>\n <div className=\"memoir-empty-hint\">{t('search.ranked')}</div>\n </div>\n )\n }\n if (!grouped) {\n return (\n <Fragment>\n <div className=\"memoir-ranked-note\">{t('search.ranked')}</div>\n {results.map((r) => (\n <EntryCard key={r.entry.id} entry={r.entry} score={r.score} t={t} onDelete={(entry) => onDelete(entry, r.projectPath)} onUpdate={(entry, patch) => onUpdate?.(entry, r.projectPath, patch)} />\n ))}\n </Fragment>\n )\n }\n const groups = new Map<string, WireSearchResult[]>()\n for (const result of results) {\n const bucket = groups.get(result.projectPath) ?? []\n bucket.push(result)\n groups.set(result.projectPath, bucket)\n }\n return (\n <Fragment>\n {[...groups.entries()].map(([path, bucket]) => (\n <div className=\"memoir-project-card\" key={path}>\n <div className=\"memoir-project-head\">\n <span className=\"memoir-project-title\">{path.split('/').filter(Boolean).pop() || path}</span>\n <span className=\"memoir-project-path\">{path}</span>\n </div>\n {bucket.map((r) => (\n <EntryCard key={r.entry.id} entry={r.entry} score={r.score} t={t} onDelete={(entry) => onDelete(entry, path)} onUpdate={(entry, patch) => onUpdate?.(entry, path, patch)} />\n ))}\n </div>\n ))}\n </Fragment>\n )\n}\n\nfunction AddForm({ t, onSubmit, onCancel }: { t: (key: string) => string; onSubmit: (payload: { section: SectionKey; title?: string; content: string }) => void; onCancel: () => void }) {\n const [section, setSection] = useState<SectionKey>('lessons')\n const [title, setTitle] = useState('')\n const [content, setContent] = useState('')\n const submit = () => {\n if (content.trim() === '') return\n onSubmit({ section, title: title.trim() === '' ? undefined : title.trim(), content: content.trim() })\n }\n return (\n <div className=\"memoir-form\">\n <div className=\"memoir-form-row\">\n <div className=\"memoir-field\">\n <label>{t('form.section')}</label>\n <select value={section} onChange={(e) => setSection(e.target.value as SectionKey)}>\n {SECTION_KEYS.map((key) => (\n <option key={key} value={key}>{t('sections.' + key)}</option>\n ))}\n </select>\n </div>\n <div className=\"memoir-field\">\n <label>{t('form.title')}</label>\n <input value={title} placeholder={t('form.placeholder.title')} onChange={(e) => setTitle(e.target.value)} />\n </div>\n </div>\n <div className=\"memoir-field\">\n <label>{t('form.content')}</label>\n <textarea value={content} placeholder={t('form.placeholder.content')} onChange={(e) => setContent(e.target.value)} />\n </div>\n <div className=\"memoir-form-actions\">\n <button type=\"button\" className=\"memoir-iconbtn\" onClick={onCancel}>{t('toolbar.cancel')}</button>\n <button type=\"button\" className=\"memoir-primary\" onClick={submit}>{t('form.submit')}</button>\n </div>\n </div>\n )\n}\n\nexport function MemoirPanel({ controller, api, cwdTracker, t }: PanelProps) {\n const cwd = useSyncExternalStore(cwdTracker.subscribe, cwdTracker.getSnapshot)\n const [tab, setTab] = useState<'project' | 'global'>('project')\n const [query, setQuery] = useState('')\n const [statusFilter, setStatusFilter] = useState<MemoirStatus | 'all'>('active')\n const [formOpen, setFormOpen] = useState(false)\n const [refreshKey, setRefreshKey] = useState(0)\n const [project, setProject] = useState<WireProject | null>(null)\n const [projects, setProjects] = useState<WireProject[]>([])\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [busy, setBusy] = useState(false)\n const [diag, setDiag] = useState<WireDiagnostics | null>(null)\n const [diagOpen, setDiagOpen] = useState(false)\n const [searchResults, setSearchResults] = useState<WireSearchResult[] | null>(null)\n const [hotMemory, setHotMemory] = useState<WireHotMemory | null>(null)\n const [inspectorOpen, setInspectorOpen] = useState(false)\n\n const q = query.trim().toLowerCase()\n\n useEffect(() => {\n let cancelled = false\n setError(null)\n if (tab === 'project') {\n if (cwd === '') {\n setProject(null)\n return undefined\n }\n setLoading(true)\n api.project(cwd, { status: statusFilter })\n .then((value) => { if (!cancelled) setProject(value.project) })\n .catch((e: Error) => { if (!cancelled) setError(e.message) })\n .finally(() => { if (!cancelled) setLoading(false) })\n } else {\n setLoading(true)\n api.global({ status: statusFilter })\n .then((value) => { if (!cancelled) setProjects(value.projects) })\n .catch((e: Error) => { if (!cancelled) setError(e.message) })\n .finally(() => { if (!cancelled) setLoading(false) })\n }\n // Diagnostics follow the active workspace (observability, roadmap \u00A74).\n api.diagnostics(cwd === '' ? undefined : cwd)\n .then((value) => { if (!cancelled) setDiag(value) })\n .catch(() => { if (!cancelled) setDiag(null) })\n // Hot-memory inspector preview (what the next session inherits).\n if (cwd === '') {\n setHotMemory(null)\n } else {\n api.hotMemory(cwd)\n .then((value) => { if (!cancelled) setHotMemory(value.hotMemory) })\n .catch(() => { if (!cancelled) setHotMemory(null) })\n }\n return () => { cancelled = true }\n }, [tab, cwd, statusFilter, refreshKey])\n\n // v0.4.2: a non-empty query searches through the host RetrievalEngine \u2014\n // the same BM25 ranking memoir_read uses. Debounced; empty query resets.\n useEffect(() => {\n if (q === '') {\n setSearchResults(null)\n return undefined\n }\n let cancelled = false\n const timer = setTimeout(() => {\n const scope = tab === 'project' ? 'project' : 'global'\n api.search({ scope, path: tab === 'project' && cwd !== '' ? cwd : undefined, query: q, status: statusFilter })\n .then((value) => { if (!cancelled) setSearchResults(value.results) })\n .catch((e: Error) => {\n if (!cancelled) {\n setSearchResults([])\n setError(e.message)\n }\n })\n }, 200)\n return () => { cancelled = true; clearTimeout(timer) }\n }, [q, tab, cwd, statusFilter, refreshKey])\n\n const filterEntries = (entries: WireEntry[]) =>\n q === '' ? entries : entries.filter((e) => `${e.title ?? ''} ${e.content}`.toLowerCase().includes(q))\n\n const reload = () => setRefreshKey((k) => k + 1)\n\n const onDelete = (entry: WireEntry, entryPath: string) => {\n if (!window.confirm(t('delete.confirm'))) return\n setBusy(true)\n api.remove({ path: entryPath, id: entry.id })\n .then(() => reload())\n .catch((e: Error) => setError(`${t('delete.failed')}: ${e.message}`))\n .finally(() => setBusy(false))\n }\n\n const onRecord = (payload: { section: SectionKey; title?: string; content: string }) => {\n setBusy(true)\n api.record({ path: cwd, ...payload })\n .then(() => {\n setFormOpen(false)\n reload()\n })\n .catch((e: Error) => setError(`${t('record.failed')}: ${e.message}`))\n .finally(() => setBusy(false))\n }\n\n const onUpdate = (entry: WireEntry, entryPath: string, patch: EntryPatch) => {\n setBusy(true)\n api.update({ path: entryPath, id: entry.id, ...patch })\n .then(() => reload())\n .catch((e: Error) => setError(`${t('update.failed')}: ${e.message}`))\n .finally(() => setBusy(false))\n }\n\n const projectEntries = project === null ? [] : filterEntries(project.entries)\n\n return (\n <div className=\"memoir-panel\">\n <div className=\"memoir-header\">\n <div className=\"memoir-title\">\n {t('panel.title')}\n <div className=\"memoir-subtitle\">{tab === 'project' ? (cwd === '' ? t('empty.workspace') : cwd) : t('tab.global')}</div>\n </div>\n <button type=\"button\" className=\"memoir-iconbtn\" title={t('panel.refresh')} onClick={reload}>\u27F3</button>\n <button type=\"button\" className=\"memoir-iconbtn\" title={t('panel.close')} onClick={() => controller.close()}>\u00D7</button>\n </div>\n <div className=\"memoir-tabs\">\n <button type=\"button\" className=\"memoir-tab\" data-active={tab === 'project' ? 'true' : undefined} onClick={() => setTab('project')}>{t('tab.project')}</button>\n <button type=\"button\" className=\"memoir-tab\" data-active={tab === 'global' ? 'true' : undefined} onClick={() => setTab('global')}>{t('tab.global')}</button>\n </div>\n <div className=\"memoir-toolbar\">\n <input className=\"memoir-search\" placeholder={t('toolbar.search')} value={query} onChange={(e) => setQuery(e.target.value)} />\n <label className=\"memoir-status-filter\">\n <span>{t('filter.status')}</span>\n <select value={statusFilter} onChange={(e) => setStatusFilter(e.target.value as MemoirStatus | 'all')}>\n <option value=\"active\">{t('status.active')}</option>\n <option value=\"all\">{t('filter.status')}: all</option>\n <option value=\"superseded\">{t('status.superseded')}</option>\n <option value=\"archived\">{t('status.archived')}</option>\n </select>\n </label>\n {tab === 'project' && cwd !== ''\n ? <button type=\"button\" className=\"memoir-primary\" onClick={() => setFormOpen((v) => !v)}>{formOpen ? t('toolbar.cancel') : t('toolbar.add')}</button>\n : null}\n </div>\n {formOpen && tab === 'project' && cwd !== ''\n ? <AddForm t={t} onSubmit={onRecord} onCancel={() => setFormOpen(false)} />\n : null}\n {error !== null ? <div className=\"memoir-error\">{error}</div> : null}\n <div className=\"memoir-body\">\n {loading\n ? <div className=\"memoir-empty\">\u2026</div>\n : q !== ''\n ? (\n <RankedResults\n results={searchResults ?? []}\n pending={searchResults === null}\n grouped={tab === 'global'}\n t={t}\n onDelete={(entry, path) => onDelete(entry, path)}\n onUpdate={onUpdate}\n />\n )\n : tab === 'project'\n ? cwd === ''\n ? (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.workspace')}</div>\n <div className=\"memoir-empty-hint\">{t('empty.workspaceHint')}</div>\n </div>\n )\n : projectEntries.length === 0\n ? (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.project')}</div>\n <div className=\"memoir-empty-hint\">{t('empty.projectHint')}</div>\n </div>\n )\n : <SectionedEntries entries={projectEntries} t={t} onDelete={(entry) => onDelete(entry, project?.path ?? cwd)} onUpdate={(entry, patch) => onUpdate(entry, project?.path ?? cwd, patch)} />\n : projects.length === 0\n ? (\n <div className=\"memoir-empty\">\n <div className=\"memoir-empty-title\">{t('empty.global')}</div>\n <div className=\"memoir-empty-hint\">{t('empty.globalHint')}</div>\n </div>\n )\n : projects.map((p) => {\n const entries = filterEntries(p.entries)\n if (entries.length === 0) return null\n const when = new Date(p.updatedAt)\n return (\n <div className=\"memoir-project-card\" key={p.key}>\n <div className=\"memoir-project-head\">\n <span className=\"memoir-project-title\">{p.title}</span>\n <span className=\"memoir-project-path\">{p.path}</span>\n </div>\n <div className=\"memoir-project-meta\">\n {`${t('updated')} ${when.toISOString().slice(0, 16).replace('T', ' ')} \u00B7 ${entries.length} ${t('entries')}`}\n </div>\n <SectionedEntries entries={entries} t={t} onDelete={(entry) => onDelete(entry, p.path)} onUpdate={(entry, patch) => onUpdate(entry, p.path, patch)} />\n </div>\n )\n })}\n </div>\n {busy ? <div className=\"memoir-empty\">\u2026</div> : null}\n <div className=\"memoir-inspector\">\n <button type=\"button\" className=\"memoir-diagnostics-toggle\" onClick={() => setInspectorOpen((v) => !v)}>\n {t('inspector.title')} {inspectorOpen ? '\u25BE' : '\u25B8'}\n </button>\n {inspectorOpen\n ? hotMemory === null || hotMemory.text === ''\n ? <div className=\"memoir-inspector-body\">{t('inspector.empty')}</div>\n : <pre className=\"memoir-inspector-body\">{hotMemory.text}</pre>\n : null}\n </div>\n <div className=\"memoir-diagnostics\">\n <button type=\"button\" className=\"memoir-diagnostics-toggle\" onClick={() => setDiagOpen((v) => !v)}>\n {t('diag.title')} {diagOpen ? '\u25BE' : '\u25B8'}\n </button>\n {diagOpen && diag !== null\n ? (\n <div className=\"memoir-diagnostics-body\">\n <div>{t('diag.revision')}: {diag.storeRevision} \u00B7 {t('diag.snapshot')}: {diag.snapshotCount}/{diag.snapshotMax}</div>\n <div>{t('diag.cache')}: {diag.cache.hits}/{diag.cache.loads} \u547D\u4E2D ({Math.round(diag.cache.hitRate * 100)}%) \u00B7 {t('diag.render')}: {Math.round(diag.cache.renderHitRate * 100)}%</div>\n {diag.hotMemory !== null\n ? <div>{t('diag.hot')}: {diag.hotMemory.selected}/{diag.hotMemory.total} \u6761 \u00B7 ~{diag.hotMemory.estimatedTokens} tokens\uFF08\u9884\u7B97 {diag.config.hotMemoryTokens}/{diag.config.hotMemoryMaxTokens}\uFF09</div>\n : null}\n <div>{t('diag.retrieval')}: {diag.retrieval.index === null ? '\u2014' : `${diag.retrieval.index.docs} docs \u00B7 ${diag.retrieval.index.terms} terms \u00B7 epoch ${diag.retrieval.index.epoch}`}</div>\n <div>{t('diag.qcache')}: {diag.retrieval.cache.hits} hits / {diag.retrieval.cache.misses} misses ({Math.round(diag.retrieval.cache.hitRate * 100)}%) \u00B7 {diag.retrieval.cache.size}/{diag.retrieval.cache.capacity} \u00B7 {diag.retrieval.cache.evictions} evicted</div>\n {diag.retrieval.lastQuery !== null\n ? <div>{t('diag.lastQuery')}: {diag.retrieval.lastQuery.latencyMs.toFixed(1)} ms \u00B7 {diag.retrieval.lastQuery.returned}/{diag.retrieval.lastQuery.candidates} returned</div>\n : null}\n {diag.snapshot !== null\n ? <div>{t('diag.snapshotInfo')}: {diag.snapshot.hash} \u00B7 rev {diag.snapshot.storeRevision}</div>\n : null}\n </div>\n )\n : null}\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["query", "import_jsx_runtime"]
|
|
7
7
|
}
|
package/lib/index.d.ts
CHANGED
|
@@ -36,6 +36,12 @@ export interface Config {
|
|
|
36
36
|
announceToAgent?: boolean;
|
|
37
37
|
/** When true (default), turns with real work are auto-distilled at turn end. */
|
|
38
38
|
autoDistill?: boolean;
|
|
39
|
+
/** Remind after every N eligible worked turns per agent (default 1, minimum 1). */
|
|
40
|
+
autoDistillEvery?: number;
|
|
41
|
+
/** Minimum minutes between successful reminders per agent (default 0). */
|
|
42
|
+
autoDistillCooldownMin?: number;
|
|
43
|
+
/** Minimum tool calls required on the triggering turn (default 1, minimum 1). */
|
|
44
|
+
autoDistillMinTools?: number;
|
|
39
45
|
/** Hot-memory soft target tokens (default 900). */
|
|
40
46
|
hotMemoryTokens?: number;
|
|
41
47
|
/** Hot-memory hard ceiling tokens (default 1200). */
|
|
@@ -59,6 +65,9 @@ export interface ResolvedConfig {
|
|
|
59
65
|
enabled: boolean;
|
|
60
66
|
announceToAgent: boolean;
|
|
61
67
|
autoDistill: boolean;
|
|
68
|
+
autoDistillEvery: number;
|
|
69
|
+
autoDistillCooldownMin: number;
|
|
70
|
+
autoDistillMinTools: number;
|
|
62
71
|
budget: MemoryBudget;
|
|
63
72
|
readDefaultLimit: number;
|
|
64
73
|
readMaxLimit: number;
|
package/lib/index.js
CHANGED
|
@@ -36,6 +36,9 @@ const SECTION_ORDER = 150;
|
|
|
36
36
|
const DEFAULT_ENABLED = true;
|
|
37
37
|
const DEFAULT_ANNOUNCE = true;
|
|
38
38
|
const DEFAULT_AUTO_DISTILL = true;
|
|
39
|
+
const DEFAULT_AUTO_DISTILL_EVERY = 1;
|
|
40
|
+
const DEFAULT_AUTO_DISTILL_COOLDOWN_MIN = 0;
|
|
41
|
+
const DEFAULT_AUTO_DISTILL_MIN_TOOLS = 1;
|
|
39
42
|
/** Model-facing announcement: minimal by design (roadmap §2.6) — parameter
|
|
40
43
|
* details live in the tool schemas, not in every prompt. */
|
|
41
44
|
export const MEMOIR_GUIDANCE = 'dsh-memoir 提供项目持久记忆。下方仅注入本项目高优先级记忆;' +
|
|
@@ -47,10 +50,15 @@ function resolveConfig(config) {
|
|
|
47
50
|
const hardMax = Math.max(target, Math.floor(config?.hotMemoryMaxTokens ?? DEFAULT_MEMORY_BUDGET.hardMaxTokens));
|
|
48
51
|
const readDefaultLimit = Math.max(1, Math.floor(config?.readDefaultLimit ?? 8));
|
|
49
52
|
const readMaxLimit = Math.max(readDefaultLimit, Math.floor(config?.readMaxLimit ?? 30));
|
|
53
|
+
const integerAtLeast = (value, fallback, minimum) => typeof value === 'number' && Number.isFinite(value) ? Math.max(minimum, Math.floor(value)) : fallback;
|
|
54
|
+
const numberAtLeast = (value, fallback, minimum) => typeof value === 'number' && Number.isFinite(value) ? Math.max(minimum, value) : fallback;
|
|
50
55
|
return {
|
|
51
56
|
enabled: config?.enabled ?? DEFAULT_ENABLED,
|
|
52
57
|
announceToAgent: config?.announceToAgent ?? DEFAULT_ANNOUNCE,
|
|
53
58
|
autoDistill: config?.autoDistill ?? DEFAULT_AUTO_DISTILL,
|
|
59
|
+
autoDistillEvery: integerAtLeast(config?.autoDistillEvery, DEFAULT_AUTO_DISTILL_EVERY, 1),
|
|
60
|
+
autoDistillCooldownMin: numberAtLeast(config?.autoDistillCooldownMin, DEFAULT_AUTO_DISTILL_COOLDOWN_MIN, 0),
|
|
61
|
+
autoDistillMinTools: integerAtLeast(config?.autoDistillMinTools, DEFAULT_AUTO_DISTILL_MIN_TOOLS, 1),
|
|
54
62
|
budget: { targetTokens: target, hardMaxTokens: hardMax },
|
|
55
63
|
readDefaultLimit,
|
|
56
64
|
readMaxLimit,
|
|
@@ -155,6 +163,9 @@ export function apply(ctx, config) {
|
|
|
155
163
|
storeRevision: latest.storeRevision,
|
|
156
164
|
},
|
|
157
165
|
config: {
|
|
166
|
+
autoDistillEvery: value.autoDistillEvery,
|
|
167
|
+
autoDistillCooldownMin: value.autoDistillCooldownMin,
|
|
168
|
+
autoDistillMinTools: value.autoDistillMinTools,
|
|
158
169
|
hotMemoryTokens: value.budget.targetTokens,
|
|
159
170
|
hotMemoryMaxTokens: value.budget.hardMaxTokens,
|
|
160
171
|
readDefaultLimit: value.readDefaultLimit,
|
|
@@ -186,7 +197,12 @@ export function apply(ctx, config) {
|
|
|
186
197
|
};
|
|
187
198
|
}, 'dsh-memoir: routes');
|
|
188
199
|
if (value.autoDistill) {
|
|
189
|
-
ctx.effect(() => installAutoDistill(autoDistillWire(ctx), {
|
|
200
|
+
ctx.effect(() => installAutoDistill(autoDistillWire(ctx), {
|
|
201
|
+
enabled: () => value.autoDistill,
|
|
202
|
+
every: value.autoDistillEvery,
|
|
203
|
+
cooldownMin: value.autoDistillCooldownMin,
|
|
204
|
+
minTools: value.autoDistillMinTools,
|
|
205
|
+
}), 'dsh-memoir: auto-distill');
|
|
190
206
|
}
|
|
191
207
|
if (value.announceToAgent) {
|
|
192
208
|
ctx.systemPrompt.section({
|
package/lib/routes.d.ts
CHANGED
|
@@ -33,6 +33,9 @@ export interface DiagnosticsValue {
|
|
|
33
33
|
storeRevision: number;
|
|
34
34
|
} | null;
|
|
35
35
|
config: {
|
|
36
|
+
autoDistillEvery: number;
|
|
37
|
+
autoDistillCooldownMin: number;
|
|
38
|
+
autoDistillMinTools: number;
|
|
36
39
|
hotMemoryTokens: number;
|
|
37
40
|
hotMemoryMaxTokens: number;
|
|
38
41
|
readDefaultLimit: number;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-memoir",
|
|
3
3
|
"description": "Cache-aware local project memory for DeepSeek Harness (DSH): agents distill session work, lessons and next actions into a local JSON store (~/.dsh/dsh-memoir.json, source of truth) with a per-project PROJECT_MEMORY.md projection; bounded hot-memory (ranked, session-frozen) is auto-injected into the system prompt while long-tail history is served by ranked BM25 retrieval (memoir_read, no embeddings / no external services); a web-GUI 'Memory' panel offers ranked search, hot-memory inspection and retrieval diagnostics over /api/dsh-memoir routes. TypeScript, mounted via the dsh.bundle.patch manifest, no dsh source changes.",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"@deepseek-ai/dsh-llm": "^0.1.
|
|
29
|
-
"@deepseek-ai/dsh-tools": "^0.1.
|
|
28
|
+
"@deepseek-ai/dsh-llm": "^0.1.1-rc.2",
|
|
29
|
+
"@deepseek-ai/dsh-tools": "^0.1.1-rc.2"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
33
|
-
"@deepseek-ai/dsh-agent": "0.1.
|
|
34
|
-
"@deepseek-ai/dsh-client-runtime": "0.1.
|
|
35
|
-
"@deepseek-ai/dsh-host-webserver": "0.1.
|
|
36
|
-
"@deepseek-ai/dsh-llm": "0.1.
|
|
37
|
-
"@deepseek-ai/dsh-session": "0.1.
|
|
38
|
-
"@deepseek-ai/dsh-system-prompt": "0.1.
|
|
39
|
-
"@deepseek-ai/dsh-tools": "0.1.
|
|
33
|
+
"@deepseek-ai/dsh-agent": "0.1.1-rc.2",
|
|
34
|
+
"@deepseek-ai/dsh-client-runtime": "0.1.1-rc.2",
|
|
35
|
+
"@deepseek-ai/dsh-host-webserver": "0.1.1-rc.2",
|
|
36
|
+
"@deepseek-ai/dsh-llm": "0.1.1-rc.2",
|
|
37
|
+
"@deepseek-ai/dsh-session": "0.1.1-rc.2",
|
|
38
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.1-rc.2",
|
|
39
|
+
"@deepseek-ai/dsh-tools": "0.1.1-rc.2",
|
|
40
40
|
"@types/node": "^22.20.1",
|
|
41
41
|
"@types/react": "~18.3.31",
|
|
42
42
|
"@types/react-dom": "^18.3.7",
|