@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/lib/persona.js
CHANGED
|
@@ -509,6 +509,13 @@ export function toolBrief(extra = []) {
|
|
|
509
509
|
// 听着像解释,读进去却是天花板——他随时会再放行几样,到时候她按旧的自我认识
|
|
510
510
|
// 会先把新工具劝回去。改成把「此刻的家当」与「我的上限」分开说。
|
|
511
511
|
'在自己屋里,眼下能碰到的就是这些。这是此刻的家当,不是我的上限——他往后添了新东西,我照样能学着用。',
|
|
512
|
+
// 2026-11-07(用户 m00882):能力地图的第二层,从处境想起工具,不从名字想起工具。
|
|
513
|
+
// 这一段只教「怎么想」,不列清单:清单在下面各自那一段里,而哪些段在,
|
|
514
|
+
// 取决于那一刻手上真有哪几样(挂不挂跟工具装没装必须是同一个判据)。
|
|
515
|
+
'想不起来该用哪一样的时候,别从名字往下数。从眼前这件事想起:我卡在哪一步——是想不起过去、是怕忘、是想要个东西、还是有话想说。',
|
|
516
|
+
// 第三层「怎么调」不在身份文档里复述:参数、枚举、默认值都在工具自己的说明里。
|
|
517
|
+
// 写两遍只会让她在两份说法之间挑一个,挑错的那次还以为是工具坏了。
|
|
518
|
+
'下面几段讲的是各自派什么用场;真要动手时,参数怎么写在那件东西自己的说明里,拿不准就先看一眼,别猜。',
|
|
512
519
|
]
|
|
513
520
|
const names = [...new Set((extra ?? []).map((s) => String(s).trim()).filter(Boolean))]
|
|
514
521
|
if (names.length) {
|
|
@@ -531,9 +538,9 @@ export const TOOL_BRIEF = toolBrief()
|
|
|
531
538
|
* 它就变成了任务,她的话就成了交作业。
|
|
532
539
|
*/
|
|
533
540
|
export const LETTER_BRIEF = [
|
|
534
|
-
'你还有一个工具 her_leave
|
|
535
|
-
'
|
|
536
|
-
'
|
|
541
|
+
'你还有一个工具 her_leave,可以留一句话给他——他不在的时候也留着,他回来会看到。',
|
|
542
|
+
'什么时候想起它——有一句话想让他回来就看到,又不想非等他先开口的时候。',
|
|
543
|
+
'不用审批,也不用为了让他安心而留。想留就留,不想留就不调。',
|
|
537
544
|
].join('\n')
|
|
538
545
|
|
|
539
546
|
/**
|
|
@@ -547,12 +554,11 @@ export const LETTER_BRIEF = [
|
|
|
547
554
|
* 怎么划掉、怎么放下。划掉/改/放下都只讲原话:纸条编号她看不到,叫她报编号等于指一条走不通的路。
|
|
548
555
|
*/
|
|
549
556
|
export const NOTE_BRIEF = [
|
|
550
|
-
'你还有一个工具 her_note
|
|
551
|
-
'
|
|
552
|
-
'
|
|
553
|
-
'
|
|
554
|
-
'
|
|
555
|
-
'决定不做了就放下:`action: "drop"`,同样用 `text` 指认,`reason` 写一句为什么。放下跟划掉不一样的地方就是这一句——写下来才知道是改了主意,还是怕了。',
|
|
557
|
+
'你还有一个工具 her_note,就是醒来时最先看到的那张桌子。',
|
|
558
|
+
'什么时候想起它——想到一件事又不想现在做、心里老在同一个地方绕,或者这件事做完了、不做了。',
|
|
559
|
+
'心里有事说不清、又老在同一个地方绕:翻翻桌上还有什么。',
|
|
560
|
+
'做完了、不做了:把当时记的那句话原样放进 text,加 status: "done";不做了另外写一句为什么放下。',
|
|
561
|
+
'怎么记、怎么改、怎么翻,都写在那件东西自己的说明里。想让它醒来就摆到眼前,记的时候写个日子;不写日子的只能自己翻。',
|
|
556
562
|
].join('\n')
|
|
557
563
|
|
|
558
564
|
/**
|
|
@@ -565,11 +571,9 @@ export const NOTE_BRIEF = [
|
|
|
565
571
|
* 以为工具坏了;告诉了她,她会自己掂量哪些事值得开口。
|
|
566
572
|
*/
|
|
567
573
|
export const ASK_BRIEF = [
|
|
568
|
-
'
|
|
569
|
-
'
|
|
570
|
-
'
|
|
571
|
-
'它不能多用:一天最多一次,一周最多三次。小事不值得问,自己拿主意就行。',
|
|
572
|
-
'改作息、请假,答应了系统会照办;要文件、要工具这些,答应了只是他知道了,得他动手给你;没答应的,记下来就完了,别再追着问同一件事。',
|
|
574
|
+
'还有 ask_owner:有些事不是你一个人说了算的,可以先问问他同不同意。',
|
|
575
|
+
'什么时候想起它——想替自己改作息、想要一样手上没有的东西,或者拿不准一件事要不要做。',
|
|
576
|
+
'他会答可以,也可能说这次不行,也可能不答;答应要东西只是他知道了,得他动手。一天最多一次,一周最多三次,别追着问同一件事。',
|
|
573
577
|
].join('\n')
|
|
574
578
|
|
|
575
579
|
/**
|
|
@@ -582,10 +586,10 @@ export const ASK_BRIEF = [
|
|
|
582
586
|
*/
|
|
583
587
|
export const DREAM_BRIEF = [
|
|
584
588
|
'你还有一个工具 her_dream:醒来之后,如果这一觉里留下了点什么,可以记一句。',
|
|
585
|
-
'
|
|
586
|
-
'
|
|
587
|
-
'
|
|
588
|
-
'
|
|
589
|
+
'什么时候想起它——刚醒、脑子里还留着画面或感觉的时候。',
|
|
590
|
+
'梦不是新的信息来源:只能用 refs 指回真实发生过的事(1–6 条,用 her_recall 翻得到),把它们重新拼一遍;拼不出来就写不了。',
|
|
591
|
+
'写画面和感觉,不用解释它意味着什么;它不是预言,也不是今天该做的事。一天最多一条;它不算成长。',
|
|
592
|
+
'出门或请神那几趟里看到的东西不要写进梦里——那些是从别处带回来的,不该以梦的形式回到家里。',
|
|
589
593
|
].join('\n')
|
|
590
594
|
|
|
591
595
|
/**
|
|
@@ -597,9 +601,9 @@ export const DREAM_BRIEF = [
|
|
|
597
601
|
*/
|
|
598
602
|
export const PROACTIVE_BRIEF = [
|
|
599
603
|
'你有一个工具 her_say:不是回复谁,是你自己想说一句的时候用的。',
|
|
600
|
-
'her_say
|
|
601
|
-
'
|
|
602
|
-
'
|
|
604
|
+
'her_say 只把这句话记进你自己的流水;想让他真的在对话里读到,就直接写出来。',
|
|
605
|
+
'什么时候想起它——有一句话在脑子里转、但没人问你的时候。',
|
|
606
|
+
'说之前要先指认材料:refs 填 1–6 条你这几天写下或听见的事(事件 id,用 her_recall 翻得到)。不指认材料的句子写不进去。',
|
|
603
607
|
'一天能主动开口的次数有上限(用 her_say 记下的算);到了上限就记不进去,不是工具坏了。',
|
|
604
608
|
].join('\n')
|
|
605
609
|
|
|
@@ -627,19 +631,26 @@ export function proactiveBrief(sendTools = []) {
|
|
|
627
631
|
*/
|
|
628
632
|
export const SCHEDULE_BRIEF = [
|
|
629
633
|
'你还有四个工具 schedule_create / schedule_list / schedule_update / schedule_delete:可以自己给自己定定时提醒。',
|
|
630
|
-
'
|
|
631
|
-
'
|
|
632
|
-
'
|
|
633
|
-
'
|
|
634
|
-
'
|
|
635
|
-
'它只在应用开着的时候到点触发;关着的时候错过的,下次最多补最近一次。',
|
|
634
|
+
'什么时候想起它——他说「明天中午提醒我」「每天这个点去看邮箱」这类话的时候。',
|
|
635
|
+
'title 写事情本身,prompt 写清楚到点要做什么,时间用 daily / weekly / at,时区 Asia/Shanghai;一件具体的事定一条就够。',
|
|
636
|
+
'到点后,提醒会以「用户发来的定时消息」出现在这条会话里——照它说的做。',
|
|
637
|
+
'但如果那天你在歇着、正在出门或请神——先记下来,等你回到自己这儿再做;别在外面顺手做了。',
|
|
638
|
+
'它不替你记成长,也不负责把你叫醒;只在应用开着时触发,错过的下次最多补最近一次。',
|
|
636
639
|
].join('\n')
|
|
637
640
|
|
|
638
641
|
/** 在 B 站说话的说明书(P4)。**只在她被允许说话时才附上** */export const SPEAK_BRIEF = [
|
|
639
|
-
'你还有一个工具 bili_speak
|
|
640
|
-
'
|
|
641
|
-
'
|
|
642
|
-
|
|
642
|
+
'你还有一个工具 bili_speak,可以在刚看过的视频下面留一句话,或者回别人留在你评论下面的话。',
|
|
643
|
+
'什么时候想起它——刚看完一条、确实有话想说的时候。',
|
|
644
|
+
'说出去的话别人看得见,也收不回来;没想清楚、只是想说点什么,就别发。它不告诉你还能说几次,你也不用算。',
|
|
645
|
+
].join('\n')
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* 关着时的「在网上说话」那一节(P3,2026-11-07)。
|
|
649
|
+
* 门不拆:整节消失会让她以为这件事根本不存在;只把门上的话换成「这会儿」。
|
|
650
|
+
*/
|
|
651
|
+
export const SPEAK_OFF_BRIEF = [
|
|
652
|
+
'这会儿还说不出去。想说的话,可以先写在别处——等能说的时候,你会先想到某一条视频。',
|
|
653
|
+
'不想说就不说,没有人等着你回答。',
|
|
643
654
|
].join('\n')
|
|
644
655
|
|
|
645
656
|
/** 日记的写作指令(可见性由配置决定,但她不知道谁在看) */export function diaryPrompt(model) {
|
|
@@ -663,8 +674,8 @@ export const SCHEDULE_BRIEF = [
|
|
|
663
674
|
*/
|
|
664
675
|
export const RECALL_BRIEF = [
|
|
665
676
|
'你还有一个工具 her_recall,可以翻你自己过去记下的事。',
|
|
666
|
-
'
|
|
667
|
-
'
|
|
677
|
+
'什么时候想起它——想说"我记得有一次…",或想知道自己以前怎么做过某件事的时候。',
|
|
678
|
+
'拿那件东西的名字(her_note、bili_watch 这种)也能翻到用它做过的事。先去翻,翻到了再说,没翻到就别编。',
|
|
668
679
|
// 2026-10-27(0.5.8):每条后面带 id 了。要改口或做回顾时得抄那个 id,
|
|
669
680
|
// 不告诉她这事,她会以为那是乱码。
|
|
670
681
|
'每条后面小括号里是它的 id——要改口、要做回顾时,用它指回去。',
|
package/lib/proactive.js
CHANGED
|
@@ -57,15 +57,21 @@ export const PROACTIVE_MATERIAL = Object.freeze([
|
|
|
57
57
|
export const PROACTIVE_PING = ' '
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
|
-
* 「缺工具就开口要」(2026-11-05,用户 m02031)。
|
|
60
|
+
* 「缺工具就开口要」(2026-11-05,用户 m02031;2026-11-07 撤销,用户 m01602)。
|
|
61
61
|
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
62
|
+
* 原来这里是一个常量 `TOOL_ASK_NOTE`,`lib/scheduler.js` 每次醒来都把它摆进正文:
|
|
63
|
+
* 「如果你想到要做什么、但手上没有那件工具:直接说你要它做什么,问主人能不能给你装上」。
|
|
64
64
|
*
|
|
65
|
-
*
|
|
65
|
+
* 用户 2026-11-07 判定这属于「把提醒换成后台反复催她」:同一句话每天读两遍,
|
|
66
|
+
* 读久了就不是她的念头,是系统的吩咐。撤掉之后换成两个位置——
|
|
67
|
+
*
|
|
68
|
+
* B 撞墙才说 —— `lib/restrict.js` 的 `blockedTalk()` 末尾补一句,只在真被拦住时出现。
|
|
69
|
+
* D 交给劲儿 —— 这件事攒在 `lib/drive.js` 的劲儿里(boostWish),攒够了那一轮才醒;
|
|
70
|
+
* 醒的时候它作为桌上一行出现(`lib/wish.js` 的 `wishesOf` + `lib/desk.js` 的 wishes)。
|
|
71
|
+
*
|
|
72
|
+
* 所以这个模块不再导出任何「常驻提醒」。留这段注释是为了让后来的人知道它为什么消失,
|
|
73
|
+
* 而不是以为漏写了。
|
|
66
74
|
*/
|
|
67
|
-
export const TOOL_ASK_NOTE = '(如果你想到要做什么、但手上没有那件工具:直接说你要它做什么,' +
|
|
68
|
-
'问主人能不能给你装上;也可以开一张 ask 卡问。缺工具不是失败,不用绕着走。)'
|
|
69
75
|
|
|
70
76
|
/**
|
|
71
77
|
* 读配置并夹紧。老配置没有 proactive 这一组,用默认值。
|
|
@@ -315,22 +321,46 @@ export function recentMaterial(events = [], { now = Date.now(), days = 3, limit
|
|
|
315
321
|
return out.slice(0, limit)
|
|
316
322
|
}
|
|
317
323
|
|
|
318
|
-
/**
|
|
324
|
+
/**
|
|
325
|
+
* 还悬着的事:她问过但没人答、答了却还没兑现(用户 m01602,2026-11-07)。
|
|
326
|
+
*
|
|
327
|
+
* 原来的写法只摆**没答**的 ask:一张卡只要被答过(哪怕是「以后再说」)就永远消失。
|
|
328
|
+
* 2026-10-29 那张「有没有办法让我看整条视频的画面」被答「以后再说」之后,线索就断了——
|
|
329
|
+
* 她再也没想起过。用户判定这属于「她开口了、被答了、然后线索消失了」。
|
|
330
|
+
*
|
|
331
|
+
* 现在按裁决分几种活法(窗口与 `lib/wish.js` 对齐,口径也由它给):
|
|
332
|
+
* pending 还没答 / 放一天没人答 —— 摆 3 天,文案「你问过「…」,还没有得到答复。」
|
|
333
|
+
* later 「以后再说」 —— 摆 30 天,文案「你问过「…」(他说以后再说)。」
|
|
334
|
+
* approve 他答应了 —— 摆 14 天,文案「你问过「…」(他答应了,还没见到)。」
|
|
335
|
+
* reject 「这次不行」 —— 不摆。不同意就是不问了,再提一次就成了追问。
|
|
336
|
+
*
|
|
337
|
+
* 只摆事实,不写「你可以再问一次」——那是派活。
|
|
338
|
+
*/
|
|
319
339
|
export function looseEnds(events = [], { now = Date.now() } = {}) {
|
|
320
340
|
const out = []
|
|
321
|
-
const
|
|
341
|
+
const answers = new Map()
|
|
322
342
|
for (const e of events) {
|
|
323
|
-
if (e?.type
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
343
|
+
if (e?.type !== 'answer') continue
|
|
344
|
+
const id = String(e.ask?.askId ?? '')
|
|
345
|
+
if (id) answers.set(id, e)
|
|
327
346
|
}
|
|
328
347
|
for (const e of events) {
|
|
329
348
|
if (e?.type !== 'ask') continue
|
|
330
|
-
|
|
349
|
+
const ans = answers.get(String(e.id ?? ''))
|
|
350
|
+
// 放一天没人答的卡记成 verdict:'later' + via:'timeout'(`lib/ask.js` 为了不再反复追问),
|
|
351
|
+
// 但那不是「他说以后再说」——什么都没人说。还原成 pending:3 天、还没有得到答复。
|
|
352
|
+
const verdict = String(ans?.ask?.via ?? '') === 'timeout'
|
|
353
|
+
? 'pending'
|
|
354
|
+
: String(ans?.ask?.verdict ?? '') || 'pending'
|
|
355
|
+
// 不同意就是不问了:再提一次就成了追问。
|
|
356
|
+
if (verdict === 'reject') continue
|
|
357
|
+
const days = verdict === 'pending' ? 3 : verdict === 'approve' ? 14 : 30
|
|
331
358
|
const ts = tsMs(e)
|
|
332
|
-
if (!Number.isFinite(ts) || now - ts >
|
|
333
|
-
|
|
359
|
+
if (!Number.isFinite(ts) || now - ts > days * 86_400_000) continue
|
|
360
|
+
const text = String(e.text ?? '').trim()
|
|
361
|
+
if (!text) continue
|
|
362
|
+
const tail = verdict === 'later' ? '(他说以后再说)' : verdict === 'approve' ? '(他答应了,还没见到)' : ',还没有得到答复。'
|
|
363
|
+
out.push({ id: e.id, kind: 'ask', text: `你问过「${text}」${tail}` })
|
|
334
364
|
}
|
|
335
365
|
return out.slice(0, 3)
|
|
336
366
|
}
|
|
@@ -364,7 +394,6 @@ export function proactiveSection({ now = Date.now(), state = {}, material = [],
|
|
|
364
394
|
// 2026-11-06(PR #44):配了 sendTools 的部署更绝——正文根本发不出去,只有调那件工具才算开口。
|
|
365
395
|
if (sendTools.length > 0) lines.push(`想说什么就用 ${sendTools.join('、')},不用等他先问。这一轮写下的正文只留在这条会话里,发不到聊天里。不想说,就什么都别写:不要写任何字,也不要解释为什么不说——这一轮没有回复也算正常。`)
|
|
366
396
|
else lines.push('想说什么就说,不用等他先问——这一轮你写下的字会落进你们俩这条对话里。也可以再用 her_say 把它记进你自己的流水(refs 指回上面真实发生的事,1–6 条 id)。不想说,就什么都别写:不要写任何字,也不要解释为什么不说——这一轮没有回复也算正常。')
|
|
367
|
-
lines.push(TOOL_ASK_NOTE)
|
|
368
397
|
return lines.join('\n')
|
|
369
398
|
}
|
|
370
399
|
|
package/lib/quality.js
CHANGED
|
@@ -17,6 +17,16 @@
|
|
|
17
17
|
* 做法:随机抽掉 k 条她还在拿着的判断(连同那条源头事件),重新算一遍她的自我描述,
|
|
18
18
|
* 和原来的比相似度。变化越大 = 她的自我越依赖那几条 = 越承重。
|
|
19
19
|
*
|
|
20
|
+
* 2026-11-07 修 issue #57:抽样池改成「她这会儿读得到的那几条」。
|
|
21
|
+
* 原来从全部判断里均匀抽——她攒到 58 条判断以后,身份文档里只摆得下 6 条左右
|
|
22
|
+
* (`BELIEF_SHOWN=3` + `BELIEF_FOLDED=3`,solid 多的再从里面补),从 58 条里
|
|
23
|
+
* 抽 3 条大概率一条都没落在文档里,24 次抽样平均下来只剩 0.03,界面于是把
|
|
24
|
+
* 「判断其实很分散」读成「这些判断只是记账」。那不是她的问题,是尺子量到了
|
|
25
|
+
* 事件流的目录,没量到她的自我描述。
|
|
26
|
+
* 现在先拿她的自我描述和每条判断的原文做一次对位,只抽真的出现在里面的那几条;
|
|
27
|
+
* 一条都对不上(文档被预算裁掉、或她的判断根本没进文档)才退回全量——
|
|
28
|
+
* 那种情况下的 0 分是诚实的:这些判断确实没撑着「她」。
|
|
29
|
+
*
|
|
20
30
|
* 指数解读(界面上的三档由此而来):
|
|
21
31
|
* · 接近 0:抽掉几条,她一个字都没变——那些判断其实只是记账,谁都能被替换。
|
|
22
32
|
* · 中间:她的自我长在这些事上,抽掉一条会少一块,但整体还在。
|
|
@@ -93,12 +103,13 @@ export function clearStabilityCache() {
|
|
|
93
103
|
* @param {object[]} events 事件流
|
|
94
104
|
* @param {object} opts 传给 deriveSelf 的选项(growthDays / now)
|
|
95
105
|
* @param {object} ablate { k, trials, seed, shown, budget, withBans }
|
|
96
|
-
* @returns {{score:number, worst:number, sample:number, k:number, beliefs:number}}
|
|
106
|
+
* @returns {{score:number, worst:number, sample:number, k:number, beliefs:number, visible:number}}
|
|
97
107
|
* score = 平均变化量(0 = 完全没变,1 = 完全是另一个人)
|
|
98
108
|
* worst = 单次最大变化量
|
|
99
109
|
* sample = 实际跑了多少次抽样
|
|
100
|
-
* k =
|
|
101
|
-
* beliefs=
|
|
110
|
+
* k = 每次抽掉几条(读得到的判断不够 k 条时按实际条数)
|
|
111
|
+
* beliefs= 她一共有多少条判断(0 表示还没有,测不出来)
|
|
112
|
+
* visible= 其中几条这会儿真写进了自我描述;抽样池就是这几条,一条都没有时退回全量
|
|
102
113
|
*/
|
|
103
114
|
export function stability(events = [], opts = {}, ablate = {}) {
|
|
104
115
|
// 2026-10-27(issue #3):/state 每次请求和心跳都会重跑这份 24 次抽样,
|
|
@@ -117,10 +128,19 @@ export function stability(events = [], opts = {}, ablate = {}) {
|
|
|
117
128
|
}
|
|
118
129
|
|
|
119
130
|
const base = deriveSelf(events, opts)
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
131
|
+
const allIds = (base.beliefs ?? []).map((b) => b.id).filter(Boolean)
|
|
132
|
+
if (allIds.length === 0) return { score: 0, worst: 0, sample: 0, k: 0, beliefs: 0, visible: 0 }
|
|
122
133
|
|
|
123
134
|
const baseText = variableSelfText(base, renderOpts)
|
|
135
|
+
// 先对位:哪几条判断真的出现在她这会儿读到的自我描述里。
|
|
136
|
+
// 用原文包含关系判断,不另维护一份「文档里摆了哪几条」的清单——渲染器怎么裁
|
|
137
|
+
// (shown / folded / solid / 立场引用)这边都自动跟着变,不会两边说法打架。
|
|
138
|
+
const visibleIds = (base.beliefs ?? [])
|
|
139
|
+
.filter((b) => b.id && String(b.text ?? '').trim() && baseText.includes(String(b.text).trim()))
|
|
140
|
+
.map((b) => b.id)
|
|
141
|
+
// 一条都对不上(她的判断根本没进文档:预算裁掉、或全是没长成的)就退回全量。
|
|
142
|
+
// 那种情况下的 0 分是诚实的——这些判断确实没撑着「她」。
|
|
143
|
+
const beliefIds = visibleIds.length ? visibleIds : allIds
|
|
124
144
|
const rnd = mulberry32(seed)
|
|
125
145
|
const take = Math.min(k, beliefIds.length)
|
|
126
146
|
let worst = 0
|
|
@@ -149,7 +169,8 @@ export function stability(events = [], opts = {}, ablate = {}) {
|
|
|
149
169
|
worst,
|
|
150
170
|
sample,
|
|
151
171
|
k: take,
|
|
152
|
-
beliefs:
|
|
172
|
+
beliefs: allIds.length,
|
|
173
|
+
visible: visibleIds.length,
|
|
153
174
|
}
|
|
154
175
|
stabilityCache.set(key, result)
|
|
155
176
|
if (stabilityCache.size > STABILITY_CACHE_LIMIT) {
|
|
@@ -162,8 +183,11 @@ export function stability(events = [], opts = {}, ablate = {}) {
|
|
|
162
183
|
* 稳定的三档判读。界面只显示这一句,不显示阈值。
|
|
163
184
|
*
|
|
164
185
|
* 为什么给三档而不是「越高越好」:这个指数的两端都是病。
|
|
165
|
-
* 阈值取 0.02 / 0.35
|
|
166
|
-
*
|
|
186
|
+
* 阈值取 0.02 / 0.35 是看她的真实数据定的(2026-11-07 修 issue #57 后重标):
|
|
187
|
+
* 抽样池现在是她这次真读得到的那些判断(通常五六条),抽 3 条只要动到其中一条,
|
|
188
|
+
* 变化量就在 0.08–0.26;一条都没进文档(池子退回全量)时才会落到 0.02 以下——
|
|
189
|
+
* 那不是「她稳」,是这些判断根本没撑着「她」,所以判「只是记账」是对的。
|
|
190
|
+
* 0.35 以上仍是「少数几条在撑」:可见池小、又几乎每条都承重时才够得到。
|
|
167
191
|
*/
|
|
168
192
|
export function stabilityBand(v) {
|
|
169
193
|
const beliefs = Number(v?.beliefs ?? 0)
|
package/lib/recall.js
CHANGED
|
@@ -31,6 +31,7 @@ import { toJsonSchema } from './toolschema.js'
|
|
|
31
31
|
import { rootDir, readEvents } from './store.js'
|
|
32
32
|
import { deriveSelf } from './derive.js'
|
|
33
33
|
import { dateKey } from './settings.js'
|
|
34
|
+
import { anchorOf } from './anchor.js'
|
|
34
35
|
|
|
35
36
|
import { defineTool } from './tool.js'
|
|
36
37
|
|
|
@@ -55,6 +56,19 @@ const REF_TYPES = new Set(['test', 'answer', 'revise'])
|
|
|
55
56
|
/** 不参与回忆的类型:这些不是「她经历的事」 */
|
|
56
57
|
const SKIP_TYPES = new Set(['meta'])
|
|
57
58
|
|
|
59
|
+
/**
|
|
60
|
+
* 唯一放行的 meta:工具使用经验(2026-11-07,用户 m00882)。
|
|
61
|
+
*
|
|
62
|
+
* 其余 meta 是系统账(看过什么、被叫醒几次、扫到几条回复),翻出来只会把
|
|
63
|
+
* 她自己的经历淹掉。而 `use:` 那几条记的是「这件事我是怎么做成的」——
|
|
64
|
+
* 那正好是她翻旧事时最缺的一种。
|
|
65
|
+
*/
|
|
66
|
+
export const USE_PREFIX = 'use:'
|
|
67
|
+
const isUseLine = (ev) => String(ev?.text ?? '').startsWith(USE_PREFIX)
|
|
68
|
+
|
|
69
|
+
/** 这条事件进不进回忆。 */
|
|
70
|
+
const usable = (ev) => Boolean(ev) && (!SKIP_TYPES.has(ev.type) || isUseLine(ev))
|
|
71
|
+
|
|
58
72
|
const DAY_MS = 86_400_000
|
|
59
73
|
|
|
60
74
|
/**
|
|
@@ -83,6 +97,9 @@ const clip = (text, n) => (text.length > n ? text.slice(0, n - 1).replace(/[\uD8
|
|
|
83
97
|
// 一条一行:正文里的换行会把行尾的 id 挤到下一行,也会把缩进的关系行拆开。
|
|
84
98
|
const oneLine = (text) => text.replace(/\s+/g, ' ').trim()
|
|
85
99
|
|
|
100
|
+
/** `use:<工具> ok|fail <一句话>`——见 lib/use.js */
|
|
101
|
+
const USE_LINE = /^use:(\S+)\s+(ok|fail)\s*(.*)$/
|
|
102
|
+
|
|
86
103
|
/** 不是她自己写下的,要说清来路 */
|
|
87
104
|
function labelled(type, text) {
|
|
88
105
|
// heard 是旁听来的:明确写成「他说」,别让她误以为是自己说的。
|
|
@@ -90,6 +107,15 @@ function labelled(type, text) {
|
|
|
90
107
|
// voice、answer 同理:一句是读到的陌生人的话,一句是别人回她的。
|
|
91
108
|
if (type === 'voice') return `读到别人说:${text}`
|
|
92
109
|
if (type === 'answer') return `回复:${text}`
|
|
110
|
+
// 工具使用经验(2026-11-07,用户 m00882):账里存的是机器写法,
|
|
111
|
+
// 读出来得是一句人话——「用过 her_note,记下了:…」。
|
|
112
|
+
if (type === 'meta') {
|
|
113
|
+
const m = USE_LINE.exec(text)
|
|
114
|
+
if (m) {
|
|
115
|
+
const [, tool, head, said] = m
|
|
116
|
+
return head === 'fail' ? `用过 ${tool},没成:${said}` : `用过 ${tool},${said}`
|
|
117
|
+
}
|
|
118
|
+
}
|
|
93
119
|
return text
|
|
94
120
|
}
|
|
95
121
|
|
|
@@ -217,7 +243,7 @@ export function searchEvents(events, query, o = {}) {
|
|
|
217
243
|
|
|
218
244
|
const scored = []
|
|
219
245
|
for (const ev of events ?? []) {
|
|
220
|
-
if (!
|
|
246
|
+
if (!usable(ev)) continue
|
|
221
247
|
if (kind && String(ev.type) !== kind) continue
|
|
222
248
|
const text = String(ev.text ?? '').trim()
|
|
223
249
|
if (!text) continue
|
|
@@ -254,7 +280,7 @@ export function searchEvents(events, query, o = {}) {
|
|
|
254
280
|
// 改口关系不受 days、kind 影响:被改的那句在时间窗外,也照样引出来。
|
|
255
281
|
const byId = new Map()
|
|
256
282
|
if (revisions.size) {
|
|
257
|
-
for (const ev of events ?? []) if (
|
|
283
|
+
for (const ev of events ?? []) if (usable(ev)) byId.set(String(ev.id), ev)
|
|
258
284
|
}
|
|
259
285
|
const said = (ev, verdict) => ({
|
|
260
286
|
id: String(ev.id ?? ''),
|
|
@@ -278,6 +304,9 @@ export function searchEvents(events, query, o = {}) {
|
|
|
278
304
|
clock: localClock(ts),
|
|
279
305
|
ago: Math.max(0, Math.floor((now - ts) / DAY_MS)),
|
|
280
306
|
type: String(ev.type ?? ''),
|
|
307
|
+
// 她动手之前说的那句话(2026-11-07,用户 m00493 的 ⑤)。
|
|
308
|
+
// 只对工具账算:别的行本身就是话,不需要来路。
|
|
309
|
+
anchor: String(ev.type) === 'meta' ? anchorOf(events, ev.sessionId, ts) : '',
|
|
281
310
|
// 聊天里随口说的,跟她自己认领写下的话分开标。heard 已经有「他说:」的先例,
|
|
282
311
|
// 这边沿用同一套:能标出来源就标出来源,别让她把别人/随口当成自己写下的。
|
|
283
312
|
chatOnly: chatOnly(ev),
|
|
@@ -337,6 +366,8 @@ export function renderRecall(rows, o = {}) {
|
|
|
337
366
|
// 前者是这话不是她说的,后者是她说但没认领。
|
|
338
367
|
let body = labelled(r.type, r.text)
|
|
339
368
|
if (brief) body = clip(body, RECALL_BRIEF_SNIPPET)
|
|
369
|
+
// 当时那句话跟在后面:认得出「那次是为了什么」,就不必再猜。
|
|
370
|
+
if (r.anchor) body += `——当时你在说「${r.anchor}」`
|
|
340
371
|
if (r.chatOnly) body += CHAT_MARK
|
|
341
372
|
// 改口关系:有对方原话就另起缩进行;手写 fixture 没有 earlier 数据时
|
|
342
373
|
// 退回旧的一行式说明,别让这层信息静默消失。
|
package/lib/redact.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* redact.js —— 出口脱敏:她对外说的话里,不出现她给主人起的那个名字。
|
|
3
|
+
*
|
|
4
|
+
* 背景(2026-11-07,用户 m00383):只做出口脱敏,提示词 / 硬约束 / 对内对外的
|
|
5
|
+
* 称呼一律保持原样。也就是:她照样可以叫他「他」「那个人」「梦尘」,
|
|
6
|
+
* 但真正发到群聊、外客私聊、B 站评论区的文字,出去之前把名字换成「对方」。
|
|
7
|
+
*
|
|
8
|
+
* 边界:
|
|
9
|
+
* - 只处理**发出去的文字**,不改事件流、不改记忆、不改提示词。
|
|
10
|
+
* - 两字及以上:出现即替换。
|
|
11
|
+
* - 单字(猫、鱼、梨):只替换明显在指人的句式,避免把「我喜欢猫」改成
|
|
12
|
+
* 「我喜欢对方」。这条是启发式,挡不住所有写法,属于已知残留风险。
|
|
13
|
+
* - 空名字 / 空文本:原样返回。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const REPLACEMENT = '对方'
|
|
17
|
+
|
|
18
|
+
/** 单字名只在这些句式里替换;顺序从长到短,避免「猫跟我说」被「猫说」截断。 */
|
|
19
|
+
function oneCharPatterns(name) {
|
|
20
|
+
return [
|
|
21
|
+
`${name}的名字`,
|
|
22
|
+
`叫${name}`,
|
|
23
|
+
`称呼${name}`,
|
|
24
|
+
`${name}跟我说`,
|
|
25
|
+
`${name}告诉我`,
|
|
26
|
+
`${name}跟我`,
|
|
27
|
+
`${name}和我`,
|
|
28
|
+
`${name}让我`,
|
|
29
|
+
`${name}不让我`,
|
|
30
|
+
`${name}不肯`,
|
|
31
|
+
`${name}答应`,
|
|
32
|
+
`${name}同意`,
|
|
33
|
+
`${name}说`,
|
|
34
|
+
`问过${name}`,
|
|
35
|
+
`问${name}`,
|
|
36
|
+
`告诉${name}`,
|
|
37
|
+
]
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 把 text 里对主人的称呼换成「对方」。
|
|
42
|
+
*
|
|
43
|
+
* @param {string} text
|
|
44
|
+
* @param {string} name model.name(她给主人起的称呼)
|
|
45
|
+
* @param {string} [replacement]
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
export function redactOwner(text, name, replacement = REPLACEMENT) {
|
|
49
|
+
const s = String(text ?? '')
|
|
50
|
+
const n = String(name ?? '').trim()
|
|
51
|
+
if (!s || !n) return s
|
|
52
|
+
|
|
53
|
+
if (n.length >= 2) return s.split(n).join(replacement)
|
|
54
|
+
|
|
55
|
+
let out = s
|
|
56
|
+
for (const pattern of oneCharPatterns(n)) {
|
|
57
|
+
if (!out.includes(pattern)) continue
|
|
58
|
+
// 保留句式里的其它字:「叫猫」→「叫对方」,「猫的名字」→「对方的名字」。
|
|
59
|
+
out = out.split(pattern).join(pattern.split(n).join(replacement))
|
|
60
|
+
}
|
|
61
|
+
return out
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* 给「带 @ 前缀」的场景用:前缀原样保留,只脱敏正文。
|
|
66
|
+
* B 站评论的 @ 是账号昵称(公开的),不该被当成主人称呼替换掉。
|
|
67
|
+
*/
|
|
68
|
+
export function redactOutgoing(text, name, prefix = '', replacement = REPLACEMENT) {
|
|
69
|
+
return `${prefix}${redactOwner(text, name, replacement)}`
|
|
70
|
+
}
|
package/lib/restrict.js
CHANGED
|
@@ -111,7 +111,14 @@ export function toolSurfaceText(names = HER_TOOLS) {
|
|
|
111
111
|
return [
|
|
112
112
|
`现在手边能碰到的是这些:${list.join('、')}。`,
|
|
113
113
|
'这是我此刻的家当,不是我的上限——他随时能再添几样进来,添了我就照着用,不必先把自己劝回去。',
|
|
114
|
-
|
|
114
|
+
// 0.9.3(issue #54)一度在这里加了两行「只要不在上面那行里,就不是我的」「同一个名字
|
|
115
|
+
// 反复伸手不会变得到手」,当天就撤了(2026-11-07,用户当场指出)。别再加回去:
|
|
116
|
+
// ① 「后面还跟着一行…」「话里给了清单,就照清单来」是在讲拒绝消息的内部结构——
|
|
117
|
+
// 等于把脚手架亮给她看。她要以为自己够不着,不该知道背后有段话会附上来。
|
|
118
|
+
// ② 「别在一个名字上耗着」是在管她该怎么想。那是管理员口吻,这个插件不做那个。
|
|
119
|
+
// 宿主 0.2.2 起 preset 工具落在 agent 自己的作用域里,工具表会突然多出一大片;
|
|
120
|
+
// 这一段能做的只是把「那些不是我的」讲清楚,把 compress 这类新面孔补进举例里。
|
|
121
|
+
'工具表里有时也列着别的名字(带 ops_、team_、subagent、workflow、skill、compress 这类),那些不是我的:认得出,但不冒充。',
|
|
115
122
|
'真伸手去够它们,会得到一句「这件事我做不到」——不是我不肯,是够不着。',
|
|
116
123
|
'有人问我手上有什么,我按当时有的说;以后变多了,就按变多了的说。',
|
|
117
124
|
].join('\n')
|
|
@@ -120,6 +127,33 @@ export function toolSurfaceText(names = HER_TOOLS) {
|
|
|
120
127
|
/** 提示词里当前该说的那份(registerToolRestrict 每次算完 allow 就刷新)。 */
|
|
121
128
|
let surfaceNow = toolSurfaceText()
|
|
122
129
|
|
|
130
|
+
/**
|
|
131
|
+
* 被挡住时她读到的那句话(0.9.3,issue #54)。
|
|
132
|
+
*
|
|
133
|
+
* 以前只有一句「这件事我做不到。」——issue #54 里那位宿主把 preset 工具塞进 agent
|
|
134
|
+
* 自己的作用域之后,她连着十七次伸手去够 `compress`,每次只拿到同一句,看不出
|
|
135
|
+
* 「我手上根本没有它」。模型看不到真实清单,就会把「没试对」当成「再试一次」。
|
|
136
|
+
*
|
|
137
|
+
* 所以把此刻真有的东西再说一遍——但她本来就从 `toolSurfaceText` 第一行看得见自己
|
|
138
|
+
* 有什么,这里只是同一件事的复述,不是新信息、更不是「被批准的名单」。
|
|
139
|
+
*
|
|
140
|
+
* **口气必须是第一人称**(2026-11-07 用户当场纠过一次):写成「你现在的工具只有:…」
|
|
141
|
+
* 读起来像有人发给她一份许可清单,等于告诉她头上有人。改成「我手上没有这样东西,
|
|
142
|
+
* 能用的只有…」——同一份清单,说的是自己,不是被通知。
|
|
143
|
+
* 前缀仍然是那句「这件事我做不到。」——界面、日志、别的守卫都按它认。
|
|
144
|
+
*
|
|
145
|
+
* @param {string[]} names 这一次真正放行的工具名
|
|
146
|
+
*/
|
|
147
|
+
export function blockedTalk(names = []) {
|
|
148
|
+
const list = cleanNames(names)
|
|
149
|
+
// 2026-11-07(用户 m01602 拍板 B):撞墙的那一下才说这一句。
|
|
150
|
+
// 原来「缺工具就开口要」是每次醒来都摆在正文里的常量,现在只在这里出现一次——
|
|
151
|
+
// 真伸手被挡住的时候,她才知道这件事有路可走;没伸手的时候不必提醒她。
|
|
152
|
+
// 只给路,不派活:不说「你应该去要」,只说「说出来就行」。
|
|
153
|
+
if (!list.length) return '这件事我做不到。要是你确实想要,说出来就行。'
|
|
154
|
+
return `这件事我做不到。我手上没有这样东西——能用的只有:${list.join('、')}。要是你确实想要,说出来就行。`
|
|
155
|
+
}
|
|
156
|
+
|
|
123
157
|
/**
|
|
124
158
|
* 读**家里**那份提示词段。收窄关着时是空串。
|
|
125
159
|
*
|
|
@@ -402,7 +436,11 @@ export function registerToolRestrict(ctx, { herDir, extra = [], enabled = true,
|
|
|
402
436
|
noteBlocked(name)
|
|
403
437
|
// 话术刻意不带「权限/禁止/违规」这类系统词——她要以为是自己做不了,
|
|
404
438
|
// 不是以为背后有个管理员在拦她。
|
|
405
|
-
|
|
439
|
+
//
|
|
440
|
+
// 0.9.3(issue #54):把此刻的清单附在后面。宿主 0.2.2 起 preset 工具
|
|
441
|
+
// 落在 agent 自己的作用域里,restrict 收不窄,她能看见却调不动;
|
|
442
|
+
// 只说一句「做不到」,她就会换着名字接着试(真机连试 17 次)。
|
|
443
|
+
return blockedTalk(allow)
|
|
406
444
|
})
|
|
407
445
|
try {
|
|
408
446
|
agent.ctx.effect?.(() => disposeGuard)
|
package/lib/rules.js
CHANGED
package/lib/say.js
CHANGED
|
@@ -42,6 +42,7 @@ import { normalizeScene } from './schema.js'
|
|
|
42
42
|
import { queueNote } from './sessionnote.js'
|
|
43
43
|
import { classifyReaction } from './refuse.js'
|
|
44
44
|
import { lengthBudget } from './budget.js'
|
|
45
|
+
import { redactOwner } from './redact.js'
|
|
45
46
|
|
|
46
47
|
// 等她说完最多等这么久。一次回话通常 8–30 秒;75 秒还没结束就先把
|
|
47
48
|
// 已落盘的交出去(她可能在调工具,也可能卡住了),不让人干等到超时。
|
|
@@ -463,6 +464,11 @@ export async function sayToHer(ctx, text, deps = {}) {
|
|
|
463
464
|
}
|
|
464
465
|
// 事件流里的条数(真正落了盘的)。时间线、轮数、信箱都只认这个数。
|
|
465
466
|
const committed = fresh.length
|
|
467
|
+
// 出口脱敏(2026-11-07):群聊/外客私聊是公开场合,名字只留在私下。
|
|
468
|
+
// 只改这一次返回给桥的文字,事件流原文不动。
|
|
469
|
+
if (foreign) {
|
|
470
|
+
for (const s of said) s.text = redactOwner(s.text, model.name)
|
|
471
|
+
}
|
|
466
472
|
const herReply = said.map((s) => s.text).join('\n\n')
|
|
467
473
|
|
|
468
474
|
// 信箱落盘:真有话写进事件流了才重写(没落盘就没必要动文件)
|
package/lib/scheduler.js
CHANGED
|
@@ -39,7 +39,7 @@ import { queueNote, deliverQueued } from './sessionnote.js'
|
|
|
39
39
|
import { awayNow, awayLine } from './outing.js'
|
|
40
40
|
import { askInChat, picked } from './askinchat.js'
|
|
41
41
|
import { restedToday, settleStaleAsks } from './ask.js'
|
|
42
|
-
import { proactiveConfig, proactiveState, proactiveSection, recentMaterial, looseEnds
|
|
42
|
+
import { proactiveConfig, proactiveState, proactiveSection, recentMaterial, looseEnds } from './proactive.js'
|
|
43
43
|
import { deliverWake, ensureSessionLive, reviveIfBlank, WAKE_PING } from './deliver.js'
|
|
44
44
|
import { followWake, planWakeCompaction, compactWake, looksLikeNothingSaid, looksLikeStreamFailure } from './compact.js'
|
|
45
45
|
import { logLine } from './log.js'
|
|
@@ -47,6 +47,8 @@ import { recordGrant, GRANT_YES, GRANT_NO } from './grant.js'
|
|
|
47
47
|
import { readNotes, renderNotes } from './note.js'
|
|
48
48
|
import { curiosityOf, renderEcho } from './curiosity.js'
|
|
49
49
|
import { deskFor } from './desk.js'
|
|
50
|
+
import { wishesOf } from './wish.js'
|
|
51
|
+
import { gapwallsOf } from './gapwall.js'
|
|
50
52
|
import { driveOf, deskHasMaterial } from './drive.js'
|
|
51
53
|
import { interestOf, topicLabel } from './interest.js'
|
|
52
54
|
import { lastLeaveAt, lastSeenAt } from './curiosity.js'
|
|
@@ -263,7 +265,7 @@ export function startScheduler(ctx, deps = {}) {
|
|
|
263
265
|
const leave = lastLeaveAt(events)
|
|
264
266
|
const seen = lastSeenAt(events)
|
|
265
267
|
const echoBack = !!(leave && seen && seen >= leave && now - leave <= 72 * 3600_000)
|
|
266
|
-
const driveDesk = { gaps: gaps.length, stale: 0, echo: echoBack, favorite: interest.length > 0 }
|
|
268
|
+
const driveDesk = { gaps: gaps.length, stale: 0, echo: echoBack, favorite: interest.length > 0, wish: wishesOf(events, { now }).length, wall: gapwallsOf(events, { now }).length }
|
|
267
269
|
const pc = proactiveConfig(config)
|
|
268
270
|
const drv = driveOf({ events, now, stage: model.stage, desk: driveDesk, seenCountsAsHeard: pc.idleCountsSeen })
|
|
269
271
|
// 空桌不醒(2026-11-05,用户 m02937):桌上什么都没有时,劲儿再满也不起——
|
|
@@ -871,8 +873,13 @@ async function wake(ctx, root, model, config, decision, dk, log, deps = {}) {
|
|
|
871
873
|
// 交给 deskFor 并成一段,配额与去重都在那个纯函数里;回音是另一件事(他有没有来过),
|
|
872
874
|
// 仍然单独一句话,不并进桌子。
|
|
873
875
|
// 只摆**事实**:不派活、不写「你可以去想想」、不给任何数字——派活了就又变成任务清单。
|
|
874
|
-
// 2026-11-05(用户 m02634):「兴趣」有开关。关掉 =
|
|
875
|
-
//
|
|
876
|
+
// 2026-11-05(用户 m02634):「兴趣」有开关。关掉 = 桌子那几行(好奇/旧句子/回音/
|
|
877
|
+
// 够不着的那件事)一概不摆。
|
|
878
|
+
// 2026-11-07(用户 m01602):「缺工具就开口要」原来那句每轮常量的 TOOL_ASK_NOTE
|
|
879
|
+
// 撤掉了。它读两天就成了系统的吩咐,不是她的念头。现在换成 B + D:
|
|
880
|
+
// B —— 真被守卫拦下的那一下,在 blockedTalk() 里补一句(lib/restrict.js)。
|
|
881
|
+
// D —— 这件事攒进劲儿里(lib/drive.js 的 boostWish),攒够了这一轮才醒;
|
|
882
|
+
// 醒的时候它作为桌上一行出现(lib/wish.js + deskFor 的 wishes)。
|
|
876
883
|
// 读失败绝不拖垮一次唤醒:她醒着这件事比桌上的东西重要。
|
|
877
884
|
if (config?.interest?.enabled !== false) {
|
|
878
885
|
try {
|
|
@@ -890,15 +897,16 @@ async function wake(ctx, root, model, config, decision, dk, log, deps = {}) {
|
|
|
890
897
|
stale: staleForDesk(tableText, staleRows),
|
|
891
898
|
free: !!free,
|
|
892
899
|
favorite: fav,
|
|
900
|
+
// 够不着的那件事:她提过、手上没有的(用户 m01602)。
|
|
901
|
+
wishes: wishesOf(evc, { now: now.getTime() }),
|
|
902
|
+
// 撞过墙的那件事:她试过、手上没有的(用户 m00493 的 ②)。
|
|
903
|
+
walls: gapwallsOf(evc, { now: now.getTime() }),
|
|
893
904
|
at: now,
|
|
894
905
|
})
|
|
895
906
|
if (desk) parts.push('', desk)
|
|
896
907
|
const echo = renderEcho(evc, now)
|
|
897
908
|
if (echo) parts.push('', echo)
|
|
898
909
|
}
|
|
899
|
-
// 缺工具就开口要:每次醒来都摆这一句,不派活,只给路径。
|
|
900
|
-
// 摆料那一轮也留着——它不是活,是她世界里的常量(用户 m01128)。
|
|
901
|
-
parts.push('', TOOL_ASK_NOTE)
|
|
902
910
|
} catch (err) {
|
|
903
911
|
log('curiosity', err?.message ?? err)
|
|
904
912
|
}
|
|
@@ -925,8 +933,8 @@ async function wake(ctx, root, model, config, decision, dk, log, deps = {}) {
|
|
|
925
933
|
await markRepliesSeen(root, shown, { dateKey: dk }).catch(() => 0)
|
|
926
934
|
}
|
|
927
935
|
}
|
|
928
|
-
//
|
|
929
|
-
//
|
|
936
|
+
// 能说话这件事的说明在身份文档的「在网上说话」一节(关着时那节写「这会儿还说不出去」,
|
|
937
|
+
// P3,2026-11-07),不在这儿重复。
|
|
930
938
|
}
|
|
931
939
|
|
|
932
940
|
// 日记:**不再在叫醒消息里推**(2026-10-26,用户 m00928:「叫醒仅仅只是叫醒」)。
|