dsh-layered-memory 0.9.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tui.js ADDED
@@ -0,0 +1,201 @@
1
+ import { EFFORT_CHOICES } from './config.js';
2
+ import { errDetail } from './util/filelog.js';
3
+ /** 状态行/命令回显的档位短名(与 web 芯片「记忆 · 智能」同口径)。 */
4
+ const MODE_LABEL = { auto: '智能', chat: '对话', work: '工作', off: '暂停' };
5
+ /**
6
+ * 单服务的「现在挂 + internal/service 补挂」:服务晚于本插件就绪(行序 ≠ 发布序)
7
+ * 或换实例时自动重挂;下线即摘。fiber 卸载统一清理(disposer 指向已死服务时吞错)。
8
+ */
9
+ function watchService(ctx, name, attach, logger) {
10
+ let disposer;
11
+ let bound;
12
+ const up = () => {
13
+ const svc = ctx.get?.(name);
14
+ if (!svc || svc === bound)
15
+ return;
16
+ try {
17
+ const d = attach(svc);
18
+ if (!d)
19
+ return;
20
+ // 换实例摘旧:旧 disposer 可能指向已死服务,吞错不阻断新挂接
21
+ try {
22
+ disposer?.();
23
+ }
24
+ catch {
25
+ // 旧服务所在宿主行已先一步卸载
26
+ }
27
+ disposer = d;
28
+ bound = svc;
29
+ }
30
+ catch (err) {
31
+ logger.warn(`[memory] TUI 接缝 ${name} 挂接失败(忽略): ${errDetail(err)}`);
32
+ }
33
+ };
34
+ const down = () => {
35
+ try {
36
+ disposer?.();
37
+ }
38
+ catch {
39
+ // 服务所在宿主行已先一步卸载,残留引用调用必抛——吞掉
40
+ }
41
+ disposer = undefined;
42
+ bound = undefined;
43
+ };
44
+ up();
45
+ ctx.on('internal/service', (n, impl) => {
46
+ if (n !== name)
47
+ return;
48
+ if (impl)
49
+ up();
50
+ else if (bound)
51
+ down();
52
+ });
53
+ ctx.effect(() => () => down());
54
+ }
55
+ export function registerTuiSurface(ctx, deps) {
56
+ const { logger, live, modes } = deps;
57
+ // ── 状态行:当前会话的记忆档位(TUI 单活动 agent;agent/session-start 覆盖
58
+ // /new、/resume、rewind 全部切换路径) ──
59
+ let lastSid = '';
60
+ let lastText = '';
61
+ let refreshStatus;
62
+ const statusText = () => {
63
+ if (!live.get().enabled)
64
+ return '记忆:停用';
65
+ return `记忆:${MODE_LABEL[modes.get(lastSid)]}`;
66
+ };
67
+ watchService(ctx, 'tuiStatus', (svc) => {
68
+ const status = svc;
69
+ refreshStatus = () => {
70
+ const text = statusText();
71
+ if (text === lastText)
72
+ return;
73
+ lastText = text;
74
+ // 第三参 identity(插件 ctx)只喂 dsh-tui 的效果台账归属(C-060),省略记 undeclared
75
+ status.set('memory', text, ctx);
76
+ };
77
+ refreshStatus();
78
+ logger.info('[memory] TUI 状态行已接入(当前会话记忆档位可见)');
79
+ return () => {
80
+ refreshStatus = undefined;
81
+ lastText = '';
82
+ try {
83
+ // 本插件独占该 key,显式清行与保留 set() 返回的 disposer 语义等价(后者只清
84
+ // 自己那次写——这里没有并发写者,选更直白的一种)
85
+ status.set('memory', undefined);
86
+ }
87
+ catch {
88
+ // 宿主行已卸载
89
+ }
90
+ };
91
+ }, logger);
92
+ ctx.on('agent/session-start', (payload) => {
93
+ const sid = String(payload?.agent?.id ?? '');
94
+ if (!sid || sid === lastSid)
95
+ return;
96
+ lastSid = sid;
97
+ refreshStatus?.();
98
+ });
99
+ // 全局开关在 /settings 里被改动 → 状态行即时跟随(否则「记忆:停用」读数要等下次交互才刷新)
100
+ const unsubscribeLive = live.onChange?.(() => refreshStatus?.());
101
+ if (unsubscribeLive)
102
+ ctx.effect(() => () => unsubscribeLive());
103
+ // ── /memory 命令:切当前会话档位;无参数时 TUI 环境弹托管单选 ──
104
+ const MODE_OPTIONS = [
105
+ { id: 'auto', label: '智能', description: '按会话类型自动选 chat/work 族' },
106
+ { id: 'chat', label: '对话', description: '用户画像与偏好(chat 族)' },
107
+ { id: 'work', label: '工作', description: '团队操作准则(work 族)' },
108
+ { id: 'off', label: '暂停', description: '本会话停用记忆,可随时切回' },
109
+ ];
110
+ const MODE_USAGE = '用法:/memory <auto|chat|work|off>';
111
+ watchService(ctx, 'commands', (svc) => {
112
+ const commands = svc;
113
+ const dispose = commands.register({
114
+ name: 'memory',
115
+ description: '切换本会话记忆档位(auto/chat/work/off,无参数弹出选择)',
116
+ handler: async (inv) => {
117
+ // 宿主侧 Agent 身份契约是 id(见 CommandsLike 注释);sessionId 仅作兜底
118
+ const agentId = inv.agent ?? {};
119
+ const sid = String(agentId.id ?? agentId.sessionId ?? '');
120
+ if (!sid)
121
+ return { kind: 'error', text: '无法确定当前会话' };
122
+ const arg = inv.rawInput.trim().toLowerCase();
123
+ let mode = ['auto', 'chat', 'work', 'off'].includes(arg)
124
+ ? arg
125
+ : undefined;
126
+ if (!mode) {
127
+ const dialogs = ctx.get?.('tuiDialogs');
128
+ if (!dialogs)
129
+ return { kind: 'error', text: MODE_USAGE };
130
+ const picked = await dialogs.select(ctx, {
131
+ title: '记忆档位',
132
+ options: MODE_OPTIONS,
133
+ timeoutMs: 60_000,
134
+ signal: inv.signal,
135
+ });
136
+ if (!picked)
137
+ return { kind: 'success', text: '已取消(档位未变)' };
138
+ mode = picked;
139
+ }
140
+ const old = modes.get(sid);
141
+ // ADR-0003 切换链(切走落袋/off 挂起/切回恢复)随 onModeChange 回调自动走
142
+ modes.set(sid, mode);
143
+ lastSid = sid;
144
+ refreshStatus?.();
145
+ logger.info(`[memory] /memory 切档 session=${sid} ${old}→${mode}`);
146
+ return { kind: 'success', text: `记忆档位:${MODE_LABEL[old]} → ${MODE_LABEL[mode]}(本会话)` };
147
+ },
148
+ });
149
+ logger.info('[memory] /memory 命令已注册(会话档位切换)');
150
+ return dispose;
151
+ }, logger);
152
+ // ── /settings 设置区块:dsh-memory 命名空间的声明式编辑面(渲染/草稿/保存
153
+ // 全由 TUI 宿主负责;复杂嵌套键如路由链仍走 YAML/web 面板) ──
154
+ watchService(ctx, 'tuiSettingsSections', (svc) => {
155
+ const sections = svc;
156
+ const dispose = sections.register({
157
+ ns: 'dsh-memory',
158
+ title: 'Layered Memory',
159
+ descriptions: { zh: '分层蒸馏记忆' },
160
+ fields: [
161
+ { path: ['enabled'], label: 'Master switch', descriptions: { zh: '总开关(关闭即全停)' }, kind: 'boolean' },
162
+ { path: ['capture'], label: 'Capture', descriptions: { zh: '捕获(L0 对话留档)' }, kind: 'boolean' },
163
+ { path: ['distill'], label: 'Distill', descriptions: { zh: '蒸馏(L1~L3 记忆整理)' }, kind: 'boolean' },
164
+ { path: ['recall'], label: 'Recall injection', descriptions: { zh: '召回注入' }, kind: 'boolean' },
165
+ {
166
+ path: ['reasoningEffort'],
167
+ label: 'Distill reasoning effort',
168
+ descriptions: { zh: '蒸馏思考档位' },
169
+ kind: 'select',
170
+ options: EFFORT_CHOICES.map((e) => ({ value: e, label: e === '' ? 'follow config' : e })),
171
+ },
172
+ {
173
+ path: ['distillProvider'],
174
+ label: 'Distill provider',
175
+ descriptions: { zh: '蒸馏模型供应商' },
176
+ kind: 'text',
177
+ hint: 'Empty = follow default model',
178
+ hintDescriptions: { zh: '留空跟随默认模型' },
179
+ },
180
+ {
181
+ path: ['distillModel'],
182
+ label: 'Distill model',
183
+ descriptions: { zh: '蒸馏模型' },
184
+ kind: 'text',
185
+ hint: 'Empty = follow default model',
186
+ hintDescriptions: { zh: '留空跟随默认模型' },
187
+ },
188
+ {
189
+ path: ['distillMaxInputChars'],
190
+ label: 'Distill input budget',
191
+ descriptions: { zh: '蒸馏输入预算(字符)' },
192
+ kind: 'number',
193
+ hint: '0 = default',
194
+ hintDescriptions: { zh: '0 = 默认' },
195
+ },
196
+ ],
197
+ });
198
+ logger.info('[memory] TUI 设置区块已注册(/settings · 分层蒸馏记忆)');
199
+ return dispose;
200
+ }, logger);
201
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-layered-memory",
3
- "version": "0.9.2",
3
+ "version": "0.11.0",
4
4
  "description": "L0~L3 分层蒸馏记忆插件 for DeepSeek Harness:自动捕获对话(L0)、抽取原子记忆(L1)、整合场景块(L2)、蒸馏核心画像/团队方法论(L3),并在模型步骤前自动召回注入。移植自 MemoryCore (TencentDB Agent Memory) 的管线设计。",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -31,14 +31,15 @@
31
31
  ],
32
32
  "scripts": {
33
33
  "build": "tsc -p tsconfig.json && node scripts/build-client.mjs && node scripts/copy-client.mjs",
34
- "smoke": "npm run build:smoke && node dist-smoke/smoke.js",
35
- "verify-catalog": "npm run build && node scripts/verify-catalog.mjs",
34
+ "smoke": "pnpm run build:smoke && node dist-smoke/smoke.js",
35
+ "verify-catalog": "pnpm run build && node scripts/verify-catalog.mjs",
36
36
  "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
37
37
  "build:smoke": "tsc src/smoke.ts --outDir dist-smoke --module nodenext --moduleResolution nodenext --target es2022 --strict --skipLibCheck --esModuleInterop"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=22.16.0"
41
41
  },
42
+ "packageManager": "pnpm@11.21.0",
42
43
  "keywords": [
43
44
  "dsh",
44
45
  "deepseek-harness",
@@ -69,15 +70,16 @@
69
70
  },
70
71
  "devDependencies": {
71
72
  "@deepseek-ai/cordis": "4.0.2",
72
- "@deepseek-ai/dsh-agent": "0.1.2-alpha.2",
73
- "@deepseek-ai/dsh-agent-default-model": "0.1.2-alpha.2",
74
- "@deepseek-ai/dsh-client-connection": "0.1.2-alpha.2",
75
- "@deepseek-ai/dsh-home-paths": "0.1.2-alpha.2",
76
- "@deepseek-ai/dsh-llm": "0.1.2-alpha.2",
77
- "@deepseek-ai/dsh-session": "0.1.2-alpha.2",
78
- "@deepseek-ai/dsh-settings": "0.1.2-alpha.2",
79
- "@deepseek-ai/dsh-system-prompt": "0.1.2-alpha.2",
80
- "@deepseek-ai/dsh-tools": "0.1.2-alpha.2",
73
+ "@deepseek-ai/cosmokit": "1.8.3",
74
+ "@deepseek-ai/dsh-agent": "0.1.2-rc.1",
75
+ "@deepseek-ai/dsh-agent-default-model": "0.1.2-rc.1",
76
+ "@deepseek-ai/dsh-client-connection": "0.1.2-rc.1",
77
+ "@deepseek-ai/dsh-home-paths": "0.1.2-rc.1",
78
+ "@deepseek-ai/dsh-llm": "0.1.2-rc.1",
79
+ "@deepseek-ai/dsh-session": "0.1.2-rc.1",
80
+ "@deepseek-ai/dsh-settings": "0.1.2-rc.1",
81
+ "@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1",
82
+ "@deepseek-ai/dsh-tools": "0.1.2-rc.1",
81
83
  "@types/node": "^22.0.0",
82
84
  "@types/react": "^19.2.18",
83
85
  "esbuild": "^0.28.2",