@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/types/chatter.js
CHANGED
|
@@ -7,10 +7,12 @@
|
|
|
7
7
|
* the working-activity plugin's status line. Lines rotate round-robin —
|
|
8
8
|
* while a phase persists the copy advances every few seconds, so the pet
|
|
9
9
|
* feels alive without flickering per streamed chunk.
|
|
10
|
-
* 2. The murmur engine (碎碎念): the pet's inner whispers
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* 2. The murmur engine (碎碎念): the pet's inner whispers — category
|
|
11
|
+
* lines woken by the SITUATION (thinking / writing / the running tool
|
|
12
|
+
* family), plus outcome lines woken only by structured session results
|
|
13
|
+
* (test green, tool errors, turn completion). The model's own prose is
|
|
14
|
+
* never read, and no whisper ever quotes real content. Cooldowns keep
|
|
15
|
+
* whispers occasional.
|
|
14
16
|
*
|
|
15
17
|
* Pure and deterministic: round-robin everywhere (no Math.random), clocks are
|
|
16
18
|
* injected. The first line of each status pool is the legacy fixed copy the
|
|
@@ -487,329 +489,269 @@ export class StatusVoice {
|
|
|
487
489
|
.replaceAll('{n}', String(count));
|
|
488
490
|
}
|
|
489
491
|
}
|
|
490
|
-
/**
|
|
492
|
+
/** Every whisper category key, in declaration order (voice-pack key allow-list). */
|
|
493
|
+
export const WHISPER_CATEGORIES = [
|
|
494
|
+
'thinking', 'writing', 'reading', 'editing', 'running', 'searching',
|
|
495
|
+
'git', 'delegating', 'browsing', 'generic',
|
|
496
|
+
];
|
|
497
|
+
/** Every whisper outcome key, in declaration order (voice-pack key allow-list). */
|
|
498
|
+
export const WHISPER_RESULTS = ['pass', 'fail', 'done'];
|
|
499
|
+
/** Murmur pacing: the cooldown between category whispers. */
|
|
491
500
|
export const WHISPER_COOLDOWN_MS = 9000;
|
|
492
|
-
|
|
501
|
+
/** Outcome whispers get their own shorter cooldown so a real moment still speaks. */
|
|
502
|
+
export const WHISPER_RESULT_COOLDOWN_MS = 5000;
|
|
493
503
|
/** How long a whisper stays on screen (host-side expiry). */
|
|
494
504
|
export const WHISPER_TTL_MS = 8000;
|
|
495
|
-
/**
|
|
496
|
-
export
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
'能帮上忙就好~',
|
|
673
|
-
'你的谢意,我收进口袋啦',
|
|
674
|
-
'这话我爱听',
|
|
675
|
-
'客气啥,应该的',
|
|
676
|
-
'收下这份心意,干劲+1',
|
|
677
|
-
'你谢我,我谢你,扯平啦',
|
|
678
|
-
],
|
|
679
|
-
},
|
|
680
|
-
{
|
|
681
|
-
keywords: ['复杂', '棘手', '困难', '难点', '坑', '头疼', 'tricky', 'complex'],
|
|
682
|
-
pool: [
|
|
683
|
-
'难不倒我们俩的',
|
|
684
|
-
'越难啃的骨头越香',
|
|
685
|
-
'硬骨头?我最喜欢了',
|
|
686
|
-
'复杂问题拆开看,小事',
|
|
687
|
-
'这坑我们一起填',
|
|
688
|
-
'有点东西,但不多',
|
|
689
|
-
'硬骨头,慢慢啃',
|
|
690
|
-
'问题越难,赢的时候越爽',
|
|
691
|
-
'绕是绕不过去的,正面刚',
|
|
692
|
-
'再难的题,拆开都是小问号',
|
|
693
|
-
],
|
|
694
|
-
},
|
|
695
|
-
{
|
|
696
|
-
keywords: ['检查', '审查', '确认一下', '核对', 'review', '仔细看看', '验证'],
|
|
697
|
-
pool: [
|
|
698
|
-
'火眼金睛,启动',
|
|
699
|
-
'让我仔细瞧瞧',
|
|
700
|
-
'细节魔鬼,一个都不放过',
|
|
701
|
-
'认真检查的样子最迷人',
|
|
702
|
-
'多核一遍,稳上加稳',
|
|
703
|
-
'再看一眼,不亏',
|
|
704
|
-
'确认键,点了才安心',
|
|
705
|
-
'细节控上线',
|
|
706
|
-
'查完这遍,稳了',
|
|
707
|
-
],
|
|
708
|
-
},
|
|
709
|
-
{
|
|
710
|
-
keywords: ['搜索', '查一下', '资料', '文档', '搜一搜', '找找', '查询'],
|
|
711
|
-
pool: [
|
|
712
|
-
'去知识的海洋里捞一捞',
|
|
713
|
-
'翻翻找找,线索快出来',
|
|
714
|
-
'检索小雷达启动',
|
|
715
|
-
'答案就藏在某个角落',
|
|
716
|
-
'线索有点散,拼一下',
|
|
717
|
-
'找东西,我最在行',
|
|
718
|
-
'答案在网线那头等我',
|
|
719
|
-
'翻箱倒柜中,稍等',
|
|
720
|
-
],
|
|
721
|
-
},
|
|
722
|
-
{
|
|
723
|
-
keywords: ['写代码', '实现', '编码', '函数', '接口', '重构'],
|
|
724
|
-
pool: [
|
|
725
|
-
'指尖跳舞,代码开花',
|
|
726
|
-
'把逻辑织成网',
|
|
727
|
-
'一行一行,垒起小城堡',
|
|
728
|
-
'这代码写得,我自己都佩服',
|
|
729
|
-
'码着码着,天就亮了',
|
|
730
|
-
'代码跑通了,比中奖还开心',
|
|
731
|
-
'这行代码,写得有点帅',
|
|
732
|
-
'写完再润润,讲究',
|
|
733
|
-
],
|
|
734
|
-
},
|
|
735
|
-
];
|
|
736
|
-
/** The built-in voice pack: the plugin's default copy, unchanged since v1. */
|
|
505
|
+
/** Map a tool family onto the whisper category it belongs to. */
|
|
506
|
+
export function whisperCategoryOf(tool) {
|
|
507
|
+
switch (tool) {
|
|
508
|
+
case 'read':
|
|
509
|
+
case 'grep':
|
|
510
|
+
case 'find':
|
|
511
|
+
case 'ls':
|
|
512
|
+
return 'reading';
|
|
513
|
+
case 'write':
|
|
514
|
+
case 'edit':
|
|
515
|
+
return 'editing';
|
|
516
|
+
case 'shell':
|
|
517
|
+
return 'running';
|
|
518
|
+
case 'webSearch':
|
|
519
|
+
case 'webFetch':
|
|
520
|
+
case 'memory':
|
|
521
|
+
case 'mcp':
|
|
522
|
+
return 'searching';
|
|
523
|
+
case 'git':
|
|
524
|
+
return 'git';
|
|
525
|
+
case 'subagent':
|
|
526
|
+
case 'todo':
|
|
527
|
+
return 'delegating';
|
|
528
|
+
case 'browser':
|
|
529
|
+
return 'browsing';
|
|
530
|
+
case 'ask':
|
|
531
|
+
case 'generic':
|
|
532
|
+
return 'generic';
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Whether a tool invocation looks like a test run. The whisper engine never
|
|
537
|
+
* reads the model's prose (a discussion that merely mentions a keyword must
|
|
538
|
+
* not wake a mood); a test-outcome mood is wanted only when a test tool
|
|
539
|
+
* actually ran, so the projection marks the call at tool/call time and the
|
|
540
|
+
* pass mood fires from the paired tool/result.
|
|
541
|
+
*/
|
|
542
|
+
export function looksLikeTestTool(name, argumentsText) {
|
|
543
|
+
const tool = name.toLowerCase();
|
|
544
|
+
if (/(^|[\/_.-])(test|tests|spec|vitest|jest|pytest|mocha|playwright|cypress|karma)([\/_.-]|$)/.test(tool)) {
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
if (argumentsText === undefined)
|
|
548
|
+
return false;
|
|
549
|
+
// Prefer the real command / code fields when the arguments parse; the raw
|
|
550
|
+
// JSON fallback still catches model-produced shapes the parser misses.
|
|
551
|
+
let haystack = argumentsText.toLowerCase();
|
|
552
|
+
try {
|
|
553
|
+
const parsed = JSON.parse(argumentsText);
|
|
554
|
+
if (typeof parsed === 'object' && parsed !== null) {
|
|
555
|
+
const record = parsed;
|
|
556
|
+
const command = record.command;
|
|
557
|
+
const code = record.code;
|
|
558
|
+
const picked = typeof command === 'string' && command !== ''
|
|
559
|
+
? command
|
|
560
|
+
: typeof code === 'string' && code !== ''
|
|
561
|
+
? code
|
|
562
|
+
: undefined;
|
|
563
|
+
if (picked !== undefined)
|
|
564
|
+
haystack = picked.toLowerCase();
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
// Keep the raw text below.
|
|
569
|
+
}
|
|
570
|
+
return /\b(pnpm|npm|yarn|npx|bun|python)\s+(run\s+)?(test|tests?)\b/.test(haystack)
|
|
571
|
+
|| /\b(pytest|vitest|jest|mocha|cypress|playwright|go test|cargo test)\b/.test(haystack);
|
|
572
|
+
}
|
|
573
|
+
/** Category-level inner-whisper pools — the pet knows roughly what is going on. */
|
|
574
|
+
export const WHISPER_CATEGORY_POOLS = {
|
|
575
|
+
thinking: [
|
|
576
|
+
'先在脑子里搭个框架',
|
|
577
|
+
'它在心里打草稿,我垫着脚看',
|
|
578
|
+
'思路在一颗一颗冒泡',
|
|
579
|
+
'脑内开会中,都别抢话筒',
|
|
580
|
+
'先想清楚,再动手不迟',
|
|
581
|
+
'草稿纸已经画满了',
|
|
582
|
+
'让我听听它下一步打算',
|
|
583
|
+
'嗯,方案在成型了',
|
|
584
|
+
],
|
|
585
|
+
writing: [
|
|
586
|
+
'落笔成文,我旁边听着',
|
|
587
|
+
'句子排着队往外走',
|
|
588
|
+
'把想法一句句摆整齐',
|
|
589
|
+
'它在组织语言,我打打气',
|
|
590
|
+
'写回复呢,不催',
|
|
591
|
+
'字斟句酌,快好了',
|
|
592
|
+
],
|
|
593
|
+
reading: [
|
|
594
|
+
'翻资料呢,我保持安静',
|
|
595
|
+
'一行一行读,不跳页',
|
|
596
|
+
'在纸堆里找线索',
|
|
597
|
+
'眼珠子跟着字跑',
|
|
598
|
+
'边读边做记号',
|
|
599
|
+
'翻箱倒柜找重点',
|
|
600
|
+
],
|
|
601
|
+
editing: [
|
|
602
|
+
'动手改起来了,手稳一点',
|
|
603
|
+
'这里补一笔,那边修一修',
|
|
604
|
+
'在改东西,听不到声音才怪',
|
|
605
|
+
'落笔小心,别有错别字',
|
|
606
|
+
'改写的节奏,我听得见',
|
|
607
|
+
'刷刷地改,一行都没跑',
|
|
608
|
+
],
|
|
609
|
+
running: [
|
|
610
|
+
'跑起来了跑起来了',
|
|
611
|
+
'命令敲出去,等个回响',
|
|
612
|
+
'在跑什么呢,我踮脚看',
|
|
613
|
+
'输出开始冒烟了',
|
|
614
|
+
'它在跑活,我不吵',
|
|
615
|
+
'盯着输出,蹲一个结果',
|
|
616
|
+
'这波跑完就靠它了',
|
|
617
|
+
],
|
|
618
|
+
searching: [
|
|
619
|
+
'去外面捞点信息',
|
|
620
|
+
'翻翻记忆库,等我一小会儿',
|
|
621
|
+
'顺着网线找线索',
|
|
622
|
+
'把老账翻出来对一对',
|
|
623
|
+
'情报在路上了',
|
|
624
|
+
'搜索引擎当跑腿',
|
|
625
|
+
],
|
|
626
|
+
git: [
|
|
627
|
+
'版本在往前迈步',
|
|
628
|
+
'改动排队上车',
|
|
629
|
+
'提交历史在长个子',
|
|
630
|
+
'分支合并,神清气爽',
|
|
631
|
+
'记录都焊在时间线上',
|
|
632
|
+
],
|
|
633
|
+
delegating: [
|
|
634
|
+
'派了活儿出去,等回话',
|
|
635
|
+
'清单列好,一件件来',
|
|
636
|
+
'任务拆开分了组',
|
|
637
|
+
'手下的伙计在远处跑着',
|
|
638
|
+
'分工完毕,各司其职',
|
|
639
|
+
],
|
|
640
|
+
browsing: [
|
|
641
|
+
'它在看网页,我偷瞄两眼',
|
|
642
|
+
'页面一张张翻过去',
|
|
643
|
+
'网页里翻答案呢',
|
|
644
|
+
'这网速,我先歇会儿',
|
|
645
|
+
],
|
|
646
|
+
generic: [
|
|
647
|
+
'这波活儿,我陪着',
|
|
648
|
+
'又开工了,我盯梢',
|
|
649
|
+
'它忙它的,我守着',
|
|
650
|
+
'不打扰,就安静待着',
|
|
651
|
+
'有活儿就有我',
|
|
652
|
+
],
|
|
653
|
+
};
|
|
654
|
+
/** The pet's outcome reactions — woken by structured session results only. */
|
|
655
|
+
export const WHISPER_RESULT_POOLS = {
|
|
656
|
+
pass: [
|
|
657
|
+
'全绿!亮瞎我眼了',
|
|
658
|
+
'测试过了,击掌~',
|
|
659
|
+
'绿灯一排排,看着就舒坦',
|
|
660
|
+
'稳了稳了,这波稳得很',
|
|
661
|
+
'全绿,奖励自己一口小鱼干',
|
|
662
|
+
'这波测试,赢得干脆',
|
|
663
|
+
],
|
|
664
|
+
fail: [
|
|
665
|
+
'哎呀,踩到小石子了',
|
|
666
|
+
'这报错我盯上它了',
|
|
667
|
+
'别慌,先看它在喊什么',
|
|
668
|
+
'修好它,今天才不算白干',
|
|
669
|
+
'又一次踩坑,老熟人了',
|
|
670
|
+
'问题不大,就是有点问题',
|
|
671
|
+
],
|
|
672
|
+
done: [
|
|
673
|
+
'搞定,收工~',
|
|
674
|
+
'又翻过一页,踏实',
|
|
675
|
+
'努力没白费,开心',
|
|
676
|
+
'任务清零,舒服',
|
|
677
|
+
'攻下一城,转个圈',
|
|
678
|
+
'收工收工,今天圆满',
|
|
679
|
+
],
|
|
680
|
+
};
|
|
681
|
+
/** The built-in voice pack: the plugin's default copy. */
|
|
737
682
|
export const BUILTIN_VOICE_PACK = {
|
|
738
683
|
status: STATUS_POOLS,
|
|
739
684
|
tools: TOOL_POOLS,
|
|
740
685
|
toolRemaining: TOOL_REMAINING_POOL,
|
|
741
|
-
whispers: {
|
|
686
|
+
whispers: { categories: WHISPER_CATEGORY_POOLS, results: WHISPER_RESULT_POOLS },
|
|
742
687
|
};
|
|
743
688
|
/**
|
|
744
|
-
* The murmur engine (碎碎念):
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
*
|
|
749
|
-
*
|
|
750
|
-
*
|
|
689
|
+
* The murmur engine (碎碎念): the pet's inner voice while sessions work.
|
|
690
|
+
* Category awareness: the projection feeds the current situation (thinking /
|
|
691
|
+
* writing / the running tool family), and the engine answers with a line from
|
|
692
|
+
* that category's pool — so a whisper always roughly knows what is going on
|
|
693
|
+
* without ever quoting real content (no tool names, no paths, no model text).
|
|
694
|
+
* Outcome moments (test green, tool errors, turn completion) fire from the
|
|
695
|
+
* structured result events, never from output text, so a mood cannot mis-
|
|
696
|
+
* fire on a discussion that merely mentions a keyword. A cooldown keeps
|
|
697
|
+
* whispers occasional; all picks are round-robin so tests reproduce exact
|
|
698
|
+
* lines. The voice-pack provider (pet-center M4) swaps the pools at draw
|
|
699
|
+
* time, so a pet switch re-voices live engines in place.
|
|
751
700
|
*/
|
|
752
701
|
export class WhisperEngine {
|
|
753
702
|
pools;
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
703
|
+
categoryCooldownMs;
|
|
704
|
+
resultCooldownMs;
|
|
705
|
+
categoryCursor = new Map();
|
|
706
|
+
resultCursor = new Map();
|
|
758
707
|
lastWhisperAt = Number.NEGATIVE_INFINITY;
|
|
759
|
-
|
|
760
|
-
constructor(pools = () => BUILTIN_VOICE_PACK, cooldownMs = WHISPER_COOLDOWN_MS, charBudget = WHISPER_CHAR_BUDGET) {
|
|
708
|
+
constructor(pools = () => BUILTIN_VOICE_PACK, categoryCooldownMs = WHISPER_COOLDOWN_MS, resultCooldownMs = WHISPER_RESULT_COOLDOWN_MS) {
|
|
761
709
|
this.pools = pools;
|
|
762
|
-
this.
|
|
763
|
-
this.
|
|
710
|
+
this.categoryCooldownMs = categoryCooldownMs;
|
|
711
|
+
this.resultCooldownMs = resultCooldownMs;
|
|
764
712
|
}
|
|
765
|
-
/**
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
rules() {
|
|
770
|
-
const override = this.pools().whispers?.rules;
|
|
771
|
-
return override === undefined ? WHISPER_RULES : override;
|
|
713
|
+
/** Effective category pool (an explicit empty override mutes the category). */
|
|
714
|
+
categoryPool(category) {
|
|
715
|
+
const override = this.pools().whispers?.categories?.[category];
|
|
716
|
+
return override === undefined ? WHISPER_CATEGORY_POOLS[category] : override;
|
|
772
717
|
}
|
|
773
|
-
/** Effective
|
|
774
|
-
|
|
775
|
-
const override = this.pools().whispers?.
|
|
776
|
-
return override === undefined ?
|
|
718
|
+
/** Effective outcome pool (an explicit empty override mutes the outcome). */
|
|
719
|
+
resultPool(kind) {
|
|
720
|
+
const override = this.pools().whispers?.results?.[kind];
|
|
721
|
+
return override === undefined ? WHISPER_RESULT_POOLS[kind] : override;
|
|
777
722
|
}
|
|
778
723
|
/**
|
|
779
|
-
* Feed one
|
|
780
|
-
*
|
|
724
|
+
* Feed one situation while a session works. Returns the whisper to show,
|
|
725
|
+
* or undefined when the moment stays quiet (cooldown, or the category
|
|
726
|
+
* pool is muted).
|
|
781
727
|
*/
|
|
782
|
-
feed(
|
|
783
|
-
if (
|
|
728
|
+
feed(category, nowMs) {
|
|
729
|
+
if (nowMs - this.lastWhisperAt < this.categoryCooldownMs)
|
|
784
730
|
return undefined;
|
|
785
|
-
const
|
|
786
|
-
if (
|
|
787
|
-
this.charsSinceWhisper += text.length;
|
|
731
|
+
const pool = this.categoryPool(category);
|
|
732
|
+
if (pool.length === 0)
|
|
788
733
|
return undefined;
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
this.charsSinceWhisper += text.length;
|
|
801
|
-
if (this.charsSinceWhisper < this.charBudget)
|
|
734
|
+
const index = (this.categoryCursor.get(category) ?? 0) % pool.length;
|
|
735
|
+
this.categoryCursor.set(category, index + 1);
|
|
736
|
+
return this.speak(pool[index], nowMs);
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Feed one structured outcome (test green / tool failure / turn
|
|
740
|
+
* completion). Outcomes carry their own shorter cooldown so the emotional
|
|
741
|
+
* moment is heard unless another whisper just spoke.
|
|
742
|
+
*/
|
|
743
|
+
result(kind, nowMs) {
|
|
744
|
+
if (nowMs - this.lastWhisperAt < this.resultCooldownMs)
|
|
802
745
|
return undefined;
|
|
803
|
-
const
|
|
804
|
-
if (
|
|
746
|
+
const pool = this.resultPool(kind);
|
|
747
|
+
if (pool.length === 0)
|
|
805
748
|
return undefined;
|
|
806
|
-
const
|
|
807
|
-
this.
|
|
808
|
-
return this.speak(
|
|
749
|
+
const index = (this.resultCursor.get(kind) ?? 0) % pool.length;
|
|
750
|
+
this.resultCursor.set(kind, index + 1);
|
|
751
|
+
return this.speak(pool[index], nowMs);
|
|
809
752
|
}
|
|
810
753
|
speak(line, nowMs) {
|
|
811
754
|
this.lastWhisperAt = nowMs;
|
|
812
|
-
this.charsSinceWhisper = 0;
|
|
813
755
|
return line;
|
|
814
756
|
}
|
|
815
757
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAkE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,4DAA4D;IAC5D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,CAAA;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,qEAAqE;IACrE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAgGD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"PetSprite.d.ts","sourceRoot":"","sources":["../../../src/client/PetSprite.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAkE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAEnD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAIjD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAA;AAGjC,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,gDAAgD;IAChD,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,aAAa,CAAA;IACzB,qDAAqD;IACrD,OAAO,EAAE,gBAAgB,CAAA;IACzB,sCAAsC;IACtC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;IAC5B,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAA;IACjB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,sCAAsC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,+BAA+B;IAC/B,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,uDAAuD;IACvD,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,4DAA4D;IAC5D,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC1C,2DAA2D;IAC3D,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B;;;;OAIG;IACH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB;;;;OAIG;IACH,GAAG,CAAC,EAAE,SAAS,CAAA;IACf;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,qEAAqE;IACrE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,+CAA+C;IAC/C,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE,CAAC,CAAA;CAC1B;AAgGD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,WAAW,CA6iB5D"}
|