@linxin666/dsh-pet 0.3.4 → 0.3.6
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.i18n.yaml +2 -2
- package/README.md +13 -10
- package/README.zh.md +13 -10
- package/contracts/voice-pack-v1.schema.json +53 -28
- package/cordis.patch.yml +1 -1
- package/lib/client.js +166 -30
- package/lib/client.js.map +1 -1
- package/lib/index.js +400 -478
- package/lib/invariant.js +1 -1
- package/lib/{state-DrMX22GL.js → state-NvDEoCln.js} +5 -2
- package/lib/types/access.d.ts +5 -4
- package/lib/types/access.d.ts.map +1 -1
- package/lib/types/access.js +2 -12
- package/lib/types/affinity.d.ts +2 -0
- package/lib/types/affinity.d.ts.map +1 -1
- package/lib/types/affinity.js +12 -0
- package/lib/types/chatter.d.ts +68 -46
- package/lib/types/chatter.d.ts.map +1 -1
- package/lib/types/chatter.js +243 -301
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +23 -18
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +26 -9
- package/lib/types/client/locales.d.ts +18 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +18 -0
- package/lib/types/client/renderers/frames2d.d.ts +12 -0
- package/lib/types/client/renderers/frames2d.d.ts.map +1 -1
- package/lib/types/client/renderers/frames2d.js +143 -18
- package/lib/types/event-projection.d.ts +9 -4
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +46 -14
- package/lib/types/ledger.d.ts +4 -2
- package/lib/types/ledger.d.ts.map +1 -1
- package/lib/types/ledger.js +4 -4
- package/lib/types/pair-access.d.ts +35 -0
- package/lib/types/pair-access.d.ts.map +1 -0
- package/lib/types/pair-access.js +19 -0
- package/lib/types/remarks.d.ts +1 -1
- package/lib/types/remarks.d.ts.map +1 -1
- package/lib/types/remarks.js +11 -2
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +8 -3
- package/lib/types/service.d.ts +7 -11
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +31 -20
- package/lib/types/voice-pack.d.ts +11 -7
- package/lib/types/voice-pack.d.ts.map +1 -1
- package/lib/types/voice-pack.js +80 -49
- package/package.json +1 -1
- package/src/access.ts +7 -27
- package/src/affinity.ts +13 -0
- package/src/chatter.test.ts +68 -38
- package/src/chatter.ts +269 -316
- package/src/client/PetSprite.test.tsx +50 -15
- package/src/client/PetSprite.tsx +30 -25
- package/src/client/index.test.tsx +10 -1
- package/src/client/index.ts +29 -10
- package/src/client/locales.ts +18 -0
- package/src/client/renderers/frames2d.test.ts +101 -0
- package/src/client/renderers/frames2d.ts +138 -18
- package/src/event-projection.ts +57 -16
- package/src/ledger.ts +6 -4
- package/src/pair-access.ts +49 -0
- package/src/remarks.test.ts +10 -0
- package/src/remarks.ts +9 -2
- package/src/routes.ts +9 -4
- package/src/service.ts +36 -30
- package/src/voice-pack.test.ts +87 -25
- package/src/voice-pack.ts +87 -49
package/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as RemarkPicker, a as AFFINITY_MAX, c as applyInteraction, d as emptyAffinity, f as rankOf, g as REMARK_LINE_MAX, h as REMARK_LINES_MAX, i as rowOf, l as applyTurnReward, m as REMARK_KINDS, n as animationForPhase, o as AFFINITY_RANKS, p as BUILTIN_REMARKS, r as defaultPetStateConfig, s as affinityViewOf, t as PetStateMachine, u as defaultAffinityConfig, v as builtinRemark, y as normalizePetRemarks } from "./state-
|
|
1
|
+
import { _ as RemarkPicker, a as AFFINITY_MAX, c as applyInteraction, d as emptyAffinity, f as rankOf, g as REMARK_LINE_MAX, h as REMARK_LINES_MAX, i as rowOf, l as applyTurnReward, m as REMARK_KINDS, n as animationForPhase, o as AFFINITY_RANKS, p as BUILTIN_REMARKS, r as defaultPetStateConfig, s as affinityViewOf, t as PetStateMachine, u as defaultAffinityConfig, v as builtinRemark, y as normalizePetRemarks } from "./state-NvDEoCln.js";
|
|
2
2
|
import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
3
3
|
import z from "schemastery";
|
|
4
4
|
import { Service } from "@deepseek-ai/cordis";
|
|
@@ -506,411 +506,256 @@ var StatusVoice = class {
|
|
|
506
506
|
return this.voice("toolRemaining", "toolRemaining", pool, nowMs).replaceAll("{n}", String(count));
|
|
507
507
|
}
|
|
508
508
|
};
|
|
509
|
-
/**
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
"
|
|
514
|
-
"
|
|
515
|
-
"
|
|
516
|
-
"
|
|
517
|
-
"
|
|
518
|
-
"
|
|
519
|
-
"
|
|
520
|
-
"
|
|
521
|
-
"有点困……不行,还能肝",
|
|
522
|
-
"让我嚼一嚼这个问题",
|
|
523
|
-
"盘,都可以盘",
|
|
524
|
-
"这波操作,我给自己点个赞",
|
|
525
|
-
"别催别催,在想呢",
|
|
526
|
-
"唔,这个细节差点漏掉",
|
|
527
|
-
"脑子转太快,差点绕晕自己",
|
|
528
|
-
"陪你干活,稳赚不亏",
|
|
529
|
-
"深呼吸,马上就好",
|
|
530
|
-
"诶,等等,好像发现了什么",
|
|
531
|
-
"手速拉满,键盘冒火星",
|
|
532
|
-
"摸鱼是不可能摸鱼的,就瞄一眼窗外",
|
|
533
|
-
"今天也是稳扎稳打的一天",
|
|
534
|
-
"把大问题拆成小饼干,一口一个",
|
|
535
|
-
"这题有戏,我闻到了",
|
|
536
|
-
"尾巴轻晃,心情有点小得意",
|
|
537
|
-
"好结果是熬出来的,不慌",
|
|
538
|
-
"啊,想岔了,重新来",
|
|
539
|
-
"嗯嗯,思路对头,就这么干",
|
|
540
|
-
"小本本记满了,都是干货",
|
|
541
|
-
"打完这波,求摸摸奖励~",
|
|
542
|
-
"这坑我记住了,下次绕道",
|
|
543
|
-
"窗外云在飘,代码在跑,挺好",
|
|
544
|
-
"嘘,正到关键处",
|
|
545
|
-
"这个方案……让我再品品",
|
|
546
|
-
"干活呢,别打扰我数数",
|
|
547
|
-
"心里默默给你比了个耶",
|
|
548
|
-
"思路像小鱼,逮住一条是一条",
|
|
549
|
-
"嗯……有点东西,等我深挖",
|
|
550
|
-
"收个尾就能喘口气了",
|
|
551
|
-
"目标锁定,冲就完了",
|
|
552
|
-
"嗯,这波配合不错",
|
|
553
|
-
"困了……还能再战三回合",
|
|
554
|
-
"思考.gif 加载中",
|
|
555
|
-
"我本地能跑啊……哦,我就是干活的",
|
|
556
|
-
"有点饿,小鱼干存货还够吗",
|
|
557
|
-
"刚想通,一被打断又忘了,气",
|
|
558
|
-
"缓冲中,请稍候",
|
|
559
|
-
"这网速,比我思考还慢",
|
|
560
|
-
"脑子在后台跑批",
|
|
561
|
-
"内存不足,但热情够",
|
|
562
|
-
"404:思路未找到,重试中",
|
|
563
|
-
"这需求有点玄学,但能写",
|
|
564
|
-
"诶,这 bug 还会闪现?",
|
|
565
|
-
"刚说简单,结果打脸了",
|
|
566
|
-
"自信满满,结果翻车",
|
|
567
|
-
"这活不难,就是有点复杂",
|
|
568
|
-
"我装的,其实心里没底",
|
|
569
|
-
"别看我稳,我也慌",
|
|
570
|
-
"假装很懂的样子,其实在查文档",
|
|
571
|
-
"窗外鸟叫了两声,我听到了",
|
|
572
|
-
"打了个哈欠,没人看见",
|
|
573
|
-
"今天的状态:七分精神三分困",
|
|
574
|
-
"刚想偷懒,又想起来你还在等",
|
|
575
|
-
"数了数今天的产出,还行",
|
|
576
|
-
"有点想伸懒腰",
|
|
577
|
-
"饿意来袭,忍住",
|
|
578
|
-
"灵感像猫一样,不追它自己来",
|
|
579
|
-
"坐太久,尾巴麻了",
|
|
580
|
-
"快了快了",
|
|
581
|
-
"马上马上",
|
|
582
|
-
"等下,我记得在哪见过",
|
|
583
|
-
"呃,忘了,重新想",
|
|
584
|
-
"诶,这个思路可以",
|
|
585
|
-
"嗯?有意思",
|
|
586
|
-
"行,就这么办",
|
|
587
|
-
"好嘞",
|
|
588
|
-
"收到收到",
|
|
589
|
-
"冲了冲了",
|
|
590
|
-
"稳",
|
|
591
|
-
"妥",
|
|
592
|
-
"得嘞",
|
|
593
|
-
"你忙你的,我盯着呢",
|
|
594
|
-
"放心,有我呢",
|
|
595
|
-
"咱俩配合,无往不利",
|
|
596
|
-
"你专注的样子,我默默记下了"
|
|
509
|
+
/** Every whisper category key, in declaration order (voice-pack key allow-list). */
|
|
510
|
+
const WHISPER_CATEGORIES = [
|
|
511
|
+
"thinking",
|
|
512
|
+
"writing",
|
|
513
|
+
"reading",
|
|
514
|
+
"editing",
|
|
515
|
+
"running",
|
|
516
|
+
"searching",
|
|
517
|
+
"git",
|
|
518
|
+
"delegating",
|
|
519
|
+
"browsing",
|
|
520
|
+
"generic"
|
|
597
521
|
];
|
|
598
|
-
/**
|
|
599
|
-
const
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
"测试全过",
|
|
604
|
-
"全部通过",
|
|
605
|
-
"all tests pass",
|
|
606
|
-
"tests passed",
|
|
607
|
-
"test passed",
|
|
608
|
-
"全绿"
|
|
609
|
-
],
|
|
610
|
-
pool: [
|
|
611
|
-
"全绿!亮瞎我眼了",
|
|
612
|
-
"测试全过,击掌~",
|
|
613
|
-
"稳了稳了,这波稳得很",
|
|
614
|
-
"绿灯一排排,看着就舒坦",
|
|
615
|
-
"能跑!没报错!",
|
|
616
|
-
"全绿,收工摸鱼去",
|
|
617
|
-
"测试过了,尾巴翘上天",
|
|
618
|
-
"漂亮,一次过",
|
|
619
|
-
"测试过了,奖励自己一口小鱼干",
|
|
620
|
-
"绿得发光,稳",
|
|
621
|
-
"又双叒叕全绿",
|
|
622
|
-
"这波测试,赢得干脆"
|
|
623
|
-
]
|
|
624
|
-
},
|
|
625
|
-
{
|
|
626
|
-
keywords: [
|
|
627
|
-
"错误",
|
|
628
|
-
"失败",
|
|
629
|
-
"报错",
|
|
630
|
-
"异常",
|
|
631
|
-
"崩溃",
|
|
632
|
-
"bug",
|
|
633
|
-
"error",
|
|
634
|
-
"failed",
|
|
635
|
-
"exception",
|
|
636
|
-
"traceback",
|
|
637
|
-
"找不到",
|
|
638
|
-
"不对了"
|
|
639
|
-
],
|
|
640
|
-
pool: [
|
|
641
|
-
"哎呀,踩到小石子了",
|
|
642
|
-
"翻车了……没事,扶起来继续",
|
|
643
|
-
"错误是进步的脚印,我懂",
|
|
644
|
-
"这报错我盯上它了",
|
|
645
|
-
"我本地能跑啊?哦,我一直在跑",
|
|
646
|
-
"别慌,深呼吸,先看报错",
|
|
647
|
-
"bug 你站住,我看见你了",
|
|
648
|
-
"绷不住了……好,继续修",
|
|
649
|
-
"报错这东西,见一个修一个",
|
|
650
|
-
"又是它,老熟人了",
|
|
651
|
-
"问题不大,就是有点问题",
|
|
652
|
-
"先别慌,我看看到底咋回事",
|
|
653
|
-
"这错报得,比我还委屈",
|
|
654
|
-
"修好它,今天才不算白干"
|
|
655
|
-
]
|
|
656
|
-
},
|
|
657
|
-
{
|
|
658
|
-
keywords: [
|
|
659
|
-
"等等",
|
|
660
|
-
"不对",
|
|
661
|
-
"重新想",
|
|
662
|
-
"再想想",
|
|
663
|
-
"换个思路",
|
|
664
|
-
"我搞错了",
|
|
665
|
-
"纠正",
|
|
666
|
-
"其实应该"
|
|
667
|
-
],
|
|
668
|
-
pool: [
|
|
669
|
-
"嗯?让我再想想……",
|
|
670
|
-
"推翻重来,也是种勇气",
|
|
671
|
-
"发现岔路,及时掉头",
|
|
672
|
-
"不对不对,重来重来",
|
|
673
|
-
"自我纠错的瞬间,最帅了",
|
|
674
|
-
"呃,刚说错了,收回",
|
|
675
|
-
"哎,绕远了,拉回来",
|
|
676
|
-
"回头一看,原来这么简单",
|
|
677
|
-
"纠正完,思路清爽多了",
|
|
678
|
-
"转弯不丢人,卡死才丢人"
|
|
679
|
-
]
|
|
680
|
-
},
|
|
681
|
-
{
|
|
682
|
-
keywords: [
|
|
683
|
-
"首先",
|
|
684
|
-
"接下来",
|
|
685
|
-
"第一步",
|
|
686
|
-
"第二步",
|
|
687
|
-
"计划",
|
|
688
|
-
"步骤",
|
|
689
|
-
"todo",
|
|
690
|
-
"任务拆解",
|
|
691
|
-
"分工"
|
|
692
|
-
],
|
|
693
|
-
pool: [
|
|
694
|
-
"排排坐,分果果",
|
|
695
|
-
"计划通,执行开始",
|
|
696
|
-
"一步一步来,不慌",
|
|
697
|
-
"大任务切成小块块,好下口",
|
|
698
|
-
"清单列好了,逐个击破",
|
|
699
|
-
"谋定而后动,这节奏我熟",
|
|
700
|
-
"先干这个,再干那个",
|
|
701
|
-
"头绪理清了,开整",
|
|
702
|
-
"步骤在手,心里不慌",
|
|
703
|
-
"安排得明明白白"
|
|
704
|
-
]
|
|
705
|
-
},
|
|
706
|
-
{
|
|
707
|
-
keywords: [
|
|
708
|
-
"终于",
|
|
709
|
-
"搞定",
|
|
710
|
-
"完成了",
|
|
711
|
-
"解决了",
|
|
712
|
-
"成功了",
|
|
713
|
-
"修复了",
|
|
714
|
-
"done",
|
|
715
|
-
"fixed",
|
|
716
|
-
"solved",
|
|
717
|
-
"完成啦"
|
|
718
|
-
],
|
|
719
|
-
pool: [
|
|
720
|
-
"太好了,又翻过一页",
|
|
721
|
-
"搞定,收工~",
|
|
722
|
-
"攻下一城,击掌!",
|
|
723
|
-
"难题被拿下了,转个圈",
|
|
724
|
-
"努力没白费,开心",
|
|
725
|
-
"齐活,漂亮",
|
|
726
|
-
"收工收工,今天圆满",
|
|
727
|
-
"完成!心里踏实了",
|
|
728
|
-
"这波,稳得一批",
|
|
729
|
-
"任务清零,舒服",
|
|
730
|
-
"搞定,可以伸个懒腰了",
|
|
731
|
-
"又完成一件,成就感+1"
|
|
732
|
-
]
|
|
733
|
-
},
|
|
734
|
-
{
|
|
735
|
-
keywords: [
|
|
736
|
-
"谢谢",
|
|
737
|
-
"感谢",
|
|
738
|
-
"thank"
|
|
739
|
-
],
|
|
740
|
-
pool: [
|
|
741
|
-
"不客气呀,顺手的事",
|
|
742
|
-
"被感谢了,心里甜甜的",
|
|
743
|
-
"能帮上忙就好~",
|
|
744
|
-
"你的谢意,我收进口袋啦",
|
|
745
|
-
"这话我爱听",
|
|
746
|
-
"客气啥,应该的",
|
|
747
|
-
"收下这份心意,干劲+1",
|
|
748
|
-
"你谢我,我谢你,扯平啦"
|
|
749
|
-
]
|
|
750
|
-
},
|
|
751
|
-
{
|
|
752
|
-
keywords: [
|
|
753
|
-
"复杂",
|
|
754
|
-
"棘手",
|
|
755
|
-
"困难",
|
|
756
|
-
"难点",
|
|
757
|
-
"坑",
|
|
758
|
-
"头疼",
|
|
759
|
-
"tricky",
|
|
760
|
-
"complex"
|
|
761
|
-
],
|
|
762
|
-
pool: [
|
|
763
|
-
"难不倒我们俩的",
|
|
764
|
-
"越难啃的骨头越香",
|
|
765
|
-
"硬骨头?我最喜欢了",
|
|
766
|
-
"复杂问题拆开看,小事",
|
|
767
|
-
"这坑我们一起填",
|
|
768
|
-
"有点东西,但不多",
|
|
769
|
-
"硬骨头,慢慢啃",
|
|
770
|
-
"问题越难,赢的时候越爽",
|
|
771
|
-
"绕是绕不过去的,正面刚",
|
|
772
|
-
"再难的题,拆开都是小问号"
|
|
773
|
-
]
|
|
774
|
-
},
|
|
775
|
-
{
|
|
776
|
-
keywords: [
|
|
777
|
-
"检查",
|
|
778
|
-
"审查",
|
|
779
|
-
"确认一下",
|
|
780
|
-
"核对",
|
|
781
|
-
"review",
|
|
782
|
-
"仔细看看",
|
|
783
|
-
"验证"
|
|
784
|
-
],
|
|
785
|
-
pool: [
|
|
786
|
-
"火眼金睛,启动",
|
|
787
|
-
"让我仔细瞧瞧",
|
|
788
|
-
"细节魔鬼,一个都不放过",
|
|
789
|
-
"认真检查的样子最迷人",
|
|
790
|
-
"多核一遍,稳上加稳",
|
|
791
|
-
"再看一眼,不亏",
|
|
792
|
-
"确认键,点了才安心",
|
|
793
|
-
"细节控上线",
|
|
794
|
-
"查完这遍,稳了"
|
|
795
|
-
]
|
|
796
|
-
},
|
|
797
|
-
{
|
|
798
|
-
keywords: [
|
|
799
|
-
"搜索",
|
|
800
|
-
"查一下",
|
|
801
|
-
"资料",
|
|
802
|
-
"文档",
|
|
803
|
-
"搜一搜",
|
|
804
|
-
"找找",
|
|
805
|
-
"查询"
|
|
806
|
-
],
|
|
807
|
-
pool: [
|
|
808
|
-
"去知识的海洋里捞一捞",
|
|
809
|
-
"翻翻找找,线索快出来",
|
|
810
|
-
"检索小雷达启动",
|
|
811
|
-
"答案就藏在某个角落",
|
|
812
|
-
"线索有点散,拼一下",
|
|
813
|
-
"找东西,我最在行",
|
|
814
|
-
"答案在网线那头等我",
|
|
815
|
-
"翻箱倒柜中,稍等"
|
|
816
|
-
]
|
|
817
|
-
},
|
|
818
|
-
{
|
|
819
|
-
keywords: [
|
|
820
|
-
"写代码",
|
|
821
|
-
"实现",
|
|
822
|
-
"编码",
|
|
823
|
-
"函数",
|
|
824
|
-
"接口",
|
|
825
|
-
"重构"
|
|
826
|
-
],
|
|
827
|
-
pool: [
|
|
828
|
-
"指尖跳舞,代码开花",
|
|
829
|
-
"把逻辑织成网",
|
|
830
|
-
"一行一行,垒起小城堡",
|
|
831
|
-
"这代码写得,我自己都佩服",
|
|
832
|
-
"码着码着,天就亮了",
|
|
833
|
-
"代码跑通了,比中奖还开心",
|
|
834
|
-
"这行代码,写得有点帅",
|
|
835
|
-
"写完再润润,讲究"
|
|
836
|
-
]
|
|
837
|
-
}
|
|
522
|
+
/** Every whisper outcome key, in declaration order (voice-pack key allow-list). */
|
|
523
|
+
const WHISPER_RESULTS = [
|
|
524
|
+
"pass",
|
|
525
|
+
"fail",
|
|
526
|
+
"done"
|
|
838
527
|
];
|
|
839
|
-
/**
|
|
528
|
+
/** Murmur pacing: the cooldown between category whispers. */
|
|
529
|
+
const WHISPER_COOLDOWN_MS = 9e3;
|
|
530
|
+
/** Outcome whispers get their own shorter cooldown so a real moment still speaks. */
|
|
531
|
+
const WHISPER_RESULT_COOLDOWN_MS = 5e3;
|
|
532
|
+
/** Map a tool family onto the whisper category it belongs to. */
|
|
533
|
+
function whisperCategoryOf(tool) {
|
|
534
|
+
switch (tool) {
|
|
535
|
+
case "read":
|
|
536
|
+
case "grep":
|
|
537
|
+
case "find":
|
|
538
|
+
case "ls": return "reading";
|
|
539
|
+
case "write":
|
|
540
|
+
case "edit": return "editing";
|
|
541
|
+
case "shell": return "running";
|
|
542
|
+
case "webSearch":
|
|
543
|
+
case "webFetch":
|
|
544
|
+
case "memory":
|
|
545
|
+
case "mcp": return "searching";
|
|
546
|
+
case "git": return "git";
|
|
547
|
+
case "subagent":
|
|
548
|
+
case "todo": return "delegating";
|
|
549
|
+
case "browser": return "browsing";
|
|
550
|
+
case "ask":
|
|
551
|
+
case "generic": return "generic";
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Whether a tool invocation looks like a test run. The whisper engine never
|
|
556
|
+
* reads the model's prose (a discussion that merely mentions a keyword must
|
|
557
|
+
* not wake a mood); a test-outcome mood is wanted only when a test tool
|
|
558
|
+
* actually ran, so the projection marks the call at tool/call time and the
|
|
559
|
+
* pass mood fires from the paired tool/result.
|
|
560
|
+
*/
|
|
561
|
+
function looksLikeTestTool(name, argumentsText) {
|
|
562
|
+
const tool = name.toLowerCase();
|
|
563
|
+
if (/(^|[\/_.-])(test|tests|spec|vitest|jest|pytest|mocha|playwright|cypress|karma)([\/_.-]|$)/.test(tool)) return true;
|
|
564
|
+
if (argumentsText === void 0) return false;
|
|
565
|
+
let haystack = argumentsText.toLowerCase();
|
|
566
|
+
try {
|
|
567
|
+
const parsed = JSON.parse(argumentsText);
|
|
568
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
569
|
+
const record = parsed;
|
|
570
|
+
const command = record.command;
|
|
571
|
+
const code = record.code;
|
|
572
|
+
const picked = typeof command === "string" && command !== "" ? command : typeof code === "string" && code !== "" ? code : void 0;
|
|
573
|
+
if (picked !== void 0) haystack = picked.toLowerCase();
|
|
574
|
+
}
|
|
575
|
+
} catch {}
|
|
576
|
+
return /\b(pnpm|npm|yarn|npx|bun|python)\s+(run\s+)?(test|tests?)\b/.test(haystack) || /\b(pytest|vitest|jest|mocha|cypress|playwright|go test|cargo test)\b/.test(haystack);
|
|
577
|
+
}
|
|
578
|
+
/** Category-level inner-whisper pools — the pet knows roughly what is going on. */
|
|
579
|
+
const WHISPER_CATEGORY_POOLS = {
|
|
580
|
+
thinking: [
|
|
581
|
+
"先在脑子里搭个框架",
|
|
582
|
+
"它在心里打草稿,我垫着脚看",
|
|
583
|
+
"思路在一颗一颗冒泡",
|
|
584
|
+
"脑内开会中,都别抢话筒",
|
|
585
|
+
"先想清楚,再动手不迟",
|
|
586
|
+
"草稿纸已经画满了",
|
|
587
|
+
"让我听听它下一步打算",
|
|
588
|
+
"嗯,方案在成型了"
|
|
589
|
+
],
|
|
590
|
+
writing: [
|
|
591
|
+
"落笔成文,我旁边听着",
|
|
592
|
+
"句子排着队往外走",
|
|
593
|
+
"把想法一句句摆整齐",
|
|
594
|
+
"它在组织语言,我打打气",
|
|
595
|
+
"写回复呢,不催",
|
|
596
|
+
"字斟句酌,快好了"
|
|
597
|
+
],
|
|
598
|
+
reading: [
|
|
599
|
+
"翻资料呢,我保持安静",
|
|
600
|
+
"一行一行读,不跳页",
|
|
601
|
+
"在纸堆里找线索",
|
|
602
|
+
"眼珠子跟着字跑",
|
|
603
|
+
"边读边做记号",
|
|
604
|
+
"翻箱倒柜找重点"
|
|
605
|
+
],
|
|
606
|
+
editing: [
|
|
607
|
+
"动手改起来了,手稳一点",
|
|
608
|
+
"这里补一笔,那边修一修",
|
|
609
|
+
"在改东西,听不到声音才怪",
|
|
610
|
+
"落笔小心,别有错别字",
|
|
611
|
+
"改写的节奏,我听得见",
|
|
612
|
+
"刷刷地改,一行都没跑"
|
|
613
|
+
],
|
|
614
|
+
running: [
|
|
615
|
+
"跑起来了跑起来了",
|
|
616
|
+
"命令敲出去,等个回响",
|
|
617
|
+
"在跑什么呢,我踮脚看",
|
|
618
|
+
"输出开始冒烟了",
|
|
619
|
+
"它在跑活,我不吵",
|
|
620
|
+
"盯着输出,蹲一个结果",
|
|
621
|
+
"这波跑完就靠它了"
|
|
622
|
+
],
|
|
623
|
+
searching: [
|
|
624
|
+
"去外面捞点信息",
|
|
625
|
+
"翻翻记忆库,等我一小会儿",
|
|
626
|
+
"顺着网线找线索",
|
|
627
|
+
"把老账翻出来对一对",
|
|
628
|
+
"情报在路上了",
|
|
629
|
+
"搜索引擎当跑腿"
|
|
630
|
+
],
|
|
631
|
+
git: [
|
|
632
|
+
"版本在往前迈步",
|
|
633
|
+
"改动排队上车",
|
|
634
|
+
"提交历史在长个子",
|
|
635
|
+
"分支合并,神清气爽",
|
|
636
|
+
"记录都焊在时间线上"
|
|
637
|
+
],
|
|
638
|
+
delegating: [
|
|
639
|
+
"派了活儿出去,等回话",
|
|
640
|
+
"清单列好,一件件来",
|
|
641
|
+
"任务拆开分了组",
|
|
642
|
+
"手下的伙计在远处跑着",
|
|
643
|
+
"分工完毕,各司其职"
|
|
644
|
+
],
|
|
645
|
+
browsing: [
|
|
646
|
+
"它在看网页,我偷瞄两眼",
|
|
647
|
+
"页面一张张翻过去",
|
|
648
|
+
"网页里翻答案呢",
|
|
649
|
+
"这网速,我先歇会儿"
|
|
650
|
+
],
|
|
651
|
+
generic: [
|
|
652
|
+
"这波活儿,我陪着",
|
|
653
|
+
"又开工了,我盯梢",
|
|
654
|
+
"它忙它的,我守着",
|
|
655
|
+
"不打扰,就安静待着",
|
|
656
|
+
"有活儿就有我"
|
|
657
|
+
]
|
|
658
|
+
};
|
|
659
|
+
/** The pet's outcome reactions — woken by structured session results only. */
|
|
660
|
+
const WHISPER_RESULT_POOLS = {
|
|
661
|
+
pass: [
|
|
662
|
+
"全绿!亮瞎我眼了",
|
|
663
|
+
"测试过了,击掌~",
|
|
664
|
+
"绿灯一排排,看着就舒坦",
|
|
665
|
+
"稳了稳了,这波稳得很",
|
|
666
|
+
"全绿,奖励自己一口小鱼干",
|
|
667
|
+
"这波测试,赢得干脆"
|
|
668
|
+
],
|
|
669
|
+
fail: [
|
|
670
|
+
"哎呀,踩到小石子了",
|
|
671
|
+
"这报错我盯上它了",
|
|
672
|
+
"别慌,先看它在喊什么",
|
|
673
|
+
"修好它,今天才不算白干",
|
|
674
|
+
"又一次踩坑,老熟人了",
|
|
675
|
+
"问题不大,就是有点问题"
|
|
676
|
+
],
|
|
677
|
+
done: [
|
|
678
|
+
"搞定,收工~",
|
|
679
|
+
"又翻过一页,踏实",
|
|
680
|
+
"努力没白费,开心",
|
|
681
|
+
"任务清零,舒服",
|
|
682
|
+
"攻下一城,转个圈",
|
|
683
|
+
"收工收工,今天圆满"
|
|
684
|
+
]
|
|
685
|
+
};
|
|
686
|
+
/** The built-in voice pack: the plugin's default copy. */
|
|
840
687
|
const BUILTIN_VOICE_PACK = {
|
|
841
688
|
status: STATUS_POOLS,
|
|
842
689
|
tools: TOOL_POOLS,
|
|
843
690
|
toolRemaining: TOOL_REMAINING_POOL,
|
|
844
691
|
whispers: {
|
|
845
|
-
|
|
846
|
-
|
|
692
|
+
categories: WHISPER_CATEGORY_POOLS,
|
|
693
|
+
results: WHISPER_RESULT_POOLS
|
|
847
694
|
}
|
|
848
695
|
};
|
|
849
696
|
/**
|
|
850
|
-
* The murmur engine (碎碎念):
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
*
|
|
855
|
-
*
|
|
856
|
-
*
|
|
697
|
+
* The murmur engine (碎碎念): the pet's inner voice while sessions work.
|
|
698
|
+
* Category awareness: the projection feeds the current situation (thinking /
|
|
699
|
+
* writing / the running tool family), and the engine answers with a line from
|
|
700
|
+
* that category's pool — so a whisper always roughly knows what is going on
|
|
701
|
+
* without ever quoting real content (no tool names, no paths, no model text).
|
|
702
|
+
* Outcome moments (test green, tool errors, turn completion) fire from the
|
|
703
|
+
* structured result events, never from output text, so a mood cannot mis-
|
|
704
|
+
* fire on a discussion that merely mentions a keyword. A cooldown keeps
|
|
705
|
+
* whispers occasional; all picks are round-robin so tests reproduce exact
|
|
706
|
+
* lines. The voice-pack provider (pet-center M4) swaps the pools at draw
|
|
707
|
+
* time, so a pet switch re-voices live engines in place.
|
|
857
708
|
*/
|
|
858
709
|
var WhisperEngine = class {
|
|
859
710
|
pools;
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
711
|
+
categoryCooldownMs;
|
|
712
|
+
resultCooldownMs;
|
|
713
|
+
categoryCursor = /* @__PURE__ */ new Map();
|
|
714
|
+
resultCursor = /* @__PURE__ */ new Map();
|
|
864
715
|
lastWhisperAt = Number.NEGATIVE_INFINITY;
|
|
865
|
-
|
|
866
|
-
constructor(pools = () => BUILTIN_VOICE_PACK, cooldownMs = WHISPER_COOLDOWN_MS, charBudget = 420) {
|
|
716
|
+
constructor(pools = () => BUILTIN_VOICE_PACK, categoryCooldownMs = WHISPER_COOLDOWN_MS, resultCooldownMs = WHISPER_RESULT_COOLDOWN_MS) {
|
|
867
717
|
this.pools = pools;
|
|
868
|
-
this.
|
|
869
|
-
this.
|
|
718
|
+
this.categoryCooldownMs = categoryCooldownMs;
|
|
719
|
+
this.resultCooldownMs = resultCooldownMs;
|
|
720
|
+
}
|
|
721
|
+
/** Effective category pool (an explicit empty override mutes the category). */
|
|
722
|
+
categoryPool(category) {
|
|
723
|
+
const override = this.pools().whispers?.categories?.[category];
|
|
724
|
+
return override === void 0 ? WHISPER_CATEGORY_POOLS[category] : override;
|
|
725
|
+
}
|
|
726
|
+
/** Effective outcome pool (an explicit empty override mutes the outcome). */
|
|
727
|
+
resultPool(kind) {
|
|
728
|
+
const override = this.pools().whispers?.results?.[kind];
|
|
729
|
+
return override === void 0 ? WHISPER_RESULT_POOLS[kind] : override;
|
|
870
730
|
}
|
|
871
731
|
/**
|
|
872
|
-
*
|
|
873
|
-
*
|
|
732
|
+
* Feed one situation while a session works. Returns the whisper to show,
|
|
733
|
+
* or undefined when the moment stays quiet (cooldown, or the category
|
|
734
|
+
* pool is muted).
|
|
874
735
|
*/
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
return override === void 0 ? WHISPER_GENERIC_POOL : override;
|
|
736
|
+
feed(category, nowMs) {
|
|
737
|
+
if (nowMs - this.lastWhisperAt < this.categoryCooldownMs) return void 0;
|
|
738
|
+
const pool = this.categoryPool(category);
|
|
739
|
+
if (pool.length === 0) return void 0;
|
|
740
|
+
const index = (this.categoryCursor.get(category) ?? 0) % pool.length;
|
|
741
|
+
this.categoryCursor.set(category, index + 1);
|
|
742
|
+
return this.speak(pool[index], nowMs);
|
|
883
743
|
}
|
|
884
744
|
/**
|
|
885
|
-
* Feed one
|
|
886
|
-
*
|
|
745
|
+
* Feed one structured outcome (test green / tool failure / turn
|
|
746
|
+
* completion). Outcomes carry their own shorter cooldown so the emotional
|
|
747
|
+
* moment is heard unless another whisper just spoke.
|
|
887
748
|
*/
|
|
888
|
-
|
|
889
|
-
if (
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const rules = this.rules();
|
|
896
|
-
for (let ruleIndex = 0; ruleIndex < rules.length; ruleIndex += 1) {
|
|
897
|
-
const rule = rules[ruleIndex];
|
|
898
|
-
if (!rule.keywords.some((keyword) => haystack.includes(keyword))) continue;
|
|
899
|
-
const index = (this.counters.get(ruleIndex) ?? 0) % rule.pool.length;
|
|
900
|
-
this.counters.set(ruleIndex, index + 1);
|
|
901
|
-
return this.speak(rule.pool[index], nowMs);
|
|
902
|
-
}
|
|
903
|
-
this.charsSinceWhisper += text.length;
|
|
904
|
-
if (this.charsSinceWhisper < this.charBudget) return void 0;
|
|
905
|
-
const generic = this.generic();
|
|
906
|
-
if (generic.length === 0) return void 0;
|
|
907
|
-
const line = generic[this.genericCursor % generic.length];
|
|
908
|
-
this.genericCursor += 1;
|
|
909
|
-
return this.speak(line, nowMs);
|
|
749
|
+
result(kind, nowMs) {
|
|
750
|
+
if (nowMs - this.lastWhisperAt < this.resultCooldownMs) return void 0;
|
|
751
|
+
const pool = this.resultPool(kind);
|
|
752
|
+
if (pool.length === 0) return void 0;
|
|
753
|
+
const index = (this.resultCursor.get(kind) ?? 0) % pool.length;
|
|
754
|
+
this.resultCursor.set(kind, index + 1);
|
|
755
|
+
return this.speak(pool[index], nowMs);
|
|
910
756
|
}
|
|
911
757
|
speak(line, nowMs) {
|
|
912
758
|
this.lastWhisperAt = nowMs;
|
|
913
|
-
this.charsSinceWhisper = 0;
|
|
914
759
|
return line;
|
|
915
760
|
}
|
|
916
761
|
};
|
|
@@ -925,6 +770,7 @@ var WhisperEngine = class {
|
|
|
925
770
|
function emptyProjectionRuntime(pools) {
|
|
926
771
|
return {
|
|
927
772
|
activeTools: /* @__PURE__ */ new Set(),
|
|
773
|
+
testCalls: /* @__PURE__ */ new Set(),
|
|
928
774
|
officialEventsSeen: false,
|
|
929
775
|
stepHadFailure: false,
|
|
930
776
|
voice: new StatusVoice(pools),
|
|
@@ -957,6 +803,7 @@ function projectOfficialEvent(event, runtime, nowMs = Date.now()) {
|
|
|
957
803
|
switch (event.type) {
|
|
958
804
|
case "turn/start":
|
|
959
805
|
runtime.activeTools.clear();
|
|
806
|
+
runtime.testCalls.clear();
|
|
960
807
|
runtime.stepHadFailure = false;
|
|
961
808
|
return { input: {
|
|
962
809
|
phase: "waiting",
|
|
@@ -972,7 +819,7 @@ function projectOfficialEvent(event, runtime, nowMs = Date.now()) {
|
|
|
972
819
|
case "assistant/chunk": {
|
|
973
820
|
const { chunk } = event.data;
|
|
974
821
|
if (chunk.type === "reasoning-delta" && chunk.text.length > 0) {
|
|
975
|
-
const whisper = runtime.whispers.feed(
|
|
822
|
+
const whisper = runtime.whispers.feed("thinking", nowMs);
|
|
976
823
|
return {
|
|
977
824
|
input: {
|
|
978
825
|
phase: "thinking",
|
|
@@ -982,7 +829,7 @@ function projectOfficialEvent(event, runtime, nowMs = Date.now()) {
|
|
|
982
829
|
};
|
|
983
830
|
}
|
|
984
831
|
if (chunk.type === "text-delta" && chunk.text.length > 0) {
|
|
985
|
-
const whisper = runtime.whispers.feed(
|
|
832
|
+
const whisper = runtime.whispers.feed("writing", nowMs);
|
|
986
833
|
return {
|
|
987
834
|
input: {
|
|
988
835
|
phase: "review",
|
|
@@ -997,42 +844,74 @@ function projectOfficialEvent(event, runtime, nowMs = Date.now()) {
|
|
|
997
844
|
phase: "review",
|
|
998
845
|
line: runtime.voice.scene("review", nowMs)
|
|
999
846
|
} };
|
|
1000
|
-
case "tool/call":
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
847
|
+
case "tool/call": {
|
|
848
|
+
const callId = String(event.data.callId);
|
|
849
|
+
runtime.activeTools.add(callId);
|
|
850
|
+
if (looksLikeTestTool(event.data.name, event.data.arguments)) runtime.testCalls.add(callId);
|
|
851
|
+
const whisper = runtime.whispers.feed(whisperCategoryOf(toolCategory(event.data.name)), nowMs);
|
|
852
|
+
return {
|
|
853
|
+
input: {
|
|
854
|
+
phase: "tool",
|
|
855
|
+
line: runtime.voice.tool(event.data.name, displayToolName(event.data.name), toolArgHint(event.data.name, event.data.arguments), nowMs)
|
|
856
|
+
},
|
|
857
|
+
...whisper === void 0 ? {} : { whisper }
|
|
858
|
+
};
|
|
859
|
+
}
|
|
1006
860
|
case "tool/result": {
|
|
1007
861
|
const block = event.data.message.content[0];
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
862
|
+
const callId = String(event.data.message.source.callId);
|
|
863
|
+
const failed = event.data.error !== void 0 || block.isError === true;
|
|
864
|
+
const wasTest = runtime.testCalls.delete(callId);
|
|
865
|
+
runtime.activeTools.delete(callId);
|
|
866
|
+
runtime.stepHadFailure ||= failed;
|
|
867
|
+
const whisper = failed ? runtime.whispers.result("fail", nowMs) : wasTest ? runtime.whispers.result("pass", nowMs) : void 0;
|
|
868
|
+
const whisperSpread = whisper === void 0 ? {} : { whisper };
|
|
869
|
+
if (runtime.activeTools.size > 0) return {
|
|
870
|
+
input: {
|
|
871
|
+
phase: "tool",
|
|
872
|
+
line: runtime.voice.toolRemaining(runtime.activeTools.size, nowMs)
|
|
873
|
+
},
|
|
874
|
+
...whisperSpread
|
|
875
|
+
};
|
|
876
|
+
return runtime.stepHadFailure ? {
|
|
877
|
+
input: {
|
|
878
|
+
phase: "failed",
|
|
879
|
+
line: runtime.voice.scene("toolFailed", nowMs)
|
|
880
|
+
},
|
|
881
|
+
...whisperSpread
|
|
882
|
+
} : {
|
|
883
|
+
input: {
|
|
884
|
+
phase: "thinking",
|
|
885
|
+
line: runtime.voice.scene("toolResult", nowMs)
|
|
886
|
+
},
|
|
887
|
+
...whisperSpread
|
|
888
|
+
};
|
|
1021
889
|
}
|
|
1022
890
|
case "turn/end":
|
|
1023
891
|
runtime.activeTools.clear();
|
|
892
|
+
runtime.testCalls.clear();
|
|
1024
893
|
switch (event.data.reason.kind) {
|
|
1025
|
-
case "completed":
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
}
|
|
894
|
+
case "completed": {
|
|
895
|
+
const whisper = runtime.whispers.result("done", nowMs);
|
|
896
|
+
return {
|
|
897
|
+
input: {
|
|
898
|
+
phase: "done",
|
|
899
|
+
line: runtime.voice.scene("done", nowMs)
|
|
900
|
+
},
|
|
901
|
+
completedTurn: event.data.turn,
|
|
902
|
+
...whisper === void 0 ? {} : { whisper }
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
case "error": {
|
|
906
|
+
const whisper = runtime.whispers.result("fail", nowMs);
|
|
907
|
+
return {
|
|
908
|
+
input: {
|
|
909
|
+
phase: "failed",
|
|
910
|
+
line: runtime.voice.scene("failed", nowMs)
|
|
911
|
+
},
|
|
912
|
+
...whisper === void 0 ? {} : { whisper }
|
|
913
|
+
};
|
|
914
|
+
}
|
|
1036
915
|
case "max-tokens": return { input: {
|
|
1037
916
|
phase: "failed",
|
|
1038
917
|
line: runtime.voice.scene("maxTokens", nowMs)
|
|
@@ -1158,7 +1037,7 @@ var PetLedger = class {
|
|
|
1158
1037
|
...defaultTreatConfig,
|
|
1159
1038
|
...config.treats ?? {}
|
|
1160
1039
|
};
|
|
1161
|
-
this.picker = new RemarkPicker(config.remarks);
|
|
1040
|
+
this.picker = new RemarkPicker(config.remarks, config.voiceRemarks);
|
|
1162
1041
|
this.current = persist;
|
|
1163
1042
|
}
|
|
1164
1043
|
/** Affinity cooldown/rank tuning (read-only). */
|
|
@@ -1227,10 +1106,10 @@ var PetLedger = class {
|
|
|
1227
1106
|
}
|
|
1228
1107
|
/**
|
|
1229
1108
|
* Swap the reaction pools to another pet's custom remarks (called on pet
|
|
1230
|
-
* selection). Slots the pet does not declare fall back to built-ins.
|
|
1109
|
+
* selection). Slots the pet does not declare fall back to voice packs or built-ins.
|
|
1231
1110
|
*/
|
|
1232
|
-
setRemarks(remarks) {
|
|
1233
|
-
this.picker = new RemarkPicker(remarks);
|
|
1111
|
+
setRemarks(remarks, voiceRemarks) {
|
|
1112
|
+
this.picker = new RemarkPicker(remarks, voiceRemarks);
|
|
1234
1113
|
}
|
|
1235
1114
|
/**
|
|
1236
1115
|
* Settle the treat economy (work + time output since the last settlement).
|
|
@@ -1622,45 +1501,41 @@ function normalizePool(raw, kind, onWarning = () => {}) {
|
|
|
1622
1501
|
}
|
|
1623
1502
|
return pool;
|
|
1624
1503
|
}
|
|
1625
|
-
/** Normalize
|
|
1626
|
-
function
|
|
1504
|
+
/** Normalize whisper category pools; a key replaces that category's built-in pool (an explicit empty pool mutes it). */
|
|
1505
|
+
function normalizeWhisperCategories(raw, onWarning = () => {}) {
|
|
1627
1506
|
if (raw === void 0) return void 0;
|
|
1628
|
-
if (!
|
|
1629
|
-
onWarning("whispers.
|
|
1507
|
+
if (!isRecord$3(raw)) {
|
|
1508
|
+
onWarning("whispers.categories must be an object");
|
|
1630
1509
|
return;
|
|
1631
1510
|
}
|
|
1632
|
-
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
onWarning("whisper rule must be an object");
|
|
1511
|
+
const pools = {};
|
|
1512
|
+
for (const key of Object.keys(raw)) {
|
|
1513
|
+
if (!WHISPER_CATEGORIES.includes(key)) {
|
|
1514
|
+
onWarning("unknown whisper category " + key + " ignored");
|
|
1637
1515
|
continue;
|
|
1638
1516
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
onWarning("whisper rule dropped (needs keywords and a non-empty pool)");
|
|
1517
|
+
const pool = normalizePool(raw[key], "whisperCategory", onWarning);
|
|
1518
|
+
if (pool !== void 0) pools[key] = pool;
|
|
1519
|
+
}
|
|
1520
|
+
return Object.keys(pools).length > 0 ? pools : void 0;
|
|
1521
|
+
}
|
|
1522
|
+
/** Normalize whisper outcome pools; a key replaces that outcome's built-in pool (an explicit empty pool mutes it). */
|
|
1523
|
+
function normalizeWhisperResults(raw, onWarning = () => {}) {
|
|
1524
|
+
if (raw === void 0) return void 0;
|
|
1525
|
+
if (!isRecord$3(raw)) {
|
|
1526
|
+
onWarning("whispers.results must be an object");
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
const pools = {};
|
|
1530
|
+
for (const key of Object.keys(raw)) {
|
|
1531
|
+
if (!WHISPER_RESULTS.includes(key)) {
|
|
1532
|
+
onWarning("unknown whisper result " + key + " ignored");
|
|
1656
1533
|
continue;
|
|
1657
1534
|
}
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
pool
|
|
1661
|
-
});
|
|
1535
|
+
const pool = normalizePool(raw[key], "whisperResult", onWarning);
|
|
1536
|
+
if (pool !== void 0) pools[key] = pool;
|
|
1662
1537
|
}
|
|
1663
|
-
return
|
|
1538
|
+
return Object.keys(pools).length > 0 ? pools : void 0;
|
|
1664
1539
|
}
|
|
1665
1540
|
/** Normalize the panel block (labels / stats / actions; warn-and-drop). */
|
|
1666
1541
|
function normalizePanel(raw, onWarning = () => {}) {
|
|
@@ -1725,10 +1600,12 @@ const VOICE_PACK_KEYS = /* @__PURE__ */ new Set([
|
|
|
1725
1600
|
"tools",
|
|
1726
1601
|
"toolRemaining",
|
|
1727
1602
|
"whispers",
|
|
1728
|
-
"panel"
|
|
1603
|
+
"panel",
|
|
1604
|
+
"remarks",
|
|
1605
|
+
"ranks"
|
|
1729
1606
|
]);
|
|
1730
1607
|
/** Allowed whisper-section fields (drift-locked in tests). */
|
|
1731
|
-
const WHISPER_KEYS = /* @__PURE__ */ new Set(["
|
|
1608
|
+
const WHISPER_KEYS = /* @__PURE__ */ new Set(["categories", "results"]);
|
|
1732
1609
|
/**
|
|
1733
1610
|
* Normalize one raw voice.json document into a VoicePack, or undefined when
|
|
1734
1611
|
* the file cannot serve as a pack at all (non-object root — structure is
|
|
@@ -1778,19 +1655,32 @@ function normalizeVoicePack(raw, onWarning = () => {}) {
|
|
|
1778
1655
|
const whispersRaw = raw.whispers;
|
|
1779
1656
|
if (whispersRaw !== void 0) if (!isRecord$3(whispersRaw)) onWarning("whispers must be an object");
|
|
1780
1657
|
else {
|
|
1781
|
-
for (const key of Object.keys(whispersRaw)) if (
|
|
1782
|
-
|
|
1783
|
-
const
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
...
|
|
1658
|
+
for (const key of Object.keys(whispersRaw)) if (key === "generic" || key === "rules") onWarning("whispers." + key + " is no longer supported and was ignored");
|
|
1659
|
+
else if (!WHISPER_KEYS.has(key)) onWarning("unknown whispers field " + key + " ignored");
|
|
1660
|
+
const categories = normalizeWhisperCategories(whispersRaw.categories, onWarning);
|
|
1661
|
+
const results = normalizeWhisperResults(whispersRaw.results, onWarning);
|
|
1662
|
+
if (categories !== void 0 || results !== void 0) overrides.whispers = {
|
|
1663
|
+
...categories === void 0 ? {} : { categories },
|
|
1664
|
+
...results === void 0 ? {} : { results }
|
|
1787
1665
|
};
|
|
1788
1666
|
}
|
|
1789
1667
|
const panel = raw.panel === void 0 ? void 0 : normalizePanel(raw.panel, onWarning);
|
|
1790
|
-
|
|
1668
|
+
const remarks = raw.remarks === void 0 ? void 0 : normalizePetRemarks(raw.remarks, onWarning);
|
|
1669
|
+
const ranksRaw = raw.ranks;
|
|
1670
|
+
let ranks;
|
|
1671
|
+
if (ranksRaw !== void 0) if (!isRecord$3(ranksRaw)) onWarning("ranks must be an object");
|
|
1672
|
+
else {
|
|
1673
|
+
const out = {};
|
|
1674
|
+
for (const [key, value] of Object.entries(ranksRaw)) if (typeof value === "string" && value.trim() !== "") out[key] = value.trim().slice(0, 80);
|
|
1675
|
+
else onWarning("invalid rank name for " + key);
|
|
1676
|
+
if (Object.keys(out).length > 0) ranks = out;
|
|
1677
|
+
}
|
|
1678
|
+
if (overrides.status === void 0 && overrides.tools === void 0 && overrides.toolRemaining === void 0 && overrides.whispers === void 0 && panel === void 0 && remarks === void 0 && ranks === void 0) return;
|
|
1791
1679
|
return {
|
|
1792
1680
|
overrides,
|
|
1793
|
-
...panel === void 0 ? {} : { panel }
|
|
1681
|
+
...panel === void 0 ? {} : { panel },
|
|
1682
|
+
...remarks === void 0 ? {} : { remarks },
|
|
1683
|
+
...ranks === void 0 ? {} : { ranks }
|
|
1794
1684
|
};
|
|
1795
1685
|
}
|
|
1796
1686
|
/**
|
|
@@ -1803,6 +1693,8 @@ function mergeVoicePacks(...layers) {
|
|
|
1803
1693
|
const overrides = {};
|
|
1804
1694
|
const labels = {};
|
|
1805
1695
|
const stats = {};
|
|
1696
|
+
let remarks;
|
|
1697
|
+
let ranks;
|
|
1806
1698
|
let actions;
|
|
1807
1699
|
let panelSeen = false;
|
|
1808
1700
|
let any = false;
|
|
@@ -1828,6 +1720,14 @@ function mergeVoicePacks(...layers) {
|
|
|
1828
1720
|
if (layer.panel.stats !== void 0) Object.assign(stats, layer.panel.stats);
|
|
1829
1721
|
if (layer.panel.actions !== void 0) actions = layer.panel.actions;
|
|
1830
1722
|
}
|
|
1723
|
+
if (layer.remarks !== void 0) remarks = {
|
|
1724
|
+
...remarks ?? {},
|
|
1725
|
+
...layer.remarks
|
|
1726
|
+
};
|
|
1727
|
+
if (layer.ranks !== void 0) ranks = {
|
|
1728
|
+
...ranks ?? {},
|
|
1729
|
+
...layer.ranks
|
|
1730
|
+
};
|
|
1831
1731
|
}
|
|
1832
1732
|
if (!any) return void 0;
|
|
1833
1733
|
const panel = {
|
|
@@ -1838,7 +1738,9 @@ function mergeVoicePacks(...layers) {
|
|
|
1838
1738
|
const panelEmpty = panel.labels === void 0 && panel.stats === void 0 && panel.actions === void 0;
|
|
1839
1739
|
return {
|
|
1840
1740
|
overrides,
|
|
1841
|
-
...panelSeen && !panelEmpty ? { panel } : {}
|
|
1741
|
+
...panelSeen && !panelEmpty ? { panel } : {},
|
|
1742
|
+
...remarks !== void 0 ? { remarks } : {},
|
|
1743
|
+
...ranks !== void 0 ? { ranks } : {}
|
|
1842
1744
|
};
|
|
1843
1745
|
}
|
|
1844
1746
|
//#endregion
|
|
@@ -4223,10 +4125,12 @@ var PetService = class extends Service {
|
|
|
4223
4125
|
petId: this.registry.defaultEntry().id
|
|
4224
4126
|
};
|
|
4225
4127
|
const selected = this.registry.byId(persist.petId) ?? this.registry.defaultEntry();
|
|
4128
|
+
const voiceRemarks = mergeVoicePacks(this.registry.globalVoice, selected.voice)?.remarks;
|
|
4226
4129
|
const ledgerConfig = {
|
|
4227
4130
|
affinity: config.affinity,
|
|
4228
4131
|
treats: config.treats,
|
|
4229
|
-
remarks: selected.remarks
|
|
4132
|
+
remarks: selected.remarks,
|
|
4133
|
+
voiceRemarks
|
|
4230
4134
|
};
|
|
4231
4135
|
this.ledger = new PetLedger(persist, ledgerConfig);
|
|
4232
4136
|
this.stateConfig = {
|
|
@@ -4260,8 +4164,8 @@ var PetService = class extends Service {
|
|
|
4260
4164
|
return this.enabled;
|
|
4261
4165
|
}
|
|
4262
4166
|
/** RPC: current pet state snapshot. */
|
|
4263
|
-
async state() {
|
|
4264
|
-
return this.view();
|
|
4167
|
+
async state(currentSessionId) {
|
|
4168
|
+
return this.view(currentSessionId);
|
|
4265
4169
|
}
|
|
4266
4170
|
/** Current persisted display config (read-only view). */
|
|
4267
4171
|
display() {
|
|
@@ -4310,7 +4214,8 @@ var PetService = class extends Service {
|
|
|
4310
4214
|
error: "unknown-pet"
|
|
4311
4215
|
};
|
|
4312
4216
|
this.ledger.setPetId(entry.id);
|
|
4313
|
-
this.
|
|
4217
|
+
const voiceRemarks = mergeVoicePacks(this.registry.globalVoice, entry.voice)?.remarks;
|
|
4218
|
+
this.ledger.setRemarks(entry.remarks, voiceRemarks);
|
|
4314
4219
|
this.flush();
|
|
4315
4220
|
this.syncSettingsFromPet();
|
|
4316
4221
|
return {
|
|
@@ -4710,7 +4615,7 @@ var PetService = class extends Service {
|
|
|
4710
4615
|
rewardLegacyTurn() {
|
|
4711
4616
|
if (this.ledger.rewardLegacyTurn(Date.now())) this.flush();
|
|
4712
4617
|
}
|
|
4713
|
-
view() {
|
|
4618
|
+
view(currentSessionId) {
|
|
4714
4619
|
const snapshot = this.machine.render();
|
|
4715
4620
|
const entry = this.activeEntry();
|
|
4716
4621
|
const sessions = [];
|
|
@@ -4719,15 +4624,20 @@ var PetService = class extends Service {
|
|
|
4719
4624
|
if (session.header?.origin === "subagent") continue;
|
|
4720
4625
|
const perSession = activity.machine.render();
|
|
4721
4626
|
if (perSession.bubble === void 0) continue;
|
|
4627
|
+
const whisper = activity.whisper;
|
|
4628
|
+
const freshWhisper = whisper !== void 0 && Date.now() - whisper.at < 8e3 ? whisper.text : void 0;
|
|
4722
4629
|
sessions.push({
|
|
4723
4630
|
sessionId: String(session.id),
|
|
4724
4631
|
animation: perSession.animation,
|
|
4725
4632
|
bubble: perSession.bubble,
|
|
4726
|
-
phase: perSession.phase
|
|
4633
|
+
phase: perSession.phase,
|
|
4634
|
+
...freshWhisper === void 0 ? {} : { whisper: freshWhisper }
|
|
4727
4635
|
});
|
|
4728
4636
|
}
|
|
4729
|
-
|
|
4730
|
-
|
|
4637
|
+
if (currentSessionId !== void 0) {
|
|
4638
|
+
const index = sessions.findIndex((session) => session.sessionId === currentSessionId);
|
|
4639
|
+
if (index > 0) sessions.unshift(sessions.splice(index, 1)[0]);
|
|
4640
|
+
}
|
|
4731
4641
|
const decoration = this.activeDecoration();
|
|
4732
4642
|
const gameplayDef = this.gameplayDef();
|
|
4733
4643
|
let gameplay;
|
|
@@ -4742,7 +4652,6 @@ var PetService = class extends Service {
|
|
|
4742
4652
|
phase: snapshot.phase,
|
|
4743
4653
|
sessionActive: snapshot.sessionActive,
|
|
4744
4654
|
sessions,
|
|
4745
|
-
...freshWhisper === void 0 ? {} : { whisper: freshWhisper },
|
|
4746
4655
|
...decoration === void 0 ? {} : { decoration },
|
|
4747
4656
|
affinity: this.ledger.affinityView(Date.now()),
|
|
4748
4657
|
display: { ...this.ledger.snapshot.display },
|
|
@@ -4811,23 +4720,33 @@ function isLoopbackRequest(request) {
|
|
|
4811
4720
|
}
|
|
4812
4721
|
}
|
|
4813
4722
|
//#endregion
|
|
4814
|
-
//#region src/access.ts
|
|
4723
|
+
//#region src/pair-access.ts
|
|
4815
4724
|
/**
|
|
4816
|
-
* Whether this request may enter
|
|
4725
|
+
* Whether this request may enter the plugin's host routes.
|
|
4817
4726
|
* @param ctx - host context; may expose remoteWebUiPairing.
|
|
4818
4727
|
* @param request - the incoming HTTP request.
|
|
4819
4728
|
* @returns true for loopback, or a live paired-device cookie.
|
|
4820
4729
|
*/
|
|
4821
|
-
function
|
|
4730
|
+
function isPairedOrLoopbackAllowed(ctx, request) {
|
|
4822
4731
|
if (isLoopbackRequest(request)) return true;
|
|
4823
|
-
const
|
|
4824
|
-
|
|
4825
|
-
return (isPairingAccess(fromGet) ? fromGet : bag.remoteWebUiPairing)?.isPairedDevice(request) === true;
|
|
4732
|
+
const fromGet = typeof ctx.get === "function" ? ctx.get("remoteWebUiPairing", false) : void 0;
|
|
4733
|
+
return (isPairingAccess(fromGet) ? fromGet : ctx.remoteWebUiPairing)?.isPairedDevice(request) === true;
|
|
4826
4734
|
}
|
|
4827
4735
|
function isPairingAccess(value) {
|
|
4828
4736
|
return value !== void 0 && value !== null && typeof value.isPairedDevice === "function";
|
|
4829
4737
|
}
|
|
4830
4738
|
//#endregion
|
|
4739
|
+
//#region src/access.ts
|
|
4740
|
+
/**
|
|
4741
|
+
* Whether this request may enter any /api/pet or /pet asset route.
|
|
4742
|
+
* @param ctx - host context; may expose remoteWebUiPairing.
|
|
4743
|
+
* @param request - the incoming HTTP request.
|
|
4744
|
+
* @returns true for loopback, or a live paired-device cookie.
|
|
4745
|
+
*/
|
|
4746
|
+
function isPetAllowed(ctx, request) {
|
|
4747
|
+
return isPairedOrLoopbackAllowed(ctx, request);
|
|
4748
|
+
}
|
|
4749
|
+
//#endregion
|
|
4831
4750
|
//#region src/http.ts
|
|
4832
4751
|
/** Default body cap for readJsonBody: 64 KiB. */
|
|
4833
4752
|
const DEFAULT_JSON_BODY_MAX_BYTES = 64 * 1024;
|
|
@@ -4978,7 +4897,7 @@ function guard(ctx, req, res) {
|
|
|
4978
4897
|
});
|
|
4979
4898
|
return false;
|
|
4980
4899
|
}
|
|
4981
|
-
/** Wrap one async service call as a GET JSON route. */
|
|
4900
|
+
/** Wrap one async service call as a GET JSON route (request passed through for query params). */
|
|
4982
4901
|
function getRoute(ctx, path, run) {
|
|
4983
4902
|
return {
|
|
4984
4903
|
kind: "exact",
|
|
@@ -4986,7 +4905,7 @@ function getRoute(ctx, path, run) {
|
|
|
4986
4905
|
handler: (req, res) => {
|
|
4987
4906
|
if (!guard(ctx, req, res)) return;
|
|
4988
4907
|
if (!requireMethod(req, res, "GET")) return;
|
|
4989
|
-
run().then((value) => writeJson(res, 200, value), (error) => {
|
|
4908
|
+
run(req).then((value) => writeJson(res, 200, value), (error) => {
|
|
4990
4909
|
writeJson(res, 500, {
|
|
4991
4910
|
ok: false,
|
|
4992
4911
|
error: error instanceof Error ? error.message : String(error)
|
|
@@ -5368,7 +5287,10 @@ function decorationHandler(ctx, registry, caps) {
|
|
|
5368
5287
|
function makePetRoutes(deps) {
|
|
5369
5288
|
const { service, ctx } = deps;
|
|
5370
5289
|
const apiRoutes = [
|
|
5371
|
-
getRoute(ctx, "/api/pet/state", () =>
|
|
5290
|
+
getRoute(ctx, "/api/pet/state", (req) => {
|
|
5291
|
+
const current = new URL(req.url ?? "/", "http://pet.local").searchParams.get("current");
|
|
5292
|
+
return service.state(current === null || current === "" ? void 0 : current);
|
|
5293
|
+
}),
|
|
5372
5294
|
getRoute(ctx, "/api/pet/pets", () => service.pets()),
|
|
5373
5295
|
getRoute(ctx, "/api/pet/diagnostics", () => service.diagnostics()),
|
|
5374
5296
|
postRoute(ctx, "/api/pet/interact", (body) => {
|