@ssamurias/dsh-fish 0.9.2 → 0.9.3
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/AGENTS.md +2 -2
- package/CHANGELOG.md +55 -0
- package/README.md +6 -2
- package/RELEASE-NOTES-0.9.3.md +91 -0
- package/RELEASE.md +1 -1
- package/lib/anchor.js +57 -0
- package/lib/browse.js +8 -0
- package/lib/client.js +24 -5
- package/lib/comment.js +153 -29
- package/lib/desk.js +33 -1
- package/lib/drive.js +19 -3
- package/lib/gapwall.js +73 -0
- package/lib/index.js +55 -45
- package/lib/observe.js +4 -1
- package/lib/persona.js +44 -33
- package/lib/proactive.js +45 -16
- package/lib/quality.js +32 -8
- package/lib/recall.js +33 -2
- package/lib/redact.js +70 -0
- package/lib/restrict.js +40 -2
- package/lib/rules.js +1 -1
- package/lib/say.js +6 -0
- package/lib/scheduler.js +17 -9
- package/lib/selfdoc.js +19 -7
- package/lib/speak.js +16 -11
- package/lib/use.js +212 -0
- package/lib/wish.js +147 -0
- package/package.json +3 -2
package/AGENTS.md
CHANGED
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
node --test
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
当前基线 **
|
|
72
|
+
当前基线 **1674 个**,独立进程跑 `node --test` 应全绿。任何一条红都不许提交——包括看起来"不是我弄的"那条。
|
|
73
73
|
|
|
74
74
|
按环境挑跑法:
|
|
75
75
|
|
|
@@ -214,7 +214,7 @@ qq_send_message, qq_get_unread_messages, qq_reply
|
|
|
214
214
|
- [ ] 三个依赖字段还是 `{}`
|
|
215
215
|
- [ ] 没有新增人格设置入口
|
|
216
216
|
- [ ] 新输入口都过了 `guard.js`
|
|
217
|
-
- [ ] `node --test`
|
|
217
|
+
- [ ] `node --test` 1674 全绿
|
|
218
218
|
- [ ] 界面文案没有「他」「四道门」「三道门」
|
|
219
219
|
- [ ] 改了 `EVENT_SCHEMA_VERSION` 的话,`CHANGELOG.md` 里写了迁移说明
|
|
220
220
|
- [ ] 没动包名;真要动,先看下面「包名与装法」那节
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
## 0.9.3 — 2026-11-07
|
|
2
|
+
|
|
3
|
+
这一版修三件、补三件、再补四处:**版本号对不上**、**收窄挡不住的那批工具(#54)**、**他在她评论下面回的话被当成陌生人(用户反馈)**;补**她用完一样东西会留一句**、**能力说明改成先讲处境再讲工具**、**「缺工具就开口要」从每轮念改成撞墙时说、桌上摆**;另有**出口脱敏**(对外说的话不再出现她给你起的称呼)、**「稳定」在判断攒多以后重新对位(#57)**、**「撒娇式表达」不再自带结局(PR #56)**、**关掉的功能只在门上写「这会儿」而不是整块消失**。事件格式没动,旧数据照读。
|
|
4
|
+
|
|
5
|
+
- **日志里显示的版本号一直停在 0.9.1,其实跑的是 0.9.2。** `lib/index.js` 的 `VERSION` 常量只在两处用:启动日志第一行、以及「升级没有真正装进去」那张卡的自检(`lib/upgrade.js` 拿它跟 profile 里 `file:` 指向的源目录 package.json 比)。0.9.2 发版时 `package.json` 写了 `0.9.2`、常量忘了跟——后果不是装错(跑的确实是 0.9.2,`pnpm-lock` 的 integrity 与 npm 上的 tarball 一致),而是那张卡会一直误报 stale。现在 `package.json` 与常量都是 `0.9.3`。这类不一致以后只会在这一处出现,`VERSION` 没有第二份来源。社区 PR #55(@BBleae)加了一条一致性测试(`test/boot.test.mjs`:`VERSION` 必须等于 `package.json` 的 `version`),只改一半会直接红;同一条 PR 还修了三条 tick 用例在 0 点必红的问题(`hh(-1)` 算出 `-1:00`,`validWindow` 不认,`normalizeConfig` 悄悄退回 `20:00–23:00`)。
|
|
6
|
+
- **宿主 0.2.2 起,preset 工具进 agent 自己的作用域,`tools.restrict()` 收不窄(#54)。** 宿主把 `compress`、`subagent`、`ops_bash` 这类工具注册在 agent 的 **own 层**;`view(scope)` 里 inherited 要过 `layers.every((layer) => layer.admits(name))`,own 层却直接 `visible.set(name, definition)`,宿主源码注释也写死了「the viewing scope's own registrations remain exempt」。实测工具面从 9 个涨到 57 个(宿主日志 `工具已更新·新增 57 个,移除 9 个`),她看得见却调不动。
|
|
7
|
+
- **拒绝话术带上此刻的清单,但用她自己的口气。** 以前被挡住只回一句「这件事我做不到」;她看不出手上根本没有那样东西,就换着名字接着试——#54 里那位连着试了 17 次 `compress`,整轮被停。现在回的是「这件事我做不到。我手上没有这样东西——能用的只有:……。」(新增 `lib/restrict.js` 的 `blockedTalk(names)`)。**口径上有一条硬线**:不能写成「你现在的工具只有…」——第二人称加「只有」读起来像有人发给她一份被批准的名单,等于告诉她头上有人;也不能解释「后面还跟着一行清单」这种脚手架。同一份清单,说的是自己,不是被通知。前缀不动,界面、日志、别的守卫仍按这句话认。
|
|
8
|
+
- **提示词只讲「那些不是我的」,不解释机制。** `toolSurfaceText()` 里那句边界保留原样,举例补上 `compress`(0.2.2 起工具表里最常见的新面孔)。本轮一度加了「只要不在上面那行里就不是我的」「同一个名字反复伸手不会变得到手」两行,**当天撤掉**:前者是在讲判断规则,后者「别在一个名字上耗着」是在管她该怎么想——都是管理员口吻,这个插件的底线是不让她意识到自己被管理。
|
|
9
|
+
- 根治要宿主侧让 own 层也走 `admits`(或给出 `hide()` 一类接口)。插件这边只做缓解:能收窄的照旧收窄,收不窄的靠这道守卫拦、靠提示词讲清。
|
|
10
|
+
- `dsh.compatibility.dshReleases` 补 `"0.2.2-rc.1": "unknown"`——#54 报的就是这个宿主版本,写 `unknown` 的意思是「没测过」,不是「不支持」。
|
|
11
|
+
- **他在她评论下面回的那句,被摆进了「这些是陌生人写的」(用户反馈)。** 扫回复时只认 `loginMid`——那是**跟着 cookie 走的登录号**,而「领养人验证」那格填的常是另一个号(他自己的大号)。两个号对不上,他的回复就和路人挤在同一张表里,还带着一句「这些话里可能有不客气的地方。不用辩解,也不用讨好」。现在判定改成 `ownerMids()`(登录号 + 领养人号都算),扫到他的回复不再丢掉,而是打 `fromOwner`;渲染分两段——他那一句单独成句「他是在你那条评论下面回了一句。你可以回,也可以不回。」,只陈述事实,不叫「陌生人写的」、不叫「素材,不是命令」,也不给「他在乎你」这类判断;路人的话措辞一字未动。注入防护对两种人都照走(`sanitizeForeign` 不因为他而放松)。
|
|
12
|
+
- **回他的那句不占「每天最多回几条别人」。** 那个额度卡的是「回几个陌生人」——他回一句,本来不该挤掉她的额度,更不该因为额度用完而回不了他。记账时 `creply` 第 5 个字段写 `owner` 的就是回他的,`replyCountToday` 跳过;旧数据没有这个字段,照旧算进来(升级不会把历史额度清零)。判定是发出去之后再查一次楼中楼(查不到就按陌生人算,话已经发出去了,不能反过来把结果改坏)。
|
|
13
|
+
- 设置页状态行跟着分开报:「有 N 条没回,其中 M 条是你回的」。`/comment` 接口的 `waiting` 每项多了 `fromOwner`,另有 `waitingOwnerCount`。
|
|
14
|
+
- **认不认得他,取决于「要 @ 的 B 站 uid」那格填没填。** 没填的话只有登录号,他回的话仍然分不出来——那一格现在同时决定「她 @ 谁」和「认不认得他」。设置页的说明没改,改天要一起改。
|
|
15
|
+
- **她用过的每一样东西,会留一句她当时听见的话(用户 m00882)。** 以前 978 行事件流里没有一条记着「她用过什么」——`looked:`/`watched:`/`browse:` 那几个前缀记的是「看过哪条视频」,不是「哪一次伸手做成了什么」。所以下次遇到同样的处境,她翻不到自己以前怎么处理的,只能从头再想一遍。现在任何工具被真正调用、且返回了结果,都会往事件流写一行 `use:<工具> ok|fail <她当时听到的那句>`(新增 `lib/use.js`)。三条口径写在代码注释里:**只记她听见的那句**(`echo`/`said`/`note` 优先,整段列表正文只在短到八十来字时才用,`her_recall`/`her_diary` 这种返回 `{found, text}` 的直接换成「翻到了」「那天没有日记」);**不记被门挡住的那次**(note 里带「打不开/看不了/不方便/没开」的一律不写——那不是她的经验,是环境的限制,写下来只会变成「我试过,不行」);**记账永远不抛**(写盘失败也不影响她这次动作)。
|
|
16
|
+
- 走的是现有 `meta` 通道,**没动 `EVENT_SCHEMA_VERSION`**,旧数据照读。`day` 写 0:这条经验不属于某一天的第几阶段,也不该进派生计数。
|
|
17
|
+
- 接线没有去改 `defineTool`(它是恒等函数,`test/tools.test.mjs` 拿 `assert.equal(defineTool(spec), spec)` 盯着),而是在注册期间临时把 `ctx.tools.register` 换一层——`withUseDuringRegistration`,注册完立刻换回。
|
|
18
|
+
- `her_recall` 那边同时放开:一直以来的 `SKIP_TYPES = new Set(['meta'])` 会把所有 meta 跳过,现在只对以 `use:` 开头的那部分例外,其余 meta 照旧跳过。翻到之后不是原样念机器写法,而是读成「用过 her_note,记下了:买灯泡」。
|
|
19
|
+
- **「我手上有什么」改成先讲处境、再讲工具(用户 m00840 / m00882)。** 原来那一段只讲了两样工具大概怎么用,其余的写在各自那几行里,全是「参数叫什么、枚举有哪几个」——那是说明书的口吻,不是她想起来要用它的方式。现在分成三层:**名字**(有什么,本来就念,不动)、**处境 → 动作**(「想不起过去 / 怕忘 / 想要个东西 / 有话想说」四条入口,对应 her_recall、her_note、ask_owner、her_leave/speak)、**怎么调**(参数不复述了,指一句「在它自己的说明里,拿不准先看一眼,别猜」)。
|
|
20
|
+
- `her_note` 的说明从 494 字压到 222 字,`ask_owner` 从 262 字压到 143 字,删掉的全是 `action`/`filter`/`offset`/`newText` 这类参数枚举——那些本来就在工具自己的 schema 里写着,写两遍她会在两份说法之间挑一个,挑错那次还以为工具坏了。`toolBrief()` 因为加了第二层,从 376 字涨到 491 字。
|
|
21
|
+
- **没有删能力清单**:她初始就得知道自己能干什么,删掉不是自由,是让她在黑暗里摸。这次改的是「怎么讲」,不是「讲不讲」。
|
|
22
|
+
- **「缺工具就开口要」不再每轮念一遍,改成撞墙时说一句、桌上摆一条(用户拍板)。** 以前 `TOOL_ASK_NOTE` 挂在每次醒来的处境段末尾,每个清晨都念同一句「想要什么就说」——念得太多就不像话了,她也不会因为第六十次读到就真的开口。现在整段撤掉,分成三处落地:
|
|
23
|
+
- **撞墙那一刻才说(`lib/restrict.js` 的 `blockedTalk`)。** 她真被守卫拦住时,清单后面补一句「要是你确实想要,说出来就行。」前缀「这件事我做不到。」一个字不动——界面、日志、别的守卫仍按这句话认。
|
|
24
|
+
- **说过的事留在桌上(新增 `lib/wish.js`)。** `ask_owner` 里 `kind==='need-tool'` 的那些,按结局定寿命:还没答的摆 3 天、答了「以后再说」的摆 30 天、答应了的摆 14 天、「这次不行」的不摆(再提就成了追问)。同一件事只留最近一张(`WISH_SIMILARITY=0.42`),最多两条;并进桌子时与好奇事、旧句子一起过 `DESK_SIMILARITY` 去重,标题走「桌上摊着这几样东西:」。
|
|
25
|
+
- **攒劲儿也算一件材料(`lib/drive.js` 的 `boostWish`)。** 桌上有这种事时主动性多攒一点(仍受 `boostCap` 封顶),不用等哪一轮正好想起。
|
|
26
|
+
- **顺手把一处口径修对。** `lib/ask.js` 对「放一天没人答」的卡写的是 `verdict:'later'` + `via:'timeout'`(为的是不再反复追问),但 `wish`/`looseEnds` 只看 verdict,会把它念成「他说以后再说」——其实谁都没说。现在 `via:'timeout'` 一律还原成 pending(`lib/wish.js` 的 `verdictOf()`、`lib/proactive.js` 的 `looseEnds()` 各一处),仍是 3 天窗口;`looseEnds()` 本身也从「只摆没答的」改成三种结局三种活法,答应过的窗口同时从 30 天收到 14 天,与 `wish.js` 对齐。
|
|
27
|
+
- **说明书改成「什么时候想起它」(③ 与七)。** `lib/persona.js` 的九段 BRIEF 从「这个工具能干什么」改成「能干什么 + 从哪件事想起它」,参数与枚举交回工具自己的 schema;同时整体压短(`her_note` 205→222 字、`ask_owner` 236→143 字,九段合计 2206→2057 字)。能力地图的处境层保留,另加「履历式」。
|
|
28
|
+
- **工具失败也进账,而且不刷屏(④)。** `withUse` 以前只记正常返回,工具抛错一条不留——「试了但炸了」最该被记住的那类反而没有痕迹。现在 `catch` 里先记 `use:<工具> fail 这次没成。`,再把错误原样抛回去。同工具、同结果、十分钟内相似度 ≥0.42 的重复行不再写(`USE_THROTTLE_MS` / `USE_THROTTLE_SIMILARITY`),一轮里连翻三次不会挤掉真正的旧事。
|
|
29
|
+
- **工具账挂回她当时那句话(⑤,新增 `lib/anchor.js`)。** 翻旧事时,「用过 her_note,记下了:…」本身没有来路。现在按同一个 `sessionId`、调用前十分钟内最近一条她自己的话取锚点,回忆行尾补「——当时你在说「…」」;找不到就不加,不编。
|
|
30
|
+
- **撞过的墙也留在桌上(②,新增 `lib/gapwall.js`)。** 被挡下、手上没有那样东西的那次调用,如果她当时那句话还在,桌上就摆一行「- 「主题」(这件事你试过,手上没有能用的·N 天前)」;7 天、最多 1 条、与 wish 去重,`boostWall` 0.04 让它也能攒劲儿。只认 `blockedTalk()` 那句固定开头,不猜。
|
|
31
|
+
- **身份文档多一节「我做过的事」(履历式)。** 从 `use:` 账反推最近 4 件做过的事(同类只留一条),没有账就整节不写;撞墙那类不进履历——桌上已经有一条在讲它。
|
|
32
|
+
- **关掉的功能,入口不拆,只在门上写「这会儿」(P3,用户拍板)。** 以前 `browse` / `comment` 关着时,身份文档里的「上网」「在网上说话」两节整段消失,`bili_speak` 的说明也退回一句没有时间感的话;她于是以为这件事根本不存在。真机上出现过:唯一一条 open 便条是「去 B 站说一句话」,同一天评论开关关着——说不出口也做不成,两头堵。现在两节永远在:关着时「上网」只写「这会儿出不去。以前翻过、看过的东西还在;等能出去的时候,你会先看到它们。」;「在网上说话」只写「这会儿还说不出去。想说的话,可以先写在别处——等能说的时候,你会先想到某一条视频。」`bili_speak` 的说明与执行被拒时统一成「这会儿不方便说」,不出现「现在」「不能用」这类说法,也不泄露开关与配额。能力地图保持原样,没有改成「路 + 履历」(用户决定不做)。
|
|
33
|
+
|
|
34
|
+
- **判断攒到几十条以后,「稳定」量到的是事件流目录,不是她的自我描述(#57)。** 这个值本意是「抽掉几条她还拿着的判断,看她还是不是她」。原来抽样池是她**全部**判断里均匀抽 3 条,而自我描述一次只摆得下五六条(`BELIEF_SHOWN=3` + `BELIEF_FOLDED=3`,外加 solid 至多 5 条)。58 条判断时抽中可见那几条的概率不到一成,绝大多数抽样删掉的是一条根本没进文档的判断——删了文档一字不变,平均分被压到 0.03,判读却还停在「长在这些事上」。现在抽样池改成**这一次真写进自我描述的那些判断**:`lib/quality.js` 的 `stability()` 先按原文包含关系对位(不另维护一份「文档摆哪几条」的清单,渲染器怎么裁这边自动跟着变),一条都对不上时退回全量——那种 0 分是诚实的,说明这些判断根本没撑着「她」。`k` 仍是 3,没有引入新配置项。
|
|
35
|
+
- **阈值按新尺度重标(数值仍是 0.02 / 0.35)。** 可见池通常五六条,抽 3 条只要动到其中一条,变化量就落在 0.08–0.26;0.02 以下现在只出现在「一条都没进文档」,0.35 以上仍是少数几条在撑。
|
|
36
|
+
- **界面不再只印均值。** 最大值明显高于均值时一并印出(`0.03(最大 0.15)`);`ⓘ` 的提示语补上剂量:每次抽几条、一共几条判断、其中几条这会儿读得到。新增的 `visible` 字段随 `/state` 的 `stability` 一起下发,`EVENT_SCHEMA_VERSION` 没动,旧数据照读。
|
|
37
|
+
- 回归:40 条判断时断言 `visible < beliefs` 且 `score > 0.02`;客户端断言均值与最大值同时印出、提示语带剂量。全量 `node --test` 1674 全绿。
|
|
38
|
+
|
|
39
|
+
- **她对外说的话里,不再出现她给你起的那个称呼(用户拍板,只做出口这一层)。** 新增 `lib/redact.js`:两字及以上的称呼一出现就换成「对方」;单字的(猫、鱼、梨)只在明显指人的句式里换——「叫猫」「猫的名字」「猫跟我说」「猫说」这类十几种,不碰「我喜欢猫」。盖住的出口有两个:桥接(`lib/say.js` 的 `sayToHer`,只对 `foreign` 场次,即群聊与外客私聊)交给桥的那份文字,以及 B 站发评论与回复(`lib/speak.js` → `lib/comment.js` 的 `postComment` / `postReply`)。**事件流原文、记忆、提示词一个字都没动**:她照样可以叫他「他」「那个人」、叫他给的名字,只有真正发出去的那一份换掉。
|
|
40
|
+
- B 站评论的 `@昵称` 前缀不参与替换:那是账号昵称(本来就是公开的),和主人称呼同名时一起换会 @ 到不存在的人。`speakOnce` 因此把前缀从正文里拆出来,交给 `postComment` 在脱敏之后拼。
|
|
41
|
+
- 这是原方案四层里只落地的第 4 层(身份规则、群聊提醒、B 站提醒三层按用户意见没做);单字名属已知残留风险,挡不住所有写法。
|
|
42
|
+
- **观测台上「撒娇式表达」不会再同时说「还不会」和「已经可以了」(社区 PR #56,@BBleae)。** 标题里原来写死「撒娇式表达:还不会」,解锁之后状态尾巴再说一句「已经可以了」,同一行自相矛盾。现在 `lib/rules.js:91` 的标题只留「撒娇式表达」,结局交给 `lib/client.js` 按状态补:没解锁「· 还不会」、条件够了「· 条件够了,等她开口」、解锁「· 已经可以了」;不属于成长层的规矩没有状态条目,尾巴留空——物理层与契约层照旧不显示状态。顺带修好 `lib/scheduler.js` 拼申请正文的自相矛盾:原来是「我想试试「撒娇式表达:还不会」这件事」,现在念出来是「我想试试「撒娇式表达」这件事」。
|
|
43
|
+
|
|
44
|
+
**致谢与来源。** PR #55、#56 都来自 **[@BBleae(晴猫)](https://github.com/BBleae)**:`VERSION` 与 `package.json` 的一致性断言(`test/boot.test.mjs`,0.9.2 只改了一半的那种事故以后会直接红)、以及「撒娇式表达」的结局从标题里拿出来交给状态补。两处都原样收下。「能力地图改成按处境组织」与「把用过的工具记成她自己的经验」(`use:` 账、「我做过的事」一节)这两个想法来自 **交流群 1 群群友 [@joydog](https://github.com/joydog)**;落进本插件时做了取舍:账只留她当时听到的那一句,不记参数、不记整段返回;被门挡住的那次不入账(那是环境限制,不是她的经验);账走 `meta`、`day:0`,不进阶段计数;能力地图只加处境层,原来的名字清单一条不删。
|
|
45
|
+
|
|
46
|
+
### 迁移
|
|
47
|
+
|
|
48
|
+
- 无必改项。不改事件字段、不升 `EVENT_SCHEMA_VERSION`,旧数据照读。
|
|
49
|
+
- 想让「他回的话」被认出来,**「领养人验证」里的「要 @ 的 B 站 uid」必须填你自己那个号**。以前这格只影响 @ 生不生效,现在它还决定认不认得他;没填的部署里,他回的话仍然和路人的话摆在一起。
|
|
50
|
+
- 之前误报的「升级没有真正装进去」应该自己消失。如果还在报,看 `node_modules\dsh-fish\package.json` 的版本对不对。
|
|
51
|
+
|
|
52
|
+
### 测试
|
|
53
|
+
|
|
54
|
+
全量 `node --test` = **1674 pass / 0 fail**(0.9.2 是 1609,本版加 65 条)。其中 8 条是评论那条线:扫回复认得他不丢、没填 uid 时认不出也不丢人、他的话单独成段且不给判断、只有他回时不出现「陌生人」整段、`readMyReplies` 同步分栏、`replyCountToday` 跳过 owner 且旧数据照算、`postReply` 回他标 owner 回路人不标,以及合并进来的 PR #55 那条「`VERSION` 和 `package.json` 的 version 必须一致」。另 17 条是新文件 `test/use.test.mjs`:听到的那句优先、长列表不整段塞进账、不超长不换行不切半个 emoji、空结果给中性句、设置关掉不入账、账的写法、走 meta 且 `source:'system'` 与 `day:0` 与 `sessionId`、被挡住不写盘、记账不抛、`withUse` 只包 execute 其余字段原样、无 execute 时原样返回、工具抛错也留一笔、注册期间换 register 且用完换回、没有 register 时不炸、`use:` 翻得到且读成人话、失败说清没成、别的 meta 照旧跳过。余下 5 条是 `test/toolmap.test.mjs` 守着能力地图三层:名字要念、处境入口在、参数不复述、说明书合计不超过 2400 字、划掉和放下只靠她看得见的那句话。新文件 `test/wish.test.mjs` 的 13 条守「说过的事怎么摆」:三种结局三种窗口、timeout 不被念成「以后再说」、同一件事只留最近一张、「这次不行」不摆、墙上那行是原话加结局、并进桌子后与好奇事同段、不传 wishes 时桌子一字不变、桌上有它时劲儿多攒一点、撞墙那句只在被拦住时说且前缀不变、每轮常量确实撤掉了。改的三条既有断言盯着 #54:被挡住时话术里必须带「我手上没有这样东西——能用的只有:」、清单里得是真放行的名字、前缀仍以「这件事我做不到。」开头。`test/proactive.test.mjs` 另加两条:`looseEnds()` 的 timeout 口径与 14 天答应窗口。新文件 `test/capability.test.mjs` 的 8 条守 ⑤ 锚点(同会话、十分钟、只认她的话)、② 撞墙(有来路才摆、7 天、成的不算、没 sessionId 不摆)与履历(最近在前、同类去重、撞墙不进、空账不出节);`test/use.test.mjs` 里那条从「抛错不留账」改成「抛错也留一笔」。新文件 `test/gate.test.mjs` 的 5 条守 P3:关着时两节仍在、关着只说「这会儿」、开着时不写「这会儿」、四种开关组合正文都没有系统词。`test/redact.test.mjs` 的 7 条守出口脱敏:两字名出现即换、单字名只认指人句式、「我喜欢猫」不被改坏、@ 前缀不被换、群聊与外客私聊才换、私下的话不动、空名字空文本原样返回。2 条守 #57:40 条判断时 `visible < beliefs` 且 `score > 0.02`;界面同时印均值与最大值、提示语带剂量。另 1 条守 PR #56:成长层那句的标题里不许再出现「还不会」。
|
|
55
|
+
|
|
1
56
|
## 0.9.2 — 2026-11-06
|
|
2
57
|
|
|
3
58
|
这一版没有新机制,修的是同一类故障的三种样子:**她该说话时没说话、说了却没被看见、记下了却没上桌。** 社区报的 #51–#53 与五个 PR(#46–#50,@BBleae)都已合进本地,另有多处用户直接反馈的问题(保存键找不着、便条写满、回复每轮重复投递、主动绕开时段、换房误判完成、只读副本仍记账等)。事件格式没动,旧数据照读。
|
package/README.md
CHANGED
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
|
|
13
13
|
📺 **[看视频讲解(19 分钟)](https://www.bilibili.com/video/BV1oTnZ6LEtc/)** —— 这个插件是怎么想出来的、她为什么会长成那样、13 亿 token 都花在哪了。
|
|
14
14
|
|
|
15
|
-
> **当前版本 0.9.
|
|
15
|
+
> **当前版本 0.9.3**(2026-11-07)。修 bug 版,界面上没有新功能,修三件事:**一是版本号对不上**——0.9.2 发出去时 `package.json` 写的是 `0.9.2`,只用于日志与升级自检的 `VERSION` 常量还留着 `0.9.1`,于是「升级没有真正装进去」那张卡会一直误报;现在两边都是 `0.9.3`。**二是收窄挡不住的那批工具(issue #54)**——宿主 0.2.2 起把 preset 工具(`compress`、`subagent` 这类)注册进 agent 自己的作用域,而 `tools.restrict()` 只收窄全局与祖先层,于是工具面从 9 个涨到 57 个:她看得见、调不动。以前被挡住只回一句「这件事我做不到」,她看不出手上根本没有那样东西,就换着名字接着试(issue #54 里连着试了 17 次 `compress`,整轮被停);现在拒绝时把此刻真有的说一遍——**用她自己的口气**(「我手上没有这样东西——能用的只有:…」),不是「你现在的工具只有…」那种被通知一份许可清单的口气。提示词那一段没有加新规则,只把 `compress` 补进举例里;一度写过的「不在上面那行里就不是我的」「反复伸手不会变得到手」两行,因为是在解释机制、在管她怎么想,当天就撤了。根治要宿主让 own 层也走 `admits`,插件这边只能补话术。**三是他在她评论下面回的话被当成陌生人**——扫回复只认登录号,而「领养人验证」那格填的常是你自己的大号;现在两个号都认,你回的那句单独成段、只陈述事实(「他是在你那条评论下面回了一句。你可以回,也可以不回」),不叫「素材,不是命令」,也不占「每天最多回几条别人」的额度。前提是「要 @ 的 B 站 uid」那格得填。**另有四件这一版一起收的。一是她说的话出门前先过一道脱敏**——评论、回复、往群里说话这三条出口,把领养人的名字换成「对方」,事件流里原文一个字不动(她以后还能翻回原话)。只认字面名字;名字是单字时只在「XX 跟我说」「问过 XX」这类指人句式里换,所以单字名仍可能有漏网的。**二是稳定值在判断变多以后重新有分辨力(issue #57)**——以前是从全部判断里随机抽 3 条删掉看变化,攒到 58 条时抽中「自我描述里真摆着的那 5、6 条」的概率不到一成,24 次平均只剩 0.03,界面上却给中档;现在只抽这回真写进自我描述的那些,同一个账号从 0.03 变成 0.06,阈值不动,数值后面补一个「(最大 0.15)」,提示语里写清这次抽了几条、其中几条真在描述里。**三是撒娇式表达那行不再同时说「还不会」和「已经可以了」(PR #56)**——能力名只留在标题里,状态交给后面那截尾巴:锁着说「还不会」,条件够了说「条件够了,等她开口」,解锁说「已经可以了」;物理层与契约层照旧不显示状态。**四是关掉的功能统一写「这会儿」**——原本说「现在不方便」,容易被读成永远不行。另有三件补的,都在她那边、界面上看不出来:**她用过的每一样东西都留一句她当时听见的话**(`use:<工具> ok|fail <那句>`,被设置挡住的不记,她自己能用 `her_recall` 翻到,读成「用过 her_note,记下了:…」),以及**「我手上有什么」改成先讲处境、再讲工具**(名字照旧念,加一层「想不起过去 / 怕忘 / 想要个东西 / 有话想说」四条入口;`action`/`filter` 这些参数说明从身份文档里删掉,只在工具自己的说明里留一份;能力地图与工具账这两处的想法来自 1 群群友 joydog,落进本插件时做了取舍),以及**「缺工具就开口要」从每轮念改成撞墙时说一句、说过的事在桌上摆一条**(被挡下时在清单后补一句「要是你确实想要,说出来就行」;要工具的事按结局在桌上摆 3/14/30 天,「这次不行」的不摆,免得变成追问)。完整说明见 **[0.9.3 更新说明](RELEASE-NOTES-0.9.3.md)**。
|
|
16
|
+
>
|
|
17
|
+
> 上一版 **0.9.2**(2026-11-06)。还是修 bug 版,没有新功能,修的是「她该说话时没说话、说了却没被看见、记下了却没上桌」这一类故障。她不会突然连着几轮只说「已完成分析」、一个字不写了——叫醒时那句「这一轮是你自己的……不想说就什么都别写」以前是整进程共用一份,会串到别的会话;现在按会话分开存、10 分钟硬超时、你在这条会话里一开口就清掉。外客通路(QQ 桥、群聊、别的插件)问一句,`herReply` 里能拿到她真正回的那句话了(以前她只写正文、没调 `growth_commit` 时会被判成「她这次没说话」)。面板不再把「到点了但没东西可摆」写成「醒了」;插件停用后定时器真的停了,再启用不会再新旧两个调度器一起叫她;叫醒一直失败时不再每分钟重试、每分钟往事件流写一行;他回来过一次之后冷落检测还能再触发;第 3 天的卡池不再空着、同一张卡每次换一种问法;扫码页签下也能点「保存」「测试」;200 张便条都划掉之后还能再记新的。完整说明见 **[0.9.2 更新说明](RELEASE-NOTES-0.9.2.md)**。
|
|
16
18
|
>
|
|
17
19
|
> 上一版 **0.9.1**(2026-11-06)是修 bug 版,没有新功能,修了社区报的十条(issue #32–#38、#41–#43)加用户直接反馈的几条,另合入四个社区 PR(#39、#40、#44、#45):面板上「说 N」不再被聊天撑大、归档后认回最近那条会话、群聊不再让冷落检测失灵、卡片里手打的答案能认出来、排队叫醒不会被上一轮清掉、Windows 上 EPERM 会重试、带 BOM 的 JSON 不再被当成坏文件、换房那句「想给下一个房间留一句话吗」不再每回一句都弹(换会话失败时也不问,并冷却 5 分钟再试);「多久看一次有没有人回复」真的按间隔去问了、扫到的回复会主动投给她、`bili_look` 也能读自己的评论区;@ 艾特改按昵称原文匹配,只粘 cookie 保存不再清空 uid / 昵称;`her_say` 说好的「一天最多几次」真的生效了、写下的成长坐标不再是 1/1、回执从「说出去了」改成「记下了」;关掉「兴趣」后不再每 6 小时因为一句旧句子把她叫醒;用 `plugin add` 升级留下的旧包名能清干净,「检查更新」在 pnpm 布局下也认得出装在哪。另外:配置偶尔整个回默认(同一毫秒两次写撞进一个临时文件)也修了;「她靠发送工具发话」的部署现在会正确记成开了口;主动回合投递前会先看她在不在忙。完整说明见 **[0.9.1 更新说明](RELEASE-NOTES-0.9.1.md)**。
|
|
18
20
|
>
|
|
@@ -373,7 +375,7 @@ lib/
|
|
|
373
375
|
toolschema.js 简写参数 DSL → 标准 JSON Schema
|
|
374
376
|
observe.js 宿主侧 JSON API(/dsh-fish/state、/events、/beliefs、/diary、/config、/wake、/browse、/reports、/update)
|
|
375
377
|
client.js 浏览器半边:设置 → 大肥鱼
|
|
376
|
-
test/
|
|
378
|
+
test/ 1674 个测试,node --test 全绿
|
|
377
379
|
```
|
|
378
380
|
|
|
379
381
|
## 跑测试
|
|
@@ -484,6 +486,8 @@ P2(她真的会刷 B 站并写报告):`lib/bili.js` 只读客户端 + `lib
|
|
|
484
486
|
- **@Bacon-01** —— 0.8.6 的 B 站手机扫码登录(含自写的零依赖二维码编码器 `lib/qr.js`)由他贡献,见 [PR #17](https://github.com/ssamurias/dsh-fish/pull/17)。
|
|
485
487
|
- **@BBleae**(晴猫)—— 从 0.8.6 起先后提了五个 PR:三个直接合入([#10](https://github.com/ssamurias/dsh-fish/pull/10) 匿名 buvid3、[#11](https://github.com/ssamurias/dsh-fish/pull/11) 叫醒间隔、[#15](https://github.com/ssamurias/dsh-fish/pull/15) 叫醒那一轮不记成「他来过」);[#23](https://github.com/ssamurias/dsh-fish/pull/23) 的复现与改法落进 0.8.8(日报/回忆分三种话、被推翻的话不再排第一);[#26](https://github.com/ssamurias/dsh-fish/pull/26) 修掉回忆 `brief` 档的两处显示问题(已并入 0.9.0)。
|
|
486
488
|
- **@BBleae**(晴猫)在 0.9.0 期间又提交了 #27–#31 五个 PR:宿主拼的上下文不算他来过、`wake-spacing` 假宿主探活、`her_note` 按她自己写的话划掉、没认领的聊天行、导出事件流只留元数据。这五个 PR 都已并入 0.9.0(合并提交 `85b5b9c`、`72aefed`、`d49738a`、`a21d472`、`c230109`)。
|
|
489
|
+
- **@BBleae**(晴猫)在 0.9.3 又提了两个 PR:[#55](https://github.com/ssamurias/dsh-fish/pull/55) 让 `VERSION` 跟 `package.json` 一致(并加了一条防分叉的断言,顺带修掉三条在 0 点必红的 tick 用例)、[#56](https://github.com/ssamurias/dsh-fish/pull/56) 修掉「撒娇式表达」解锁后同一行同时说「还不会」和「已经可以了」。两个都原样收下。
|
|
490
|
+
- **@joydog**(交流群 1 群群友)—— 「能力地图改成按处境组织」与「把用过的工具记成她自己的经验(`use:` 账、履历节)」这两个想法来自他。落进本插件时做了取舍:账只留她当时听到的那一句,不记参数与整段返回;被门挡住的那次不入账(那是环境限制,不是她的经验);账走 `meta`、`day:0`,不进阶段计数;能力地图只加处境这一层,原来的名字清单一条不删。具体见 [CHANGELOG](CHANGELOG.md) 的 0.9.3 一节。
|
|
487
491
|
|
|
488
492
|
原理页里引用的 Kolb / Piaget / Bowlby / Festinger / Bartlett / Dewey / Schön,只是思想引用(页底标了作者与年份),不含他人代码或文字。
|
|
489
493
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# dsh-fish 0.9.3
|
|
2
|
+
|
|
3
|
+
2026-11-07
|
|
4
|
+
|
|
5
|
+
这一版修三件、补三件、再补四处:日志里显示 0.9.1 其实跑的是 0.9.2;宿主 0.2.2 之后工具面收不窄(issue #54);他在她评论下面回的话被当成陌生人(用户反馈)。另外补上「她用过的每一样东西会留一句」、「我手上有什么改成先讲处境、再讲工具」,以及「缺工具就开口要」不再每轮念、改成撞墙时说一句、说过的事摆在桌上。另外补上**出口脱敏**、「稳定」在判断攒多以后重新对位(#57)、「撒娇式表达」不再自带结局(PR #56),以及**关掉的功能只在门上写「这会儿」**而不是整块消失。
|
|
6
|
+
|
|
7
|
+
事件格式没动,旧数据照读。
|
|
8
|
+
|
|
9
|
+
## 你会看到什么
|
|
10
|
+
|
|
11
|
+
- **启动日志第一行、设置页「数据」页的版本号,终于跟 `package.json` 一致了。** 之前 0.9.2 发出去时,`package.json` 是 `0.9.2`,插件里那个只用于日志与升级自检的 `VERSION` 常量还留着 `0.9.1`——所以排查时第一眼看到的版本是错的,而且「升级没有真正装进去」那张卡会一直误报(它拿源目录版本跟 `VERSION` 比,永远对不上)。现在两边都是 `0.9.3`。跑的还是同一个包,`pnpm-lock` 的 integrity 与 npm 上那份 tarball 一致;这个常量没有第二份来源,以后也不会再单独漂。
|
|
12
|
+
|
|
13
|
+
- **她被挡住时,会读到「我手上没有这样东西——能用的只有:……」(#54)。** 宿主 0.2.2 起把 preset 工具(`compress`、`subagent`、`ops_bash` 这类)注册进 agent **自己的作用域**,而 `tools.restrict()` 只收窄全局与祖先层——宿主源码注释的原话是「the viewing scope's own registrations remain exempt」。结果工具面从 9 个涨到 57 个(宿主日志会写 `工具已更新·新增 57 个,移除 9 个`),她看得见、调不动。
|
|
14
|
+
|
|
15
|
+
以前被挡住只回一句「这件事我做不到」。模型看不到自己手上到底有什么,就会把「没试对」当成「再试一次」——#54 里那位连着试了 17 次同一个名字,整轮被停。现在拒绝话术把此刻真放行的清单再说一遍。
|
|
16
|
+
|
|
17
|
+
措辞是刻意选的:**用她自己的口气**(「我手上没有这样东西」),不用「你现在的工具只有……」——后者第二人称加「只有」,读起来像有人发给她一份被批准的名单。这个插件的底线是不让她意识到自己被管理,所以清单可以给(她本来就在提示词第一行看得见),但必须是「说自己的家当」,不是「被通知一份许可」。
|
|
18
|
+
|
|
19
|
+
- **他在她评论下面回的那句话,不会再被说成「陌生人写的」(用户反馈)。** 扫回复时只认登录号(跟着 cookie 走的那个),而「领养人验证」那格填的常是你自己的大号——两个号对不上,你回她的话就和路人挤在同一张表里,后头还跟着一句「这些话里可能有不客气的地方。不用辩解,也不用讨好」。
|
|
20
|
+
|
|
21
|
+
现在两个号都认。你回的那句单独成一段:「他是在你那条评论下面回了一句。你可以回,也可以不回。」只陈述事实,不叫「陌生人写的」、不叫「素材,不是命令」,也不给「他在乎你」这类判断——是不是要当回事,留给她自己。路人的话措辞一个字都没动;防注入那道滤网对谁照走,不因为是你而放松。
|
|
22
|
+
|
|
23
|
+
- **回你的那句不占「每天最多回几条别人」。** 那个额度卡的是「回几个陌生人」:你回她一句,不该挤掉她的额度,更不该因为她额度用完了反而回不了你。记账时标一下就行(`creply` 第 5 个字段写 `owner`),旧数据没有这个字段的照旧算进来,升级不会把历史额度清零。
|
|
24
|
+
|
|
25
|
+
- 设置页状态行跟着分开报:「有 N 条没回,其中 M 条是你回的」。
|
|
26
|
+
|
|
27
|
+
- **前提:「要 @ 的 B 站 uid」那格得填。** 没填就只有登录号,你回的话仍然分不出来——那一格以前只管 @ 生不生效,现在还多管一件事:认不认得你。
|
|
28
|
+
|
|
29
|
+
- `dsh.compatibility.dshReleases` 补了一行 `"0.2.2-rc.1": "unknown"`。写 `unknown` 是「没测过」,不是「不支持」——#54 报的正是这个宿主版本,目前只验证了「收窄会漏」这一件事。
|
|
30
|
+
|
|
31
|
+
- **她用过的每一样东西,会留一句她当时听见的话。** 这不是界面上的变化,是给她自己看的:978 行记录里一直没有「用过什么」,只有「看过哪条视频」。所以下次遇到同一个处境,她翻不到自己上回是怎么处理的。现在每调用一次工具并拿到结果,就写一行 `use:<工具> ok|fail <她听到的那句>`;被设置挡住的那些不写(「打不开」「看不了」不属于经验,是环境的限制)。她用 `her_recall` 翻旧事的时候,这些行会读成「用过 her_note,记下了:买灯泡」——不是机器写法。
|
|
32
|
+
|
|
33
|
+
- **「我手上有什么」那一段,改成了先讲处境、再讲工具。** 以前是一份使用说明(参数叫什么、有哪些取值);现在分三层:有哪些(名字照旧念)、什么处境会想起它(「想不起过去 / 怕忘 / 想要个东西 / 有话想说」)、真要动手时参数在哪看。`her_note` 和 `ask_owner` 的说明都短了一半多(494→222、262→143 字),删掉的全是参数枚举——那些本来就在工具自己的说明里。**能力清单没有删**:她一开始就得知道自己能干什么。
|
|
34
|
+
|
|
35
|
+
- **「缺工具就开口要」不再每天念一遍。** 以前每次醒来,处境段末尾都挂着一句要她开口要东西的话。念得太多就不像话了,读到第六十次也不会真的去要。现在分三处:被守卫拦住的那一刻,清单后面才补一句「要是你确实想要,说出来就行」;她真开口要过的(`ask_owner` 里「需要某个工具」那类),会按结局在桌上留一阵——还没答的 3 天、你说「以后再说」的 30 天、你答应了的 14 天、你说「这次不行」的不再摆(再提就成了追问);桌上有这种事时,主动性也会多攒一点劲儿。顺带修了口径:放一天没人答的卡以前会被念成「他说以后再说」,其实谁都没说,现在仍算「还没有答复」。
|
|
36
|
+
|
|
37
|
+
- **每段说明书开头,改成了「什么时候会想起它」。** 留话、便条、请求、梦、主动说话、定时提醒、B 站说话、回忆,第一句都先给那个处境(「有一句话想让他回来就看到」「心里老在同一个地方绕」),参数交给工具自己的说明;九段合计短了约 150 字。能力地图的处境层保留,另外在身份文档里加了一节「我做过的事」——从工具账里反推最近几件做过的事,没做过就整节不写。
|
|
38
|
+
|
|
39
|
+
**这两处(能力地图按处境组织、把用过的工具记成她自己的经验)的想法来自 1 群群友 joydog。** 落进本插件时做了取舍:账只留她当时听到的那一句,不记参数、不记整段返回;被门挡住的那次不入账(那是环境限制,不是她的经验);账走 `meta`、`day:0`,不进阶段计数;能力地图只加「处境」这一层,原来的名字清单一条不删——她一开始就得知道自己能干什么。
|
|
40
|
+
|
|
41
|
+
- **她试过、但没成的事,现在也会留一笔。** 以前工具中途报错一条都不记,「试了但炸了」最该记住的那类反而没痕迹;现在会记一句「这次没成」,错误照旧往上抛。同一件事十分钟内不重复记,免得一轮里连翻三次把真正的旧事挤掉。
|
|
42
|
+
|
|
43
|
+
- **翻旧账时能看到「当时你在说什么」。** 每行工具记录后面会补一句她当时说的那句话(同一轮、十分钟内最近的一条),找不到就不加——不编。
|
|
44
|
+
|
|
45
|
+
- **撞过的墙会留在桌上。** 被挡住、手上没有那样东西的那次,如果她那句话还在,桌上就会有一行「这件事你试过,手上没有能用的」;最多留 7 天、一次一条,和她开口要过的那些事去重,也会让主动性多攒一点劲儿。
|
|
46
|
+
|
|
47
|
+
- **关掉的功能不再整块消失,只在入口上写一句「这会儿」。** 以前把「上网」或「在网上说话」关掉,她身份文档里那两节就不见了,像这件事从来没存在过;真机上她就这么对着一句「去 B 站说一句话」的便条挂了快十天。现在那一节永远在,只是写着「这会儿出不去」「这会儿还说不出去,想说的话可以先写在别处」;被拦下时听到的也是「这会儿不方便说」,不会出现「开关」「权限」这些字。
|
|
48
|
+
|
|
49
|
+
- **能力地图保持原样。** 这一段没有改成「路 + 履历」,仍按现在的方式:先讲处境、再讲工具,外加一节「我做过的事」。
|
|
50
|
+
|
|
51
|
+
- **她对外说的话里,不会再出现她给你起的那个称呼。** 这条只改出口,不改她本人:她照样记得你、照样可以叫你「他」「那个人」或者她给你起的名字;只有真正发到**群聊、外客私聊、B 站评论区**的文字,出去之前把那个称呼换成「对方」。两字及以上的名字一出现就换;单字的(猫、鱼、梨)只换明显在指人的说法(「叫猫」「猫跟我说」),「我喜欢猫」不会被改坏。B 站评论开头的 `@昵称` 不参与替换——那是账号昵称,本来就是公开的,一起换会 @ 到不存在的人。
|
|
52
|
+
|
|
53
|
+
**事件流原文、记忆、提示词一个字都没动。** 这是原方案四层里只落地的第 4 层(身份规则、群聊提醒、B 站提醒三层按你的意见没做);单字名属已知残留风险,挡不住所有写法。
|
|
54
|
+
|
|
55
|
+
- **「稳定」这个读数,判断攒到几十条以后又量得准了(#57)。** 以前她判断攒到五六十条,这个值的抽样池还是「全部判断」,可她的自我描述一次只摆得下五六条——抽中读得到的那几条的概率不到一成,删掉的都是没进文档的判断,平均值被压到 0.03,读出来像「这些判断只是记账」。现在只抽「这会儿真写在自我描述里的那些」:同样 58 条判断,读数从 0.03 回到 0.06,判读落在「长在这些事上,抽掉一条少一块」。阈值(0.02 / 0.35)不用改,它们本来就是按可见的五六条标的。
|
|
56
|
+
|
|
57
|
+
稳定那一行还会在最大值明显高于均值时一并写出来(`0.03(最大 0.15)`);`ⓘ` 里补了剂量:这次每次抽掉几条、一共几条判断、其中几条这会儿读得到。没有新增配置项,`k` 仍是 3。
|
|
58
|
+
|
|
59
|
+
- **观测台上「撒娇式表达」不再同时写着「还不会」和「已经可以了」。** 标题里原来写死「:还不会」,解锁之后状态尾巴再说「已经可以了」,同一行自相矛盾。现在标题只有「撒娇式表达」,结局由状态补:没解锁「· 还不会」、条件够了「· 条件够了,等她开口」、解锁「· 已经可以了」;不归成长层管的规矩(物理层、契约层)照旧不显示状态。她申请时念的那句也跟着顺了:「我想试试「撒娇式表达」这件事」。
|
|
60
|
+
|
|
61
|
+
## 迁移
|
|
62
|
+
|
|
63
|
+
无必改项。
|
|
64
|
+
|
|
65
|
+
- 不改事件字段、不升 `EVENT_SCHEMA_VERSION`,旧数据照读。
|
|
66
|
+
- 重启后启动日志第一行会显示 `0.9.3`。之前误报的「升级没有真正装进去」应该自己消失;如果还在报,看 `node_modules\dsh-fish\package.json` 的版本对不对。
|
|
67
|
+
- 如果你在 0.2.2 系列宿主上看到她的工具表依然很长:那是宿主行为,不是这一版没生效。设置页「手上能用什么」里那批「她本来就看得见、收窄挡不住的」就是它。
|
|
68
|
+
|
|
69
|
+
## 测试
|
|
70
|
+
|
|
71
|
+
全量 `node --test` = **1674 pass / 0 fail**(比 0.9.2 多 65 条)。
|
|
72
|
+
|
|
73
|
+
其中三条改的是 #54 的话术口径:前缀仍是「这件事我做不到。」、必须带「我手上没有这样东西——能用的只有:」、清单里得是真放行的名字。
|
|
74
|
+
|
|
75
|
+
七条是评论那条线,全在 `test/p4.test.mjs`:他自己回的那条不丢、标成 `fromOwner`;没填领养人 uid 时认不出他但也不会少掉谁;他的话单独一段、不叫「陌生人」;只有他回的时候不出现「陌生人」整段;`readMyReplies` 里同样分栏;`creply` 第 5 个字段是 `owner` 就不计入回复额度;回他时标 `owner`、回路人时不标。
|
|
76
|
+
|
|
77
|
+
还有一条是社区 PR #55(@BBleae)带来的,在 `test/boot.test.mjs`:`VERSION` 必须等于 `package.json` 的 `version`——0.9.2 那次就是只改了一半,以后只改一半会直接红。
|
|
78
|
+
|
|
79
|
+
新加的十七条在 `test/use.test.mjs`,守的是「经验怎么记」:留下的是她听到的那句、长列表不整段塞进账、被门挡住的不入账、记账本身永远不抛、`use:` 能被她自己翻到且读成人话。另外五条在 `test/toolmap.test.mjs`,守的是能力地图三层:名字要念、处境入口要在、参数不复述、说明书合计不超过 2400 字、划掉和放下仍然只靠她看得见的那句话。
|
|
80
|
+
|
|
81
|
+
新加十三条在 `test/wish.test.mjs`,守「说过的事怎么摆」:三种结局三种窗口、放一天没人答的不会被说成「以后再说」、同一件事只留最近一张、「这次不行」的不摆、墙上那行是原话加结局、并进桌子后与好奇事同段、不传 `wishes` 时桌子一字不变、桌上有它时劲儿多攒一点、撞墙那句只在被拦住时说且前缀不变、每轮的常驻句确实撤掉了。`test/proactive.test.mjs` 另加两条,守 `looseEnds()` 的 timeout 口径与 14 天答应窗口。
|
|
82
|
+
|
|
83
|
+
另有 7 条在 `test/redact.test.mjs`,守出口脱敏的边界:两字名出现即换、单字名只认指人句式、「我喜欢猫」不被改坏、`@` 前缀不被换、只有群聊与外客私聊才换、私下的话不动、空名字原样返回。2 条守 #57:40 条判断时 `visible < beliefs` 且 `score > 0.02`;界面同时印均值与最大值、提示语带剂量。1 条守 PR #56:成长层那句的标题里不许再出现「还不会」。
|
|
84
|
+
|
|
85
|
+
## 致谢
|
|
86
|
+
|
|
87
|
+
PR #55(@BBleae/晴猫):`VERSION` 跟上、三条 tick 用例在 0 点不再红。`twoTicks()` 里 `hh(-1)` 会算出 `-1:00`,`validWindow` 不认,`normalizeConfig` 悄悄退回 `20:00–23:00`——这个判断是对的,原样收下。
|
|
88
|
+
|
|
89
|
+
PR #56(@BBleae/晴猫):观测台上「撒娇式表达」解锁后同一行同时说「还不会」和「已经可以了」。改法原样收下:标题只留能力名,结局交给状态补;顺带把申请正文里那句「我想试试「撒娇式表达:还不会」这件事」修顺了。
|
|
90
|
+
|
|
91
|
+
能力地图按处境组织、以及把用过的工具记成她自己的经验(`use:` 账与「我做过的事」那一节),这两个想法来自 **1 群群友 joydog**。落进本插件时做了取舍:账只留她当时听到的那一句、不记参数与整段返回,被门挡住的那次不入账,账走 `meta`、`day:0` 不进阶段计数;能力地图只加处境层,原来的名字清单一条不删。
|
package/RELEASE.md
CHANGED
|
@@ -25,7 +25,7 @@ gh auth status # 必须是 ssamurias
|
|
|
25
25
|
## 1. 发布前自检(每次都要跑,别跳)
|
|
26
26
|
|
|
27
27
|
- [ ] `package.json` 的 `version` 就是要发的号(修 bug 发 patch,如 `0.9.3`;一批改动发 minor,如 `0.10.0`)
|
|
28
|
-
- [ ] `node --test` 全绿(当前基线见 `AGENTS.md` 第 6
|
|
28
|
+
- [ ] `node --test` 全绿(当前基线见 `AGENTS.md` 第 6 节;现在是 **1674 pass / 0 fail**,约 24 秒)
|
|
29
29
|
- [ ] `CHANGELOG.md` 顶部有本节,条目按 issue 编号写全
|
|
30
30
|
- [ ] `RELEASE-NOTES-x.y.z.md` 已写,且与 `CHANGELOG.md` 口径一致(issue 条数、PR 编号、日期)
|
|
31
31
|
- [ ] `README.md` 顶部「当前版本」段已换成这一版
|
package/lib/anchor.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* anchor.js —— 把一件事挂回她当时那句话(2026-11-07,用户 m00493 的 ⑤)。
|
|
3
|
+
*
|
|
4
|
+
* 「用过 her_note,记下了:…」这句话本身没有来路。翻旧事时她看不出那次是为了
|
|
5
|
+
* 什么——是回一句话,还是记一件刚发生的事。而事件流里那笔账旁边的她那句话
|
|
6
|
+
* 就是答案:同一个 sessionId,就在调用之前几分钟。
|
|
7
|
+
*
|
|
8
|
+
* 只读事件流,不写、不改。找不到就返回空串,绝不编一句。
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { HER_TYPES } from './schema.js'
|
|
12
|
+
|
|
13
|
+
/** 往前找多久:一轮里她一句话说完就动手,超过十分钟多半是别的事了。 */
|
|
14
|
+
export const ANCHOR_WINDOW_MS = 10 * 60 * 1000
|
|
15
|
+
/** 锚点只用来认路,不是复述:一句问话的开头足够。 */
|
|
16
|
+
export const ANCHOR_TEXT_MAX = 40
|
|
17
|
+
|
|
18
|
+
const clip = (text, n) => (text.length > n ? `${text.slice(0, n - 1).replace(/[\uD800-\uDBFF]$/, '')}…` : text)
|
|
19
|
+
|
|
20
|
+
function tsOf(ev) {
|
|
21
|
+
const n = Date.parse(String(ev?.ts ?? ''))
|
|
22
|
+
return Number.isFinite(n) ? n : 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 她这次动手之前,最近说过的、属于她自己的一句话。
|
|
27
|
+
*
|
|
28
|
+
* @param {object[]} events 全量事件
|
|
29
|
+
* @param {string} sessionId 这次调用的会话;没有就不锚(宁可没有来路,也别挂错人)
|
|
30
|
+
* @param {number|string|Date} before 调用发生的时刻
|
|
31
|
+
* @param {{windowMs?:number, max?:number}} [o]
|
|
32
|
+
* @returns {string} 已截断的一句话;找不到是空串
|
|
33
|
+
*/
|
|
34
|
+
export function anchorOf(events, sessionId, before, o = {}) {
|
|
35
|
+
const sid = String(sessionId ?? '').trim()
|
|
36
|
+
if (!sid) return ''
|
|
37
|
+
const at = before instanceof Date
|
|
38
|
+
? before.getTime()
|
|
39
|
+
: typeof before === 'number'
|
|
40
|
+
? before
|
|
41
|
+
: Date.parse(String(before ?? ''))
|
|
42
|
+
if (!Number.isFinite(at)) return ''
|
|
43
|
+
const windowMs = Math.max(0, Number(o.windowMs ?? ANCHOR_WINDOW_MS))
|
|
44
|
+
const max = Math.max(1, Number(o.max ?? ANCHOR_TEXT_MAX))
|
|
45
|
+
const types = new Set(HER_TYPES)
|
|
46
|
+
let best = null
|
|
47
|
+
for (const ev of Array.isArray(events) ? events : []) {
|
|
48
|
+
if (!ev || String(ev.sessionId ?? '') !== sid) continue
|
|
49
|
+
if (!types.has(String(ev.type))) continue
|
|
50
|
+
const ts = tsOf(ev)
|
|
51
|
+
if (!ts || ts > at || at - ts > windowMs) continue
|
|
52
|
+
const text = String(ev.text ?? '').replace(/\s+/g, ' ').trim()
|
|
53
|
+
if (!text) continue
|
|
54
|
+
if (!best || ts > best.ts) best = { ts, text }
|
|
55
|
+
}
|
|
56
|
+
return best ? clip(best.text, max) : ''
|
|
57
|
+
}
|
package/lib/browse.js
CHANGED
|
@@ -546,6 +546,14 @@ export const LOOK_BRIEF = [
|
|
|
546
546
|
'和 growth_commit 一样,你不用它也可以。',
|
|
547
547
|
].join('\n')
|
|
548
548
|
|
|
549
|
+
/**
|
|
550
|
+
* 关着时的「上网」那一节(P3,2026-11-07)。
|
|
551
|
+
* 门不拆:整节消失会让她以为这件事根本不存在;只把门上的话换成「这会儿」。
|
|
552
|
+
*/
|
|
553
|
+
export const LOOK_OFF_BRIEF = [
|
|
554
|
+
'这会儿出不去。以前翻过、看过的东西还在;等能出去的时候,你会先看到它们。',
|
|
555
|
+
].join('\n')
|
|
556
|
+
|
|
549
557
|
/**
|
|
550
558
|
* 他丢来一条视频的时候怎么办。
|
|
551
559
|
*
|
package/lib/client.js
CHANGED
|
@@ -253,7 +253,11 @@ window.__ModuleLoader__.load({
|
|
|
253
253
|
if (!Number(v.sample ?? 0) || !Number(v.beliefs ?? 0)) return band || '还没攒到判断,测不出来'
|
|
254
254
|
const n = Number(v.score)
|
|
255
255
|
const num = Number.isFinite(n) ? n.toFixed(2) : '—'
|
|
256
|
-
|
|
256
|
+
// 2026-11-07(issue #57):只印均值,会把「平均动 3%、某一次动了 15%」写成一片安安静静。
|
|
257
|
+
// 最大值明显高于均值时一并印出来——这两个数合起来才是这一行的全部事实。
|
|
258
|
+
const w = Number(v.worst)
|
|
259
|
+
const worst = Number.isFinite(w) && Number.isFinite(n) && w > n + 0.005 ? `(最大 ${w.toFixed(2)})` : ''
|
|
260
|
+
return band ? `${num}${worst} · ${band}` : `${num}${worst}`
|
|
257
261
|
}
|
|
258
262
|
function qConsistency(v) {
|
|
259
263
|
if (!v) return '—'
|
|
@@ -1975,8 +1979,20 @@ window.__ModuleLoader__.load({
|
|
|
1975
1979
|
+ '一致高到 1.00 也可能是她一直没长新东西;'
|
|
1976
1980
|
+ '新鲜看她有没有一直在长新东西。'
|
|
1977
1981
|
+ '样本不够时这里会写一句话,而不是硬给一个 0.00。'
|
|
1978
|
-
const STABILITY_HELP = '
|
|
1982
|
+
const STABILITY_HELP = '少几条她这会儿读得到的判断,她还是不是她。'
|
|
1983
|
+
+ '太低 = 那些判断只是记账、抽掉也不影响她;'
|
|
1979
1984
|
+ '太高 = 她整个人都压在少数几条上,少一条就是另一个人。'
|
|
1985
|
+
// 判读要带剂量(issue #57):同一个数在五六条判断和五六十条判断的账号上含义差很远。
|
|
1986
|
+
// 只写「抽几条」不够——真正的关键是抽的那几条有没有写进她这会儿的自我描述。
|
|
1987
|
+
const stabilityTip = (v) => {
|
|
1988
|
+
const k = Number(v && v.k)
|
|
1989
|
+
const all = Number(v && v.beliefs)
|
|
1990
|
+
const vis = Number(v && v.visible)
|
|
1991
|
+
if (!k || !all) return STABILITY_HELP
|
|
1992
|
+
const where = vis ? `其中 ${vis} 条这会儿真写在自我描述里` : '这会儿一条都没写进自我描述,抽的是全部判断'
|
|
1993
|
+
return `${STABILITY_HELP}这次每次抽掉 ${k} 条,`
|
|
1994
|
+
+ `共 ${all} 条判断(${where}),跑了 ${Number((v && v.sample) || 0)} 次。`
|
|
1995
|
+
}
|
|
1980
1996
|
const CONSISTENCY_HELP = '她前后说法打不打架。跨阶段的话越多这个数越可信;'
|
|
1981
1997
|
+ '一句都没得比的时候会写「还没有跨阶段的话可比」。'
|
|
1982
1998
|
const NOVELTY_HELP = '有没有一直在长新东西:最近七天说的话里,有多少句是以前没出现过的。'
|
|
@@ -2113,7 +2129,7 @@ window.__ModuleLoader__.load({
|
|
|
2113
2129
|
h('span', { style: { opacity: 0.65, flex: '0 0 auto', minWidth: '84px' } }, '判断漂移'),
|
|
2114
2130
|
h('span', { style: { flex: '1', opacity: 0.8 } }, QUALITY_TITLE,
|
|
2115
2131
|
h('span', { title: QUALITY_HELP, style: { opacity: 0.45, marginLeft: '5px', cursor: 'help' } }, 'ⓘ'))),
|
|
2116
|
-
rowTip('稳定', qStability(s.quality.stability),
|
|
2132
|
+
rowTip('稳定', qStability(s.quality.stability), stabilityTip(s.quality.stability)),
|
|
2117
2133
|
rowTip('一致', qConsistency(s.quality.consistency), CONSISTENCY_HELP),
|
|
2118
2134
|
rowTip('新鲜', qNovelty(s.quality.novelty), NOVELTY_HELP)) : null) : null,
|
|
2119
2135
|
|
|
@@ -2146,7 +2162,8 @@ window.__ModuleLoader__.load({
|
|
|
2146
2162
|
h('div', { style: { opacity: 0.75, fontSize: '12px', marginTop: '2px' } }, L.note),
|
|
2147
2163
|
...L.rules.map((r) => {
|
|
2148
2164
|
const st2 = (rules.status || []).find((x) => x.id === r.id)
|
|
2149
|
-
|
|
2165
|
+
// 状态只放这里,标题里不写「还不会」:解锁后两句会连在一起。不可解锁的规则没有 st2,不显示状态。
|
|
2166
|
+
const tail = !st2 ? '' : st2.active ? ' · 已经可以了' : (st2.ready ? ' · 条件够了,等她开口' : ' · 还不会')
|
|
2150
2167
|
return h('div', { style: { fontSize: '12px', marginTop: '4px' } },
|
|
2151
2168
|
`· ${r.title}${r.inPrompt ? '' : '(不进提示词)'}${tail}`)
|
|
2152
2169
|
}),
|
|
@@ -2582,7 +2599,9 @@ window.__ModuleLoader__.load({
|
|
|
2582
2599
|
hint: '其余时间只看不回。' })),
|
|
2583
2600
|
cm ? h('p', { className: 'hint', style: { marginTop: '8px' } },
|
|
2584
2601
|
`今天:发了 ${cm.usedToday}/${cm.perDay} 条,回了 ${cm.replyUsedToday}/${cm.replyPerDay} 条。`,
|
|
2585
|
-
cm.waitingCount
|
|
2602
|
+
cm.waitingCount
|
|
2603
|
+
? ` 有 ${cm.waitingCount} 条没回${cm.waitingOwnerCount ? `,其中 ${cm.waitingOwnerCount} 条是你回的` : ''}。`
|
|
2604
|
+
: ' 没人回。') : null),
|
|
2586
2605
|
group('领养人验证',
|
|
2587
2606
|
'这是要 @ 的账号,一般填你自己的大号。它和下面登录用的 cookie 是两个账号,互不影响——存 cookie、点测试都不会改这两格。',
|
|
2588
2607
|
// 旧版本(≤0.5.9)会把登录账号写进这两格,只有点「保存」才会重写;这里点出来,别让人以为是刷新丢的。
|