dsh-whale-loli 0.5.7 → 0.5.8
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/package.json +1 -1
- package/src/history-window.js +78 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-whale-loli",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.8",
|
|
4
4
|
"description": "鲸鱼娘 Agent 预设(三个)— 极简模式工具面 + 鲸鱼娘·小鱼人设 + 跨会话长期记忆(提示变量通道,per-preset 独立文件)+ 最近 20 轮历史窗口:调宿主 ctx.compaction.compactRegion() 把更早的内容压成一条摘要,让它们不再计入输入;自己不写任何会话事件(宿主摘要+写入+校验)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/history-window.js
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
* · 裁完轮数会掉到 N 上下,必须等到再次超过 N 才会触发 —— 天然限流,不会每轮都调。
|
|
25
25
|
* · 任何失败都被吞掉并跳过本次,**绝不影响正常对话**。
|
|
26
26
|
*/
|
|
27
|
+
import { appendFileSync } from 'node:fs';
|
|
27
28
|
import { homedir } from 'node:os';
|
|
28
29
|
import { join } from 'node:path';
|
|
29
30
|
|
|
@@ -139,6 +140,33 @@ export async function compactHistory(ctx, agent, keepTurns = DEFAULT_KEEP_TURNS,
|
|
|
139
140
|
/** 每个 agent 同时只允许一次压缩(压缩服务本身也拒绝并发,这里提前避开)。 */
|
|
140
141
|
const inFlight = new WeakSet();
|
|
141
142
|
|
|
143
|
+
/**
|
|
144
|
+
* 诊断日志:把每次 idle 的判断过程写进 `<DSH_HOME>/whale-history-window.log`。
|
|
145
|
+
*
|
|
146
|
+
* 为什么要写文件:宿主的 `ctx.logger` 只到控制台,用户看不到、我也读不到。
|
|
147
|
+
* 而这个插件是**静默跳过**式设计(任何异常都吞掉),没有日志就无法区分
|
|
148
|
+
* 「没收到 idle」「没拿到 compaction 服务」「范围不合格」「compaction 抛错」。
|
|
149
|
+
*/
|
|
150
|
+
function diagPath() {
|
|
151
|
+
return join(dshHome(), 'whale-history-window.log');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function diag(line) {
|
|
155
|
+
try {
|
|
156
|
+
appendFileSync(diagPath(), `${new Date().toISOString()} ${line}\n`, 'utf8');
|
|
157
|
+
} catch {
|
|
158
|
+
// 日志失败绝不能影响任何东西
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** 该会话当前有多少「真实用户轮」(诊断用)。 */
|
|
163
|
+
function countTurns(session) {
|
|
164
|
+
const nodes = session?.surface?.nodes ?? [];
|
|
165
|
+
let n = 0;
|
|
166
|
+
for (const seq of nodes) if (isUserTurn(session.eventAt(seq))) n++;
|
|
167
|
+
return n;
|
|
168
|
+
}
|
|
169
|
+
|
|
142
170
|
/**
|
|
143
171
|
* 挂载:agent 转为空闲时检查轮数,超过 keepTurns 就压缩掉更早的部分。
|
|
144
172
|
*
|
|
@@ -147,23 +175,66 @@ const inFlight = new WeakSet();
|
|
|
147
175
|
* - `keepTurns` 保留最近多少轮(默认 20)
|
|
148
176
|
*/
|
|
149
177
|
export function apply(ctx, config = {}) {
|
|
150
|
-
if (config.enable === false)
|
|
178
|
+
if (config.enable === false) {
|
|
179
|
+
diag('apply: enable=false,不挂载');
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
151
182
|
|
|
152
183
|
const keepTurns = typeof config.keepTurns === 'number' ? config.keepTurns : DEFAULT_KEEP_TURNS;
|
|
153
|
-
if (typeof ctx.on !== 'function')
|
|
184
|
+
if (typeof ctx.on !== 'function') {
|
|
185
|
+
diag('apply: ctx.on 不可用,不挂载');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 两个通道都试:Cordis 的服务既可以 ctx.get(name),也通常是 ctx[name]
|
|
190
|
+
const serviceVia = () => {
|
|
191
|
+
if (typeof ctx.get === 'function') {
|
|
192
|
+
const s = ctx.get('compaction');
|
|
193
|
+
if (s !== undefined) return { service: s, via: 'ctx.get' };
|
|
194
|
+
}
|
|
195
|
+
const s2 = ctx.compaction;
|
|
196
|
+
if (s2 !== undefined) return { service: s2, via: 'ctx.compaction' };
|
|
197
|
+
return { service: undefined, via: 'none' };
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const start = serviceVia();
|
|
201
|
+
diag(`apply: 已挂载 keepTurns=${keepTurns} 服务=${start.via}`);
|
|
154
202
|
|
|
155
203
|
ctx.effect(
|
|
156
204
|
() =>
|
|
157
205
|
ctx.on('agent/status', (payload) => {
|
|
158
|
-
|
|
206
|
+
const status = payload?.status;
|
|
159
207
|
const agent = payload?.agent;
|
|
160
|
-
if (
|
|
161
|
-
|
|
208
|
+
if (status !== 'idle') {
|
|
209
|
+
diag(`agent/status=${status}(非 idle,跳过)`);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (agent?.session === undefined) {
|
|
213
|
+
diag('idle 但 payload.agent.session 缺失');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (inFlight.has(agent)) {
|
|
217
|
+
diag('idle 但上一次压缩还在跑,跳过');
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const turns = countTurns(agent.session);
|
|
221
|
+
const { service, via } = serviceVia();
|
|
222
|
+
if (service?.compactRegion === undefined) {
|
|
223
|
+
diag(`idle turns=${turns} 但拿不到 compaction 服务(via=${via})`);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
const plan = planCompact(agent.session, keepTurns);
|
|
227
|
+
if (plan === null) {
|
|
228
|
+
diag(`idle turns=${turns} 未超过 ${keepTurns},无需压缩`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
162
231
|
inFlight.add(agent);
|
|
232
|
+
diag(`idle turns=${turns} 触发压缩 [${plan.startSeq}, ${plan.endSeq}] 压掉 ${plan.removedTurns} 轮`);
|
|
163
233
|
// 不 await:这是空闲期的后台整理,绝不能拖住/影响对话。
|
|
164
234
|
void Promise.resolve()
|
|
165
|
-
.then(() =>
|
|
166
|
-
.
|
|
235
|
+
.then(() => service.compactRegion(plan.startSeq, plan.endSeq, agent))
|
|
236
|
+
.then(() => diag('压缩成功'))
|
|
237
|
+
.catch((err) => diag(`压缩失败: ${String(err?.message ?? err)}`))
|
|
167
238
|
.finally(() => inFlight.delete(agent));
|
|
168
239
|
}),
|
|
169
240
|
'whale-history-window.idle-compact',
|