dsh-memoir 0.8.0 → 0.8.1
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 -13
- package/README.md +11 -13
- package/cordis.patch.yml +1 -1
- package/lib/activity.d.ts +30 -0
- package/lib/activity.js +38 -0
- package/lib/autodistill.d.ts +18 -14
- package/lib/autodistill.js +22 -13
- package/lib/client.js +69 -26
- package/lib/client.js.map +2 -2
- package/lib/index.js +50 -3
- package/lib/tools.d.ts +12 -6
- package/lib/tools.js +26 -22
- package/package.json +21 -18
package/README.en.md
CHANGED
|
@@ -9,16 +9,16 @@
|
|
|
9
9
|
|
|
10
10
|
**A local-first, cross-session project-memory plugin for DeepSeek Harness (DSH).** It persists an agent's confirmed work, lessons, and next actions, injects bounded cache-friendly Hot Memory into new sessions, and retrieves long-tail history through local BM25 ranking.
|
|
11
11
|
|
|
12
|
-
No embeddings, vector database, or cloud memory service. The npm package has zero bundled runtime dependencies; DSH peers are supplied by the host.
|
|
12
|
+
No embeddings, vector database, or cloud memory service. The npm package has zero bundled runtime dependencies; DSH and Zod 4 peers are supplied by the host environment; Zod validates session projections and is not bundled.
|
|
13
13
|
|
|
14
14
|
> [!IMPORTANT]
|
|
15
|
-
> **0.8.
|
|
15
|
+
> **0.8.1 requires DSH `>=0.1.7-rc.1 <0.1.8-0`**, with SDKs pinned to `0.1.7-rc.2`. It refines unskinned Desktop settings backgrounds, spacing and cards, adds actual-save diagnostics, and adopts public Session projections. Keep `dsh-memoir@0.7.1` on DSH 0.1.5.
|
|
16
16
|
>
|
|
17
17
|
> `dsh-memoir@0.7.1` fixes lost session snapshots after restart or RAM eviction (#10), supporting DSH **0.1.5-rc.1 / rc.2**. It requires `>=0.1.5-rc.1 <0.1.6-0`; check your host first. Keep `0.6.2` on DSH 0.1.2, or `0.5.6` on DSH 0.1.1-rc.2; those older lines do not include this fix.
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install --global @deepseek-ai/dsh@0.1.7-rc.
|
|
21
|
-
dsh plugin --profile web add dsh-memoir@0.8.
|
|
20
|
+
npm install --global @deepseek-ai/dsh@0.1.7-rc.2
|
|
21
|
+
dsh plugin --profile web add dsh-memoir@0.8.1
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
Restart `dsh web`. Memory remains local and is not automatically deleted when the plugin is updated or removed.
|
|
@@ -88,9 +88,9 @@ Similar-memory governance starts with BM25 candidates, then combines title simil
|
|
|
88
88
|
|
|
89
89
|
## Automatic distillation
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
0.8.1 diagnostics distinguish reminders, persistence, failures, cancellation, unresolved similarity, and unavailable receipts. Only persisted `memoir_record` / `memoir_update` operations suppress reminders; failed calls or unresolved candidates do not count as saves. Host cancellation may follow persistence, so counters can overlap. Correlation with a reminder proves neither causation nor semantic correctness. GUI writes are excluded from Agent-tool counters.
|
|
92
92
|
|
|
93
|
-
0.8.
|
|
93
|
+
0.8.1 uses public `sessionProjections` to reconstruct current-turn activity from logs and checkpoints without deprecated `snapshotEvents()` reads. Each session retains at most 4096 current-turn call IDs, without arguments or content; evicted IDs degrade to session-only provenance. Gate state is capped at 1024 agents and cleared on disposal. Legacy writes have no receipts: a historical call alone cannot prove persistence. BM25 remains lexical retrieval, not guaranteed cross-language semantic matching.
|
|
94
94
|
|
|
95
95
|
Automatic distillation is an observable agent turn-end reminder, not silent background scraping of every chat. The default `1 / 0 / 1` means every eligible worked turn, no extra cooldown, and at least one tool call.
|
|
96
96
|
|
|
@@ -152,11 +152,11 @@ Installing into a DSH-alpha `web` profile registers a native Memory Conversation
|
|
|
152
152
|
|
|
153
153
|
| Channel | DSH baseline | Installation | Status |
|
|
154
154
|
| --- | --- | --- | --- |
|
|
155
|
-
| npm `latest` (`0.8.
|
|
155
|
+
| npm `latest` (`0.8.1`) | `>=0.1.7-rc.1 <0.1.8-0` | `dsh plugin --profile web add dsh-memoir@0.8.1` | DSH 0.1.7 line |
|
|
156
156
|
| pinned npm `0.7.1` | `>=0.1.5-rc.1 <0.1.6-0` | `dsh plugin --profile web add dsh-memoir@0.7.1` | Legacy 0.1.5 line |
|
|
157
157
|
| pinned npm `0.6.2` | `>=0.1.2-alpha.2 <0.1.3` | `dsh plugin --profile web add dsh-memoir@0.6.2` | Legacy 0.1.2 line |
|
|
158
158
|
| pinned npm `0.5.6` | `0.1.1-rc.2` | `dsh plugin --profile web add dsh-memoir@0.5.6` | rc2 compatibility line |
|
|
159
|
-
| Source `v0.8.
|
|
159
|
+
| Source `v0.8.1` | `>=0.1.7-rc.1 <0.1.8-0` | local build + `link:` | Development; not compatible with legacy 0.1.5 / 0.1.6 |
|
|
160
160
|
|
|
161
161
|
Node.js `^22.19.0 || >=24.0.0` is required. 0.7.1 keeps the native `conversation.view` / `settings.section` slots and `snapshotEvents()`. DSH 0.1.5 uses Session log V3, independent of Memoir store v4 / settings v3. Back up DSH_HOME before upgrading DSH; migrated sessions are not guaranteed readable by older hosts. This Memoir release neither migrates nor resets memory and retains frozen session snapshots without enabling new dynamic-prompt behavior.
|
|
162
162
|
|
|
@@ -176,7 +176,7 @@ dsh plugin --profile web add "link:/absolute/path/dsh-memoir"
|
|
|
176
176
|
|
|
177
177
|
</details>
|
|
178
178
|
|
|
179
|
-
0.8.
|
|
179
|
+
0.8.1 uses native `uiWorkspace`, `conversation.view` / `settings.section`, and the Session V4 producer-owned distillation source. Links open sessions and turn IDs remain copyable; no global DOM turn scrolling is used. Settings inherit the host background and cards use theme layers while preserving skins and independent scrolling. Store v4 / settings v3 / snapshot v1 are unchanged. Back up DSH_HOME before upgrading: host session migration is separate from plugin memory storage.
|
|
180
180
|
|
|
181
181
|
## Storage, privacy, and security boundaries
|
|
182
182
|
|
|
@@ -229,9 +229,7 @@ v0.5.6 benchmark (Node 24.19, 900/1200-token budget; full data in [`bench/report
|
|
|
229
229
|
|
|
230
230
|
Numbers vary by machine and corpus. The important properties are that injection remains bounded and the cache-hit path is independent of total memory size.
|
|
231
231
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
Historical 0.7.1 has 202 tests: 201 pass on Windows with one POSIX permission test skipped; all pass on Linux. Coverage includes actual process restart, same-session concurrent creation, LRU, empty baselines, forks, language isolation, corruption and permission errors, plus existing BM25/Hot Memory/tool regressions. DSH rc.1 and the newest rc.2 are validated. Prefix equality in tests is not a guarantee of actual billing savings.
|
|
232
|
+
Automated regressions cover session projection recovery, cross-process snapshots, writes and cancellation, lifecycle cleanup, BM25 recall, Hot Memory budgets, caching, corrections and project isolation. Curated lexical Top-5 recall is 41/41; fixture results do not measure real-model semantic accuracy, and prefix equality does not guarantee actual billing savings.
|
|
235
233
|
|
|
236
234
|
## FAQ
|
|
237
235
|
|
|
@@ -260,6 +258,6 @@ pnpm test
|
|
|
260
258
|
npm run bench
|
|
261
259
|
```
|
|
262
260
|
|
|
263
|
-
Read [CONTRIBUTING.md](./CONTRIBUTING.md) before submitting changes. See [CHANGELOG.md](./CHANGELOG.md) for version history. Formal packages are published by the tag workflow through npm OIDC. The current version is [v0.8.
|
|
261
|
+
Read [CONTRIBUTING.md](./CONTRIBUTING.md) before submitting changes. See [CHANGELOG.md](./CHANGELOG.md) for version history. Formal packages are published by the tag workflow through npm OIDC. The current version is [v0.8.1](https://github.com/Qinling-Melon-Farmers/dsh-memoir/releases/tag/v0.8.1), targeting DSH 0.1.7. Keep 0.7.1 on legacy DSH 0.1.5.
|
|
264
262
|
|
|
265
263
|
Apache-2.0
|
package/README.md
CHANGED
|
@@ -8,16 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
**DeepSeek Harness(DSH)的本地优先、跨会话项目记忆插件。** 它把 Agent 已确认的工作结论、经验教训和后续行动持久化,在新会话中注入有界且缓存友好的 Hot Memory,并通过本地 BM25 排序召回长尾历史。
|
|
10
10
|
|
|
11
|
-
无需 embedding、向量数据库或云端记忆服务;npm 包零捆绑运行时依赖,DSH peer
|
|
11
|
+
无需 embedding、向量数据库或云端记忆服务;npm 包零捆绑运行时依赖,DSH 与 Zod 4 peer 由宿主环境提供;Zod 用于验证宿主会话投影,不捆绑进插件。
|
|
12
12
|
|
|
13
13
|
> [!IMPORTANT]
|
|
14
|
-
> **0.8.
|
|
14
|
+
> **0.8.1 要求 DSH `>=0.1.7-rc.1 <0.1.8-0`**,开发 SDK 为 `0.1.7-rc.2`。优化桌面端无皮肤设置页的底色、留白及卡片排版;增加实际保存诊断,并迁移到公开 Session projection。旧 DSH 0.1.5 请固定安装 `dsh-memoir@0.7.1`。
|
|
15
15
|
>
|
|
16
16
|
> `dsh-memoir@0.7.1` 修复重启和内存淘汰后旧会话快照丢失(#10),支持 DSH **0.1.5-rc.1 / rc.2**。要求 `>=0.1.5-rc.1 <0.1.6-0`;请先核对宿主版本。旧 DSH 0.1.2 用户固定使用 `0.6.2`,0.1.1-rc.2 用户固定使用 `0.5.6`;这些旧版未包含本次修复。
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
|
-
npm install --global @deepseek-ai/dsh@0.1.7-rc.
|
|
20
|
-
dsh plugin --profile web add dsh-memoir@0.8.
|
|
19
|
+
npm install --global @deepseek-ai/dsh@0.1.7-rc.2
|
|
20
|
+
dsh plugin --profile web add dsh-memoir@0.8.1
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
重启 `dsh web` 即可。记忆保存在本机,不会随插件升级或卸载自动删除。
|
|
@@ -87,9 +87,9 @@ memoir_record / memoir_update
|
|
|
87
87
|
|
|
88
88
|
## 自动蒸馏
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
0.8.1 诊断页区分提醒提交、实际保存、失败、取消、相似记忆待确认和回执降级。只有成功保存的 `memoir_record` / `memoir_update` 才抑制本回合提醒;失败或相似候选待确认不算写入。保存后宿主仍可能取消最终工具结果,因此计数可以重叠。提醒与后续保存的关联不证明因果或内容正确。GUI 手工写入不计入 Agent 工具计数。
|
|
91
91
|
|
|
92
|
-
0.8.
|
|
92
|
+
0.8.1 使用公开 `sessionProjections` 从日志和 checkpoint 重建当前回合,不再读取弃用的 `snapshotEvents()`。每会话只保留当前回合最多 4096 个调用 ID,不保存参数或正文;超出保留范围时来源降级为 session-only。门控最多保留 1024 个 Agent,销毁时清理;旧版写入没有保存回执,不能仅从历史调用推断成功。BM25 是词项召回,不承诺跨语言语义匹配。
|
|
93
93
|
|
|
94
94
|
自动蒸馏是可观察的 Agent 收尾提醒,不是后台静默抓取聊天内容。默认 `1 / 0 / 1` 表示:每个有效 worked turn、无额外冷却、至少一次工具调用即可提醒。
|
|
95
95
|
|
|
@@ -151,11 +151,11 @@ v0.6.2 的诊断页显示最近触发或跳过原因及本次进程计数。已
|
|
|
151
151
|
|
|
152
152
|
| 渠道 | DSH 基线 | 安装方式 | 状态 |
|
|
153
153
|
| --- | --- | --- | --- |
|
|
154
|
-
| npm `latest`(`0.8.
|
|
154
|
+
| npm `latest`(`0.8.1`) | `>=0.1.7-rc.1 <0.1.8-0` | `dsh plugin --profile web add dsh-memoir@0.8.1` | 0.1.7 兼容线 |
|
|
155
155
|
| npm 固定版 `0.7.1` | `>=0.1.5-rc.1 <0.1.6-0` | `dsh plugin --profile web add dsh-memoir@0.7.1` | 旧 0.1.5 维护线 |
|
|
156
156
|
| npm 固定版 `0.6.2` | `>=0.1.2-alpha.2 <0.1.3` | `dsh plugin --profile web add dsh-memoir@0.6.2` | 旧 0.1.2 兼容线 |
|
|
157
157
|
| npm 固定版 `0.5.6` | `0.1.1-rc.2` | `dsh plugin --profile web add dsh-memoir@0.5.6` | rc2 兼容线 |
|
|
158
|
-
| 源码 `v0.8.
|
|
158
|
+
| 源码 `v0.8.1` | `>=0.1.7-rc.1 <0.1.8-0` | 本地构建 + `link:` | 开发调试,不兼容旧 0.1.5 / 0.1.6 |
|
|
159
159
|
|
|
160
160
|
需要 Node.js `^22.19.0 || >=24.0.0`。0.7.1 继续使用原生 `conversation.view` / `settings.section` 与 `snapshotEvents()`。DSH 0.1.5 的会话日志升级至 V3;其迁移与 Memoir 的 store v4 / settings v3 是独立格式。升级 DSH 前备份 DSH_HOME,迁移后的 DSH 会话不能承诺被旧宿主读取。Memoir 本次不迁移或清空记忆,也不启用新动态提示词行为;既有会话快照语义保持不变。
|
|
161
161
|
|
|
@@ -175,7 +175,7 @@ dsh plugin --profile web add "link:/absolute/path/dsh-memoir"
|
|
|
175
175
|
|
|
176
176
|
</details>
|
|
177
177
|
|
|
178
|
-
0.8.
|
|
178
|
+
0.8.1 使用原生 `uiWorkspace`、`conversation.view` / `settings.section` 与 Session V4 专属蒸馏来源。来源链接打开会话,回合编号可复制;不通过全局 DOM 自动滚到回合。设置页继承宿主背景、卡片使用主题层级色,保留皮肤覆盖与独立滚动。store v4 / settings v3 / snapshot v1 不变。升级 DSH 前备份 DSH_HOME,其会话迁移与插件记忆是两回事。
|
|
179
179
|
|
|
180
180
|
## 存储、隐私与安全边界
|
|
181
181
|
|
|
@@ -228,9 +228,7 @@ v0.5.6 基准(Node 24.19,900/1200 token;完整数据见 [`bench/report.md`
|
|
|
228
228
|
|
|
229
229
|
基准值取决于机器和语料;它证明的重点是注入预算保持有界、缓存命中路径与记忆总量解耦。
|
|
230
230
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
历史 0.7.1 含 202 项测试:Windows 201 项通过、1 项 POSIX 权限测试跳过;Linux 全部通过。覆盖实际跨进程快照恢复、同会话并发写入、LRU、空基线、fork、语言隔离、损坏与权限失败,以及原有 BM25/Hot Memory/工具回归。已核验 DSH rc.1 和最新 rc.2;没有把测试前缀一致性等同于实际账单节省保证。
|
|
231
|
+
自动化回归覆盖会话投影恢复、跨进程快照、写入与取消、生命周期清理、BM25 召回、Hot Memory 预算、缓存、纠错与跨项目隔离。固定词项样本的 Top-5 召回为 41/41;样本结果不代表真实模型的语义正确率,前缀一致性也不等同于实际账单节省保证。
|
|
234
232
|
|
|
235
233
|
## 常见问题
|
|
236
234
|
|
|
@@ -259,6 +257,6 @@ pnpm test
|
|
|
259
257
|
npm run bench
|
|
260
258
|
```
|
|
261
259
|
|
|
262
|
-
提交前请阅读 [CONTRIBUTING.md](./CONTRIBUTING.md)。版本变化见 [CHANGELOG.md](./CHANGELOG.md),正式包由 tag 工作流通过 npm OIDC 发布。当前版本是 [v0.8.
|
|
260
|
+
提交前请阅读 [CONTRIBUTING.md](./CONTRIBUTING.md)。版本变化见 [CHANGELOG.md](./CHANGELOG.md),正式包由 tag 工作流通过 npm OIDC 发布。当前版本是 [v0.8.1](https://github.com/Qinling-Melon-Farmers/dsh-memoir/releases/tag/v0.8.1),面向 DSH 0.1.7;旧 0.1.5 保留 0.7.1。
|
|
263
261
|
|
|
264
262
|
Apache-2.0
|
package/cordis.patch.yml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dsh-memoir bundle patch: inserts the host-half plugin row into the web
|
|
2
2
|
# profile roster. Applied as a profile bundle layer (the `dsh.bundle.patch`
|
|
3
|
-
# manifest field) over dsh-base. dsh-memoir v0.8.
|
|
3
|
+
# manifest field) over dsh-base. dsh-memoir v0.8.1 targets the DSH
|
|
4
4
|
# line >=0.1.7-rc.1 <0.1.8-0. Keep Memoir 0.7.1 on older DSH 0.1.5.
|
|
5
5
|
#
|
|
6
6
|
# The row is a bare plugin by package name: the node half (exports ".")
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Bounded, replayable activity projection. No synchronous Session log reads. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection';
|
|
4
|
+
export declare const ACTIVITY_KEY = "dsh-memoir/activity";
|
|
5
|
+
export declare const CALL_LIMIT = 4096;
|
|
6
|
+
declare const schema: z.ZodObject<{
|
|
7
|
+
turn: z.ZodNumber;
|
|
8
|
+
toolCalls: z.ZodNumber;
|
|
9
|
+
calls: z.ZodArray<z.ZodString>;
|
|
10
|
+
recorded: z.ZodBoolean;
|
|
11
|
+
reminded: z.ZodBoolean;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
export type MemoirActivity = z.infer<typeof schema>;
|
|
14
|
+
export declare const emptyActivity: () => MemoirActivity;
|
|
15
|
+
declare module '@deepseek-ai/dsh-session-projection/types' {
|
|
16
|
+
interface SessionProjectionStateMap {
|
|
17
|
+
'dsh-memoir/activity': MemoirActivity;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
declare module '@deepseek-ai/dsh-session/types' {
|
|
21
|
+
interface SessionEventMap {
|
|
22
|
+
/** Receipt after the Memoir store commit; no memory content is copied. */
|
|
23
|
+
'dsh-memoir/written': {
|
|
24
|
+
turn: number;
|
|
25
|
+
callId: string;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export declare const activityProjection: ProjectionDefinition<typeof ACTIVITY_KEY>;
|
|
30
|
+
export {};
|
package/lib/activity.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Bounded, replayable activity projection. No synchronous Session log reads. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export const ACTIVITY_KEY = 'dsh-memoir/activity';
|
|
4
|
+
export const CALL_LIMIT = 4096;
|
|
5
|
+
const schema = z.object({
|
|
6
|
+
turn: z.number().int().nonnegative(),
|
|
7
|
+
toolCalls: z.number().int().nonnegative(),
|
|
8
|
+
calls: z.array(z.string()).max(CALL_LIMIT),
|
|
9
|
+
recorded: z.boolean(),
|
|
10
|
+
reminded: z.boolean(),
|
|
11
|
+
});
|
|
12
|
+
export const emptyActivity = () => ({ turn: 0, toolCalls: 0, calls: [], recorded: false, reminded: false });
|
|
13
|
+
export const activityProjection = {
|
|
14
|
+
key: ACTIVITY_KEY,
|
|
15
|
+
stateVersion: 1,
|
|
16
|
+
stateSchema: schema,
|
|
17
|
+
init: emptyActivity,
|
|
18
|
+
apply(previous, event) {
|
|
19
|
+
if (event.type === 'turn/start' || event.type === 'tool/call' || event.type === 'dsh-memoir/written') {
|
|
20
|
+
const turn = event.data.turn;
|
|
21
|
+
if (turn < previous.turn)
|
|
22
|
+
return previous;
|
|
23
|
+
const state = turn > previous.turn ? { ...emptyActivity(), turn } : previous;
|
|
24
|
+
if (event.type === 'turn/start')
|
|
25
|
+
return state;
|
|
26
|
+
if (event.type === 'dsh-memoir/written')
|
|
27
|
+
return state.recorded ? state : { ...state, recorded: true };
|
|
28
|
+
if (state.calls.includes(event.data.callId))
|
|
29
|
+
return state;
|
|
30
|
+
return { ...state, toolCalls: state.toolCalls + 1, calls: [...state.calls, event.data.callId].slice(-CALL_LIMIT) };
|
|
31
|
+
}
|
|
32
|
+
if (event.type === 'agent/inbox/spliced' && event.data.target === 'next-step' &&
|
|
33
|
+
event.data.inserted.some(message => message.source.kind === 'dsh-memoir')) {
|
|
34
|
+
return previous.reminded ? previous : { ...previous, reminded: true };
|
|
35
|
+
}
|
|
36
|
+
return previous;
|
|
37
|
+
},
|
|
38
|
+
};
|
package/lib/autodistill.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Automatic turn-end distillation: when the plugin is enabled, each turn of a
|
|
3
3
|
* top-level agent that did real work (made tool calls) and did not already
|
|
4
|
-
*
|
|
4
|
+
* persist memory is followed by one steering step asking the agent to distill
|
|
5
5
|
* the turn into memoir_record entries. Turns without tool activity are left
|
|
6
6
|
* alone (no extra model cost), subagent sessions are never steered, and each
|
|
7
7
|
* turn is steered at most once — the steering step runs inside the same turn,
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { UserMessage } from '@deepseek-ai/dsh-llm';
|
|
13
13
|
import type { MemoirLanguage } from './i18n.js';
|
|
14
|
+
import type { MemoirActivity } from './activity.js';
|
|
14
15
|
declare module '@deepseek-ai/dsh-llm/message' {
|
|
15
16
|
interface MessageSourceMap {
|
|
16
17
|
'dsh-memoir': {
|
|
@@ -34,17 +35,7 @@ export interface TurnActivity {
|
|
|
34
35
|
recorded: boolean;
|
|
35
36
|
toolCalls: number;
|
|
36
37
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Session-log compatibility surface. DSH <= alpha.3 exposed `events` while
|
|
39
|
-
* alpha.4+ keeps the log private and exposes an immutable snapshot method.
|
|
40
|
-
*/
|
|
41
|
-
export interface SessionEventSource {
|
|
42
|
-
readonly events?: readonly TurnEventLike[];
|
|
43
|
-
snapshotEvents?: () => readonly TurnEventLike[];
|
|
44
|
-
}
|
|
45
|
-
/** Read a stable session event snapshot across the old and new DSH APIs. */
|
|
46
|
-
export declare function sessionEventSnapshot(session: SessionEventSource | undefined): readonly TurnEventLike[];
|
|
47
|
-
/** Scan the tail of a session log for one turn's tool activity. */
|
|
38
|
+
/** Pure event-fixture fold. Runtime activity comes from the host projection. */
|
|
48
39
|
export declare function turnActivity(events: readonly TurnEventLike[], turn: number): TurnActivity;
|
|
49
40
|
/** The agent surface the turn-stopping listener needs. */
|
|
50
41
|
export interface AutoDistillAgentLike {
|
|
@@ -83,16 +74,19 @@ export declare class AutoDistillGate {
|
|
|
83
74
|
/** Drop all state for one agent (disposal hygiene). */
|
|
84
75
|
forget(agentId: string): void;
|
|
85
76
|
}
|
|
86
|
-
export type DistillOutcome = 'disabled' | 'subagent' | 'aborted' | 'idle' | 'recorded' | 'duplicate' | 'interval' | 'tools' | 'cooldown' | 'steered' | 'failed';
|
|
77
|
+
export type DistillOutcome = 'disabled' | 'subagent' | 'aborted' | 'idle' | 'recorded' | 'duplicate' | 'interval' | 'tools' | 'cooldown' | 'steered' | 'failed' | 'unavailable';
|
|
87
78
|
/** Process-local counters only; never retains message content or credentials. */
|
|
88
79
|
export declare class DistillDiagnostics {
|
|
89
80
|
private counts;
|
|
90
81
|
private last;
|
|
91
82
|
private workedTurns;
|
|
92
83
|
private agents;
|
|
84
|
+
private writes;
|
|
85
|
+
write(outcome: keyof DistillDiagnostics['writes']): void;
|
|
93
86
|
record(outcome: DistillOutcome, at: number, turn: number, toolCalls: number, agents: number): void;
|
|
94
87
|
snapshot(): {
|
|
95
88
|
counts: {
|
|
89
|
+
recorded?: number | undefined;
|
|
96
90
|
subagent?: number | undefined;
|
|
97
91
|
duplicate?: number | undefined;
|
|
98
92
|
interval?: number | undefined;
|
|
@@ -101,9 +95,17 @@ export declare class DistillDiagnostics {
|
|
|
101
95
|
disabled?: number | undefined;
|
|
102
96
|
aborted?: number | undefined;
|
|
103
97
|
idle?: number | undefined;
|
|
104
|
-
recorded?: number | undefined;
|
|
105
98
|
steered?: number | undefined;
|
|
106
99
|
failed?: number | undefined;
|
|
100
|
+
unavailable?: number | undefined;
|
|
101
|
+
};
|
|
102
|
+
writes: {
|
|
103
|
+
persisted: number;
|
|
104
|
+
afterReminder: number;
|
|
105
|
+
failed: number;
|
|
106
|
+
canceled: number;
|
|
107
|
+
needsResolution: number;
|
|
108
|
+
receiptFailed: number;
|
|
107
109
|
};
|
|
108
110
|
workedTurns: number;
|
|
109
111
|
agents: number;
|
|
@@ -146,4 +148,6 @@ export declare function installAutoDistill(wire: AutoDistillWire, options: {
|
|
|
146
148
|
language?: () => MemoirLanguage;
|
|
147
149
|
now?: () => number;
|
|
148
150
|
diagnostics?: DistillDiagnostics;
|
|
151
|
+
/** Public host projection at the exact Session cursor; absent means skip safely. */
|
|
152
|
+
activity?: (agent: AutoDistillAgentLike) => MemoirActivity | undefined;
|
|
149
153
|
}): () => void;
|
package/lib/autodistill.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Automatic turn-end distillation: when the plugin is enabled, each turn of a
|
|
3
3
|
* top-level agent that did real work (made tool calls) and did not already
|
|
4
|
-
*
|
|
4
|
+
* persist memory is followed by one steering step asking the agent to distill
|
|
5
5
|
* the turn into memoir_record entries. Turns without tool activity are left
|
|
6
6
|
* alone (no extra model cost), subagent sessions are never steered, and each
|
|
7
7
|
* turn is steered at most once — the steering step runs inside the same turn,
|
|
@@ -19,13 +19,7 @@ export function distillPrompt(language = DEFAULT_MEMOIR_LANGUAGE) {
|
|
|
19
19
|
export const DISTILL_PROMPT = distillPrompt();
|
|
20
20
|
/** Plugin identity stamped on the steering message source. */
|
|
21
21
|
export const AUTO_DISTILL_PLUGIN = 'dsh-memoir';
|
|
22
|
-
/**
|
|
23
|
-
export function sessionEventSnapshot(session) {
|
|
24
|
-
if (typeof session?.snapshotEvents === 'function')
|
|
25
|
-
return session.snapshotEvents();
|
|
26
|
-
return session?.events ?? [];
|
|
27
|
-
}
|
|
28
|
-
/** Scan the tail of a session log for one turn's tool activity. */
|
|
22
|
+
/** Pure event-fixture fold. Runtime activity comes from the host projection. */
|
|
29
23
|
export function turnActivity(events, turn) {
|
|
30
24
|
let recorded = false;
|
|
31
25
|
let toolCalls = 0;
|
|
@@ -40,9 +34,9 @@ export function turnActivity(events, turn) {
|
|
|
40
34
|
continue;
|
|
41
35
|
if (event.type === 'tool/call') {
|
|
42
36
|
toolCalls += 1;
|
|
43
|
-
if (data.name === 'memoir_record' || data.name === 'memoir_update')
|
|
44
|
-
recorded = true;
|
|
45
37
|
}
|
|
38
|
+
if (event.type === 'dsh-memoir/written')
|
|
39
|
+
recorded = true;
|
|
46
40
|
}
|
|
47
41
|
return { worked: toolCalls > 0, recorded, toolCalls };
|
|
48
42
|
}
|
|
@@ -102,6 +96,8 @@ export class DistillDiagnostics {
|
|
|
102
96
|
last = null;
|
|
103
97
|
workedTurns = 0;
|
|
104
98
|
agents = 0;
|
|
99
|
+
writes = { persisted: 0, afterReminder: 0, failed: 0, canceled: 0, needsResolution: 0, receiptFailed: 0 };
|
|
100
|
+
write(outcome) { this.writes[outcome] += 1; }
|
|
105
101
|
record(outcome, at, turn, toolCalls, agents) {
|
|
106
102
|
this.counts[outcome] = (this.counts[outcome] ?? 0) + 1;
|
|
107
103
|
if (['interval', 'tools', 'cooldown', 'steered', 'failed'].includes(outcome))
|
|
@@ -109,7 +105,7 @@ export class DistillDiagnostics {
|
|
|
109
105
|
this.last = { outcome, at, turn, toolCalls };
|
|
110
106
|
this.agents = agents;
|
|
111
107
|
}
|
|
112
|
-
snapshot() { return { counts: { ...this.counts }, workedTurns: this.workedTurns, agents: this.agents, last: this.last === null ? null : { ...this.last } }; }
|
|
108
|
+
snapshot() { return { counts: { ...this.counts }, writes: { ...this.writes }, workedTurns: this.workedTurns, agents: this.agents, last: this.last === null ? null : { ...this.last } }; }
|
|
113
109
|
setAgents(agents) { this.agents = agents; }
|
|
114
110
|
}
|
|
115
111
|
/**
|
|
@@ -137,11 +133,24 @@ export function installAutoDistill(wire, options) {
|
|
|
137
133
|
report('aborted');
|
|
138
134
|
return;
|
|
139
135
|
}
|
|
140
|
-
const
|
|
141
|
-
if (
|
|
136
|
+
const activity = options.activity?.(agent);
|
|
137
|
+
if (activity === undefined) {
|
|
138
|
+
report('unavailable');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
if (activity.turn > turn) {
|
|
142
|
+
report('duplicate');
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const { recorded, toolCalls, reminded } = activity.turn === turn ? activity : { recorded: false, toolCalls: 0, reminded: false };
|
|
146
|
+
if (toolCalls === 0 || recorded) {
|
|
142
147
|
report(recorded ? 'recorded' : 'idle', toolCalls);
|
|
143
148
|
return;
|
|
144
149
|
}
|
|
150
|
+
if (reminded) {
|
|
151
|
+
report('duplicate', toolCalls);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
145
154
|
const live = options.policy?.();
|
|
146
155
|
const policy = {
|
|
147
156
|
every: integerAtLeast(live?.every ?? options.every, 1, 1),
|
package/lib/client.js
CHANGED
|
@@ -178,7 +178,16 @@ var dictionaries = {
|
|
|
178
178
|
"distill.subagent": "\u5B50\u4EE3\u7406\u8DF3\u8FC7",
|
|
179
179
|
"distill.aborted": "\u4E2D\u6B62\u8DF3\u8FC7",
|
|
180
180
|
"distill.idle": "\u65E0\u5DE5\u5177\u6D3B\u52A8",
|
|
181
|
-
"distill.recorded": "\u5DF2\
|
|
181
|
+
"distill.recorded": "\u672C\u56DE\u5408\u5DF2\u4FDD\u5B58\u8BB0\u5FC6\u6216\u66F4\u65B0",
|
|
182
|
+
"distill.unavailable": "\u4F1A\u8BDD\u6D3B\u52A8\u6295\u5F71\u4E0D\u53EF\u7528\uFF0C\u5DF2\u5B89\u5168\u8DF3\u8FC7",
|
|
183
|
+
"writes.title": "\u5B9E\u9645\u5199\u5165\u7ED3\u679C",
|
|
184
|
+
"writes.persisted": "\u5DF2\u4FDD\u5B58\u5199\u5165 / \u66F4\u65B0",
|
|
185
|
+
"writes.afterReminder": "\u5176\u4E2D\uFF1A\u63D0\u9192\u6240\u5728\u56DE\u5408\u540E\u7EED\u4FDD\u5B58",
|
|
186
|
+
"writes.failed": "\u5DE5\u5177\u7ED3\u679C\u5931\u8D25",
|
|
187
|
+
"writes.canceled": "\u5DE5\u5177\u7ED3\u679C\u53D6\u6D88",
|
|
188
|
+
"writes.needsResolution": "\u76F8\u4F3C\u8BB0\u5FC6\u5F85\u786E\u8BA4\uFF08\u672A\u5199\u5165\uFF09",
|
|
189
|
+
"writes.receiptFailed": "\u5DF2\u4FDD\u5B58\u4F46\u56DE\u5408\u56DE\u6267\u4E0D\u53EF\u7528",
|
|
190
|
+
"writes.note": "\u4EC5\u7EDF\u8BA1\u672C\u8FDB\u7A0B\u5185 Agent \u5DE5\u5177\uFF1B\u4FDD\u5B58\u540E\u4ECD\u53EF\u80FD\u88AB\u5BBF\u4E3B\u5224\u5B9A\u53D6\u6D88\uFF0C\u56E0\u6B64\u8BA1\u6570\u53EF\u91CD\u53E0\u3002\u4FDD\u5B58\u4E0E\u63D0\u9192\u7684\u5173\u8054\u4E0D\u8BC1\u660E\u56E0\u679C\u6216\u8BB0\u5FC6\u5185\u5BB9\u6B63\u786E\u3002",
|
|
182
191
|
"distill.duplicate": "\u91CD\u590D\u6216\u8FC7\u671F\u56DE\u5408",
|
|
183
192
|
"distill.interval": "\u672A\u8FBE\u56DE\u5408\u95F4\u9694",
|
|
184
193
|
"distill.tools": "\u672A\u8FBE\u5DE5\u5177\u8C03\u7528\u9608\u503C",
|
|
@@ -368,7 +377,16 @@ var dictionaries = {
|
|
|
368
377
|
"distill.subagent": "Subagent skipped",
|
|
369
378
|
"distill.aborted": "Aborted turn skipped",
|
|
370
379
|
"distill.idle": "No tool activity",
|
|
371
|
-
"distill.recorded": "Memory write or update
|
|
380
|
+
"distill.recorded": "Memory write or update persisted in this turn",
|
|
381
|
+
"distill.unavailable": "Activity projection unavailable; skipped safely",
|
|
382
|
+
"writes.title": "Actual write outcomes",
|
|
383
|
+
"writes.persisted": "Persisted writes / updates",
|
|
384
|
+
"writes.afterReminder": "Of these: later saves in a reminded turn",
|
|
385
|
+
"writes.failed": "Failed tool results",
|
|
386
|
+
"writes.canceled": "Canceled tool results",
|
|
387
|
+
"writes.needsResolution": "Similarity needs resolution (no write)",
|
|
388
|
+
"writes.receiptFailed": "Saved but turn receipt unavailable",
|
|
389
|
+
"writes.note": "Process-local Agent tools only. Host cancellation may follow a save, so counts may overlap. Correlation with a reminder proves neither causation nor semantic correctness.",
|
|
372
390
|
"distill.duplicate": "Duplicate or stale turn",
|
|
373
391
|
"distill.interval": "Turn interval not reached",
|
|
374
392
|
"distill.tools": "Tool threshold not reached",
|
|
@@ -1500,6 +1518,15 @@ ${projectSignature}`;
|
|
|
1500
1518
|
": ",
|
|
1501
1519
|
count
|
|
1502
1520
|
] }, reason)),
|
|
1521
|
+
diag.autoDistill.writes ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-dsh-part": "write-outcomes", children: [
|
|
1522
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h4", { children: t("writes.title") }),
|
|
1523
|
+
Object.entries(diag.autoDistill.writes).map(([reason, count]) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1524
|
+
t(`writes.${reason}`),
|
|
1525
|
+
": ",
|
|
1526
|
+
count
|
|
1527
|
+
] }, reason)),
|
|
1528
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: t("writes.note") })
|
|
1529
|
+
] }) : null,
|
|
1503
1530
|
diag.autoDistill.last ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1504
1531
|
t("distill.last"),
|
|
1505
1532
|
": ",
|
|
@@ -1647,7 +1674,7 @@ function mountPanelStyles(target = document) {
|
|
|
1647
1674
|
return () => tag.remove();
|
|
1648
1675
|
}
|
|
1649
1676
|
var PANEL_CSS = `
|
|
1650
|
-
/* ---
|
|
1677
|
+
/* --- Native Web / Desktop slot surfaces ---------------------------------- */
|
|
1651
1678
|
|
|
1652
1679
|
.memoir-native-view,
|
|
1653
1680
|
.memoir-settings-section {
|
|
@@ -1664,8 +1691,9 @@ var PANEL_CSS = `
|
|
|
1664
1691
|
font-family: var(--dsw-font-family, inherit);
|
|
1665
1692
|
}
|
|
1666
1693
|
|
|
1667
|
-
.memoir-settings-section
|
|
1668
|
-
|
|
1694
|
+
.memoir-settings-section {
|
|
1695
|
+
/* Settings already owns a raised surface: do not repaint the chat base. */
|
|
1696
|
+
background: transparent;
|
|
1669
1697
|
}
|
|
1670
1698
|
|
|
1671
1699
|
/* --- center-column takeover (mirrors dsh-ssh / dsh-task-board) --------------- */
|
|
@@ -1713,6 +1741,16 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
1713
1741
|
overflow: hidden;
|
|
1714
1742
|
}
|
|
1715
1743
|
|
|
1744
|
+
.memoir-settings-section .memoir-panel {
|
|
1745
|
+
padding: 12px 16px 16px;
|
|
1746
|
+
background: transparent;
|
|
1747
|
+
}
|
|
1748
|
+
.memoir-panel *,
|
|
1749
|
+
.memoir-panel *::before,
|
|
1750
|
+
.memoir-panel *::after {
|
|
1751
|
+
box-sizing: border-box;
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1716
1754
|
.memoir-header {
|
|
1717
1755
|
display: flex;
|
|
1718
1756
|
align-items: center;
|
|
@@ -1729,9 +1767,11 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
1729
1767
|
line-height: 1.25;
|
|
1730
1768
|
}
|
|
1731
1769
|
.memoir-subtitle {
|
|
1732
|
-
font-size:
|
|
1770
|
+
font-size: 12px;
|
|
1771
|
+
font-weight: 400;
|
|
1733
1772
|
color: var(--dsw-alias-label-secondary, var(--text-secondary, #8a8f9c));
|
|
1734
|
-
margin-top:
|
|
1773
|
+
margin-top: 6px;
|
|
1774
|
+
line-height: 1.5;
|
|
1735
1775
|
overflow: hidden;
|
|
1736
1776
|
text-overflow: ellipsis;
|
|
1737
1777
|
white-space: nowrap;
|
|
@@ -1756,7 +1796,7 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
1756
1796
|
}
|
|
1757
1797
|
.memoir-surface-tab {
|
|
1758
1798
|
flex: 0 0 auto;
|
|
1759
|
-
min-height:
|
|
1799
|
+
min-height: 34px;
|
|
1760
1800
|
padding: 5px 12px;
|
|
1761
1801
|
border: 0;
|
|
1762
1802
|
border-radius: 7px;
|
|
@@ -1877,7 +1917,7 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
1877
1917
|
gap: 6px;
|
|
1878
1918
|
}
|
|
1879
1919
|
.memoir-search {
|
|
1880
|
-
flex:
|
|
1920
|
+
flex: 1 1 200px;
|
|
1881
1921
|
min-width: 120px;
|
|
1882
1922
|
padding: 6px 10px;
|
|
1883
1923
|
font-size: 13px;
|
|
@@ -1982,7 +2022,7 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
1982
2022
|
display: flex;
|
|
1983
2023
|
align-items: center;
|
|
1984
2024
|
gap: 6px;
|
|
1985
|
-
margin:
|
|
2025
|
+
margin: 12px 0 10px;
|
|
1986
2026
|
font-size: 13px;
|
|
1987
2027
|
font-weight: 600;
|
|
1988
2028
|
color: var(--dsw-alias-label-primary, var(--text-primary, #1f2328));
|
|
@@ -1995,10 +2035,10 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
1995
2035
|
/* Entry cards: dsh-skill-explorer .skill / dsh-task-board .card surfaces. */
|
|
1996
2036
|
.memoir-entry {
|
|
1997
2037
|
border: 1px solid var(--dsw-alias-border-l2, rgba(0, 0, 0, .08));
|
|
1998
|
-
border-radius:
|
|
1999
|
-
padding:
|
|
2000
|
-
margin-bottom:
|
|
2001
|
-
background: var(--dsw-alias-bg-
|
|
2038
|
+
border-radius: 12px;
|
|
2039
|
+
padding: 14px 16px;
|
|
2040
|
+
margin-bottom: 12px;
|
|
2041
|
+
background: var(--dsw-alias-bg-layer-2, var(--bg-card, rgba(127, 127, 127, .04)));
|
|
2002
2042
|
}
|
|
2003
2043
|
.memoir-entry-meta {
|
|
2004
2044
|
display: flex;
|
|
@@ -2007,7 +2047,7 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
2007
2047
|
gap: 6px;
|
|
2008
2048
|
font-size: 11px;
|
|
2009
2049
|
color: var(--dsw-alias-label-secondary, var(--text-secondary, #8a8f9c));
|
|
2010
|
-
margin-bottom:
|
|
2050
|
+
margin-bottom: 10px;
|
|
2011
2051
|
}
|
|
2012
2052
|
.memoir-chip {
|
|
2013
2053
|
display: inline-block;
|
|
@@ -2021,8 +2061,9 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
2021
2061
|
white-space: nowrap;
|
|
2022
2062
|
}
|
|
2023
2063
|
.memoir-tag {
|
|
2024
|
-
|
|
2025
|
-
|
|
2064
|
+
/* Tags are metadata, not success alerts. */
|
|
2065
|
+
color: var(--dsw-alias-label-secondary, var(--text-secondary, #6b7280));
|
|
2066
|
+
background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .06));
|
|
2026
2067
|
}
|
|
2027
2068
|
.memoir-source {
|
|
2028
2069
|
display: inline-flex;
|
|
@@ -2066,15 +2107,16 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
2066
2107
|
opacity: .8;
|
|
2067
2108
|
}
|
|
2068
2109
|
.memoir-entry-title {
|
|
2069
|
-
font-size:
|
|
2110
|
+
font-size: 14px;
|
|
2070
2111
|
font-weight: 600;
|
|
2071
|
-
line-height: 1.
|
|
2112
|
+
line-height: 1.5;
|
|
2072
2113
|
color: var(--dsw-alias-label-primary, var(--text-primary, #1f2328));
|
|
2073
|
-
margin-bottom:
|
|
2114
|
+
margin-bottom: 6px;
|
|
2115
|
+
overflow-wrap: anywhere;
|
|
2074
2116
|
}
|
|
2075
2117
|
.memoir-entry-content {
|
|
2076
|
-
font-size:
|
|
2077
|
-
line-height: 1.
|
|
2118
|
+
font-size: 13px;
|
|
2119
|
+
line-height: 1.7;
|
|
2078
2120
|
color: var(--dsw-alias-label-primary, var(--text-primary, #1f2328));
|
|
2079
2121
|
white-space: pre-wrap;
|
|
2080
2122
|
word-break: break-word;
|
|
@@ -2102,7 +2144,7 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
2102
2144
|
display: flex;
|
|
2103
2145
|
flex-wrap: wrap;
|
|
2104
2146
|
gap: 6px;
|
|
2105
|
-
margin:
|
|
2147
|
+
margin: 0 24px 12px 0;
|
|
2106
2148
|
}
|
|
2107
2149
|
.memoir-delete {
|
|
2108
2150
|
float: right;
|
|
@@ -2486,8 +2528,8 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
2486
2528
|
}
|
|
2487
2529
|
.memoir-settings-grid {
|
|
2488
2530
|
display: grid;
|
|
2489
|
-
grid-template-columns: repeat(
|
|
2490
|
-
gap:
|
|
2531
|
+
grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
|
|
2532
|
+
gap: 12px;
|
|
2491
2533
|
}
|
|
2492
2534
|
.memoir-settings-grid .memoir-field {
|
|
2493
2535
|
min-width: 0;
|
|
@@ -2705,7 +2747,8 @@ html[data-dsh-memoir-active]:not([data-dsh-ssh-active]):not([data-dsh-taskboard-
|
|
|
2705
2747
|
}
|
|
2706
2748
|
|
|
2707
2749
|
@media (max-width: 640px) {
|
|
2708
|
-
.memoir-panel
|
|
2750
|
+
.memoir-panel,
|
|
2751
|
+
.memoir-settings-section .memoir-panel {
|
|
2709
2752
|
padding: 10px 10px 12px;
|
|
2710
2753
|
gap: 8px;
|
|
2711
2754
|
}
|