dsh-whale-loli 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.md +6 -1
- package/package.json +2 -2
- package/src/memory.js +43 -5
package/README.md
CHANGED
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
- **每轮都记**:不是挑着记。短对话直接记;长对话**先精简概括**再记(压成一两句说清聊了什么、结论是什么)。
|
|
26
26
|
- **80 条上限只算「滚动摘要」**:`# 固定记忆` 不占这个名额。
|
|
27
27
|
- **想长期留的就往上挪**:某条滚动摘要以后还用得上,就总结一句写进 `# 固定记忆`;否则到点会滚出去(并归档)。
|
|
28
|
-
- 上限有两道保险:注入时只取最近 80 条(文件里堆再多也只进 80
|
|
28
|
+
- 上限有两道保险:注入时只取最近 80 条(文件里堆再多也只进 80 条);**每轮结束(agent 转 idle)时自动**把文件本身裁到 80 条,超出的写进 `archive/<预设名>/`。
|
|
29
|
+
- 为什么放在 idle 而不是组装时:组装是热路径、每次都要跑,在那里写盘既慢又容易和 agent 正在编辑的文件打架。
|
|
30
|
+
- 不足 80 条时**完全不动文件**(不产生无谓写盘)。
|
|
31
|
+
- **记忆文件还是空的时候也会注入一段带绝对路径的提示**:否则新预设开局拿到一片空白,agent 连该往哪个文件写都不知道。
|
|
29
32
|
- 总长顶到 `maxChars`(16000)时,**先丢最旧的滚动摘要,再丢详细区**,固定记忆最后才动。
|
|
30
33
|
|
|
31
34
|
### 每轮先看什么
|
|
@@ -213,6 +216,7 @@ dsh-whale-loli/
|
|
|
213
216
|
├── verify-memory-injection.mjs # 证明变量通道有效、section 通道被丢弃
|
|
214
217
|
├── verify-memory-isolation.mjs # 实测三个记忆文件不串
|
|
215
218
|
├── verify-memory-rolling.mjs # 实测四区结构与 80 条滚动 + 归档
|
|
219
|
+
├── verify-memory-archive.mjs # 超 80 条时真的归档并瘦身(idle 触发,三预设互不覆盖)
|
|
216
220
|
├── verify-peers.mjs # 校验 peer 范围覆盖 harness 各发布档
|
|
217
221
|
├── verify-listing.mjs # 复刻插件目录的收录门槛自查
|
|
218
222
|
└── prove-collision.mjs # 复现并证明重复注册冲突
|
|
@@ -235,6 +239,7 @@ node verify-history-window.mjs # 真的裁到最近 N 轮(不切
|
|
|
235
239
|
node verify-memory-injection.mjs # 变量通道有效 / section 通道被丢弃
|
|
236
240
|
node verify-memory-isolation.mjs # 记忆文件端到端隔离(三个预设)
|
|
237
241
|
node verify-memory-rolling.mjs # 固定记忆永久 + 滚动摘要 80 条 + 归档
|
|
242
|
+
node verify-memory-archive.mjs # 超 80 条自动归档瘦身(idle 触发、三预设隔离)
|
|
238
243
|
node smoke-memory.mjs # 记忆变量冒烟
|
|
239
244
|
node verify-peers.mjs # peer 范围无预发布坑
|
|
240
245
|
node verify-listing.mjs --topics=dsh-plugin # 收录门槛自查
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-whale-loli",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "鲸鱼娘 Agent 预设(三个)— 极简模式工具面 + 鲸鱼娘·小鱼人设 + 跨会话长期记忆 + 真正生效的 20 轮历史窗口。记忆分「固定记忆」(永久保留)/「滚动摘要」(每轮都记,最多 80 条 FIFO
|
|
3
|
+
"version": "0.5.2",
|
|
4
|
+
"description": "鲸鱼娘 Agent 预设(三个)— 极简模式工具面 + 鲸鱼娘·小鱼人设 + 跨会话长期记忆 + 真正生效的 20 轮历史窗口。记忆分「固定记忆」(永久保留)/「滚动摘要」(每轮都记,最多 80 条 FIFO,超限每轮结束自动归档到 archive/<预设名>/)/「最近详细区」/「归档索引」四区,经提示变量注入(persona 为 complete:true,独立 section 会被宿主丢弃);history-window 用宿主表层替换真的裁掉 20 轮以前的历史,不调 LLM、不切断工具配对,并正确放行 agent/pre-step 的 waterfall。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"keywords": [
|
package/src/memory.js
CHANGED
|
@@ -547,21 +547,59 @@ export function apply(ctx, config = {}) {
|
|
|
547
547
|
|
|
548
548
|
/**
|
|
549
549
|
* 每次都重新读盘:改完文件下一个模型步就生效,不用重载宿主。
|
|
550
|
-
*
|
|
550
|
+
*
|
|
551
|
+
* 注意两点:
|
|
552
|
+
* 1. 必须返回**字符串** —— 变量值为空串时 interpolate 只是替换成空,不会报错。
|
|
553
|
+
* 2. **文件空(或不存在)时也要给出绝对路径**。否则新预设开局记忆是空骨架,
|
|
554
|
+
* agent 拿到的是一段空白,连该往哪个文件写都不知道(只能靠猜 ~/.dsh/…)。
|
|
555
|
+
* 所以空文件回一段带路径的提示,注入标记也照样出现,便于审计。
|
|
551
556
|
*/
|
|
552
557
|
const read = () => {
|
|
558
|
+
const path = memoryPath(file);
|
|
553
559
|
try {
|
|
554
560
|
const { text } = composeMemory(file, { maxChars, turnLimit, detailLimit });
|
|
555
|
-
if (!text)
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
561
|
+
if (!text) {
|
|
562
|
+
return [
|
|
563
|
+
`【鲸鱼记忆 · ${file}】`,
|
|
564
|
+
`文件:${path}`,
|
|
565
|
+
'(现在还是空的 —— 想到什么就直接写进这个文件;只写这一个,别动别的预设的记忆。)',
|
|
566
|
+
].join('\n');
|
|
567
|
+
}
|
|
568
|
+
return `【鲸鱼记忆 · ${file}】\n文件:${path}\n\n${text}`;
|
|
569
|
+
} catch (err) {
|
|
570
|
+
// 读失败也要让 agent 知道路径,而不是默默变成空串
|
|
571
|
+
return [`【鲸鱼记忆 · ${file}】`, `文件:${path}`, `(这次没读出来:${String(err?.message ?? err)})`].join('\n');
|
|
559
572
|
}
|
|
560
573
|
};
|
|
561
574
|
|
|
562
575
|
// 主通道:变量。人设 prefix 里写 {{variableName}},随 complete section 一起进提示。
|
|
563
576
|
ctx.effect(() => ctx.systemPrompt.variable(variableName, read), `whale-memory.variable(${variableName})`);
|
|
564
577
|
|
|
578
|
+
/**
|
|
579
|
+
* 每轮结束(agent 转为 idle)时,把超出 turnLimit 的滚动条目**归档并瘦身文件**。
|
|
580
|
+
*
|
|
581
|
+
* 为什么放这里而不是 `read()`:`read()` 是组装热路径(每次组装都会跑),
|
|
582
|
+
* 在那里写盘既慢又容易和 agent 自己的编辑打架 —— `pruneMemoryFile` 的注释里
|
|
583
|
+
* 也写明了「组装热路径不会调用」。而 idle 一轮只发生一次,时机正好。
|
|
584
|
+
*
|
|
585
|
+
* 不这么做的话:注入会按 turnLimit 截断(只喂最新 80 条),但文件本身无限增长,
|
|
586
|
+
* `archive/` 目录永远空着 —— 与「超过 80 条就不断覆盖」的约定不符。
|
|
587
|
+
*/
|
|
588
|
+
ctx.effect(
|
|
589
|
+
() =>
|
|
590
|
+
typeof ctx.on === 'function' &&
|
|
591
|
+
ctx.on('agent/status', ({ status }) => {
|
|
592
|
+
if (status !== 'idle') return;
|
|
593
|
+
try {
|
|
594
|
+
const { totalRoll } = composeMemory(file, { maxChars, turnLimit, detailLimit });
|
|
595
|
+
if (totalRoll > turnLimit) pruneMemoryFile(file, turnLimit, { detailLimit });
|
|
596
|
+
} catch {
|
|
597
|
+
// 整理失败不影响对话
|
|
598
|
+
}
|
|
599
|
+
}),
|
|
600
|
+
`whale-memory.trim(${file})`,
|
|
601
|
+
);
|
|
602
|
+
|
|
565
603
|
// 备用通道:仅在明确要求时注册独立 section(非 complete 预设才用得上)。
|
|
566
604
|
if (config.emitSection === true) {
|
|
567
605
|
const sectionName =
|