@thesashadev/girl-agent 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## 0.1.2 — communication realism update
4
+
5
+ Дата: 2026-05-05
6
+
7
+ - Hotfix: профили из wizard теперь сохраняются раньше, а список профилей больше не показывает недосохранённые папки без `config.json`.
8
+ - Добавлены жизненные стили общения: **Нормальная**, **Милая**, **Альтушка**, **Залипала**, **Болтушка**.
9
+ - Добавлен `CommunicationProfile` с настройками уведомлений, стиля сообщений, инициативы и life sharing.
10
+ - Presence, reply timing, bubbles, ignore chance и proactive agenda теперь учитывают профиль общения.
11
+ - Wizard и CLI получили настройку communication profile.
12
+ - Runtime `:status` и `:debug` показывают профиль общения.
13
+ - Команда `:log` стала удобнее и поддерживает выбор дня/лимита вывода.
14
+ - Старый `vibe` автоматически нормализуется в новый формат.
15
+
16
+ ## 0.1.1 — stability baseline
17
+
18
+ - Базовый публичный релиз с Telegram bot/userbot режимами.
19
+ - Persona, speech, relationship state, memory, conflict и agenda-модули.
20
+ - Документация по установке, конфигурации, реализм-модулям и troubleshooting.
package/README.md CHANGED
@@ -5,6 +5,11 @@
5
5
 
6
6
  **[website]**  ·  **[docs]**
7
7
 
8
+
9
+ Это только бета-версия. Со временем будет дорабатыватся.
10
+ Со всеми проблемами и багами пишите в Issues.
11
+ ТГ создателя - @voided_net
12
+
8
13
  ---
9
14
 
10
15
  ## Содержание
@@ -12,6 +17,7 @@
12
17
  - [Быстрый старт](#быстрый-старт)
13
18
  - [Что под капотом](#что-под-капотом)
14
19
  - [Почему не просто GPTs или промпт](#почему-не-просто-gpts-или-промпт)
20
+ - [Changelog](./CHANGELOG.md)
15
21
  - [Безопасность](#безопасность)
16
22
  - [Лицензия](#лицензия)
17
23
 
@@ -29,11 +35,10 @@
29
35
 
30
36
  ## Быстрый старт
31
37
 
38
+ **Через NPX (рекомендуется):**
39
+
32
40
  ```powershell
33
- git clone https://github.com/TheSashaDev/girl-agent.git
34
- cd girl-agent
35
- npm install
36
- npm run dev
41
+ npx @thesashadev/girl-agent
37
42
  ```
38
43
 
39
44
  Wizard задаст пару вопросов — имя, возраст, Telegram-подключение, LLM-ключ. Всё.
@@ -41,7 +46,16 @@ Wizard задаст пару вопросов — имя, возраст, Telegr
41
46
  Если профиль уже есть:
42
47
 
43
48
  ```powershell
44
- npm run dev -- --profile=arina
49
+ npx @thesashadev/girl-agent --profile=arina
50
+ ```
51
+
52
+ **Из исходников:**
53
+
54
+ ```powershell
55
+ git clone https://github.com/TheSashaDev/girl-agent.git
56
+ cd girl-agent
57
+ npm install
58
+ npm run dev
45
59
  ```
46
60
 
47
61
  ---
@@ -167,4 +181,4 @@ npm run dev -- --profile=arina
167
181
  - Публичные конкурирующие клоны
168
182
  - Использование кода внутри коммерческих продуктов
169
183
 
170
- 📜 Полный текст лицензии: [LICENSE](./LICENSE)
184
+ 📜 Полный текст лицензии: [LICENSE](./LICENSE)
package/dist/cli.js CHANGED
@@ -344,28 +344,32 @@ var LLM_PRESETS = [
344
344
  id: "openai",
345
345
  name: "OpenAI",
346
346
  proto: "openai",
347
- defaultModel: "gpt-4o-mini",
348
- models: ["gpt-4o", "gpt-4o-mini", "gpt-4.1", "gpt-4.1-mini", "o4-mini"]
347
+ defaultModel: "gpt-5.5",
348
+ models: ["gpt-5.5", "gpt-5.5-thinking", "gpt-5.5-pro", "gpt-5.4", "gpt-5.4-pro", "gpt-5.4-thinking", "gpt-5.3-chat-latest", "gpt-5.4-mini", "gpt-5.4-nano", "gpt-4o", "gpt-4o-mini", "gpt-4.1", "gpt-4.1-mini"]
349
349
  },
350
350
  {
351
351
  id: "anthropic",
352
352
  name: "Anthropic",
353
353
  proto: "anthropic",
354
- defaultModel: "claude-sonnet-4-5",
355
- models: ["claude-sonnet-4-5", "claude-opus-4-1", "claude-haiku-4-5"]
354
+ defaultModel: "claude-sonnet-4-6",
355
+ models: ["claude-opus-4-7", "claude-sonnet-4-6", "claude-haiku-4-5-20251001", "claude-opus-4-6", "claude-sonnet-4-5", "claude-opus-4-1"]
356
356
  },
357
357
  {
358
358
  id: "openrouter",
359
359
  name: "OpenRouter",
360
360
  proto: "openai",
361
361
  baseURL: "https://openrouter.ai/api/v1",
362
- defaultModel: "anthropic/claude-sonnet-4.5",
362
+ defaultModel: "openai/gpt-5.3-chat-latest",
363
363
  models: [
364
- "anthropic/claude-sonnet-4.5",
365
- "openai/gpt-4o",
366
- "google/gemini-2.5-flash",
367
- "deepseek/deepseek-chat",
368
- "x-ai/grok-4"
364
+ "openai/gpt-5.3-chat-latest",
365
+ "openai/gpt-5.5",
366
+ "openai/gpt-5.5-thinking",
367
+ "openai/gpt-5.5-pro",
368
+ "anthropic/claude-sonnet-4.6",
369
+ "anthropic/claude-opus-4.7",
370
+ "google/gemini-3.1-pro",
371
+ "deepseek/deepseek-v4-pro",
372
+ "x-ai/grok-4.3"
369
373
  ]
370
374
  },
371
375
  {
@@ -374,31 +378,32 @@ var LLM_PRESETS = [
374
378
  proto: "openai",
375
379
  baseURL: "https://api.groq.com/openai/v1",
376
380
  defaultModel: "llama-3.3-70b-versatile",
377
- models: ["llama-3.3-70b-versatile", "llama-3.1-8b-instant", "mixtral-8x7b-32768"]
381
+ models: ["llama-3.3-70b-versatile", "llama-3.1-8b-instant", "llama-4-scout-17b-16e-instruct", "qwen-3-32b", "mixtral-8x7b-32768"]
378
382
  },
379
383
  {
380
384
  id: "deepseek",
381
385
  name: "DeepSeek",
382
386
  proto: "openai",
383
387
  baseURL: "https://api.deepseek.com",
384
- defaultModel: "deepseek-chat",
385
- models: ["deepseek-chat", "deepseek-reasoner"]
388
+ defaultModel: "deepseek-v4-flash",
389
+ models: ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-chat", "deepseek-reasoner"],
390
+ hint: "deepseek-chat/reasoner deprecated 2026-07-24, use V4 models"
386
391
  },
387
392
  {
388
393
  id: "mistral",
389
394
  name: "Mistral",
390
395
  proto: "openai",
391
396
  baseURL: "https://api.mistral.ai/v1",
392
- defaultModel: "mistral-large-latest",
393
- models: ["mistral-large-latest", "mistral-small-latest", "ministral-8b-latest"]
397
+ defaultModel: "mistral-large-2512",
398
+ models: ["mistral-large-2512", "mistral-small-2603", "ministral-8b-2512", "ministral-14b-2512", "mistral-large-latest", "mistral-small-latest"]
394
399
  },
395
400
  {
396
401
  id: "google",
397
402
  name: "Google Gemini",
398
403
  proto: "openai",
399
404
  baseURL: "https://generativelanguage.googleapis.com/v1beta/openai",
400
- defaultModel: "gemini-2.5-flash",
401
- models: ["gemini-2.5-pro", "gemini-2.5-flash", "gemini-2.5-flash-lite"],
405
+ defaultModel: "gemini-3.1-pro",
406
+ models: ["gemini-3.1-pro", "gemini-3-flash", "gemini-3.1-flash-lite", "gemini-2.5-pro", "gemini-2.5-flash"],
402
407
  hint: "Gemini via OpenAI-compatible endpoint"
403
408
  },
404
409
  {
@@ -406,8 +411,8 @@ var LLM_PRESETS = [
406
411
  name: "xAI Grok",
407
412
  proto: "openai",
408
413
  baseURL: "https://api.x.ai/v1",
409
- defaultModel: "grok-4",
410
- models: ["grok-4", "grok-3", "grok-3-mini"]
414
+ defaultModel: "grok-4.3",
415
+ models: ["grok-4.3", "grok-4.20-reasoning", "grok-4.20-non-reasoning", "grok-4", "grok-3", "grok-3-mini"]
411
416
  },
412
417
  {
413
418
  id: "together",
@@ -417,6 +422,7 @@ var LLM_PRESETS = [
417
422
  defaultModel: "meta-llama/Llama-3.3-70B-Instruct-Turbo",
418
423
  models: [
419
424
  "meta-llama/Llama-3.3-70B-Instruct-Turbo",
425
+ "meta-llama/Llama-4-scout-17b-instruct",
420
426
  "Qwen/Qwen2.5-72B-Instruct-Turbo",
421
427
  "deepseek-ai/DeepSeek-V3"
422
428
  ]
@@ -429,6 +435,7 @@ var LLM_PRESETS = [
429
435
  defaultModel: "accounts/fireworks/models/llama-v3p3-70b-instruct",
430
436
  models: [
431
437
  "accounts/fireworks/models/llama-v3p3-70b-instruct",
438
+ "accounts/fireworks/models/llama-4-scout-17b-16e-instruct",
432
439
  "accounts/fireworks/models/qwen2p5-72b-instruct",
433
440
  "accounts/fireworks/models/deepseek-v3"
434
441
  ]
@@ -455,7 +462,7 @@ var LLM_PRESETS = [
455
462
  proto: "anthropic",
456
463
  baseURL: "https://api.claudehub.fun",
457
464
  defaultModel: "claude-sonnet-4.6",
458
- models: ["claude-opus-4.7", "claude-opus-4.6", "claude-opus-4.5", "claude-sonnet-4.6", "claude-sonnet-4.5", "claude-haiku-4.5", "gpt-5.4", "gpt-5.5"],
465
+ models: ["claude-opus-4.7", "claude-opus-4.6", "claude-opus-4.5", "claude-sonnet-4.6", "claude-sonnet-4.5", "claude-haiku-4.5", "gpt-5.5", "gpt-5.4"],
459
466
  hint: "ClaudeHub proxy for Anthropic & OpenAI (\u0420\u0424, \u0421\u0411\u041F, \u043A\u0440\u0438\u043F\u0442\u0430)"
460
467
  },
461
468
  {
@@ -531,13 +538,13 @@ var STAGE_PRESETS = [
531
538
  label: "\u0412\u0441\u0442\u0440\u0435\u0442\u0438\u043B\u0438\u0441\u044C \u0432 \u0440\u0435\u0430\u043B\u0435 \u2014 \u0434\u0430\u043B\u0430 \u0442\u0433",
532
539
  description: "\u0422\u043E\u043B\u044C\u043A\u043E \u0447\u0442\u043E \u043E\u0431\u043C\u0435\u043D\u044F\u043B\u0438\u0441\u044C \u0442\u0433. \u041F\u043E\u043C\u043D\u0438\u0442 \u043B\u0438\u0446\u043E, \u0433\u043E\u043B\u043E\u0441. \u041B\u0451\u0433\u043A\u0438\u0439 \u0438\u043D\u0442\u0435\u0440\u0435\u0441.",
533
540
  defaults: {
534
- interest: 25,
535
- trust: 10,
536
- attraction: 20,
541
+ interest: 38,
542
+ trust: 14,
543
+ attraction: 30,
537
544
  annoyance: 0,
538
- cringeTolerance: 10,
539
- ignoreChance: 0.25,
540
- replyDelaySec: [60, 1800]
545
+ cringeTolerance: 14,
546
+ ignoreChance: 0.12,
547
+ replyDelaySec: [15, 600]
541
548
  }
542
549
  },
543
550
  {
@@ -564,8 +571,8 @@ var STAGE_PRESETS = [
564
571
  attraction: 25,
565
572
  annoyance: 0,
566
573
  cringeTolerance: 5,
567
- ignoreChance: 0.3,
568
- replyDelaySec: [120, 3600]
574
+ ignoreChance: 0.18,
575
+ replyDelaySec: [30, 1200]
569
576
  }
570
577
  },
571
578
  {
@@ -578,8 +585,8 @@ var STAGE_PRESETS = [
578
585
  attraction: 45,
579
586
  annoyance: 0,
580
587
  cringeTolerance: 15,
581
- ignoreChance: 0.15,
582
- replyDelaySec: [60, 900]
588
+ ignoreChance: 0.07,
589
+ replyDelaySec: [10, 420]
583
590
  }
584
591
  },
585
592
  {
@@ -592,8 +599,8 @@ var STAGE_PRESETS = [
592
599
  attraction: 55,
593
600
  annoyance: 0,
594
601
  cringeTolerance: 25,
595
- ignoreChance: 0.1,
596
- replyDelaySec: [30, 600]
602
+ ignoreChance: 0.05,
603
+ replyDelaySec: [8, 300]
597
604
  }
598
605
  },
599
606
  {
@@ -606,8 +613,8 @@ var STAGE_PRESETS = [
606
613
  attraction: 70,
607
614
  annoyance: 0,
608
615
  cringeTolerance: 35,
609
- ignoreChance: 0.05,
610
- replyDelaySec: [10, 300]
616
+ ignoreChance: 0.02,
617
+ replyDelaySec: [3, 120]
611
618
  }
612
619
  },
613
620
  {
@@ -620,8 +627,8 @@ var STAGE_PRESETS = [
620
627
  attraction: 75,
621
628
  annoyance: 0,
622
629
  cringeTolerance: 50,
623
- ignoreChance: 0.07,
624
- replyDelaySec: [5, 600]
630
+ ignoreChance: 0.03,
631
+ replyDelaySec: [3, 240]
625
632
  }
626
633
  },
627
634
  {
@@ -634,8 +641,8 @@ var STAGE_PRESETS = [
634
641
  attraction: 65,
635
642
  annoyance: 10,
636
643
  cringeTolerance: 60,
637
- ignoreChance: 0.1,
638
- replyDelaySec: [10, 1800]
644
+ ignoreChance: 0.05,
645
+ replyDelaySec: [5, 900]
639
646
  }
640
647
  },
641
648
  {
@@ -657,6 +664,92 @@ function findStage(id) {
657
664
  return STAGE_PRESETS.find((s) => s.id === id) ?? STAGE_PRESETS[1];
658
665
  }
659
666
 
667
+ // src/presets/communication.ts
668
+ init_esm_shims();
669
+ var NOTIFICATIONS = ["muted", "normal", "priority"];
670
+ var MESSAGE_STYLES = ["one-liners", "balanced", "bursty", "longform"];
671
+ var INITIATIVES = ["low", "medium", "high"];
672
+ var LIFE_SHARING = ["low", "medium", "high"];
673
+ var COMMUNICATION_PRESETS = [
674
+ {
675
+ id: "normal",
676
+ label: "\u041D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u0430\u044F",
677
+ description: "\u0437\u043E\u043B\u043E\u0442\u0430\u044F \u0441\u0435\u0440\u0435\u0434\u0438\u043D\u0430 \u2014 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u043E, \u043D\u0435 \u043B\u0438\u043F\u043D\u0435\u0442, \u0438\u043D\u043E\u0433\u0434\u0430 \u0441\u0430\u043C\u0430 \u043F\u0438\u0448\u0435\u0442",
678
+ profile: { notifications: "normal", messageStyle: "balanced", initiative: "medium", lifeSharing: "medium" }
679
+ },
680
+ {
681
+ id: "cute",
682
+ label: "\u041C\u0438\u043B\u0430\u044F",
683
+ description: "\u0442\u0451\u043F\u043B\u0430\u044F \u0438 \u0437\u0430\u0431\u043E\u0442\u043B\u0438\u0432\u0430\u044F, \u0447\u0430\u0441\u0442\u043E \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442, \u043F\u0438\u0448\u0435\u0442 \u043F\u0435\u0440\u0432\u043E\u0439, \u0434\u0435\u043B\u0438\u0442\u0441\u044F \u043C\u043E\u043C\u0435\u043D\u0442\u0430\u043C\u0438",
684
+ profile: { notifications: "priority", messageStyle: "balanced", initiative: "high", lifeSharing: "high" }
685
+ },
686
+ {
687
+ id: "alt",
688
+ label: "\u0410\u043B\u044C\u0442\u0443\u0448\u043A\u0430",
689
+ description: "\u0445\u043E\u043B\u043E\u0434\u043D\u0430\u044F, \u0441\u0443\u0445\u0430\u044F, \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0435 \u043E\u0442\u0432\u0435\u0442\u044B, \u043F\u043E\u0447\u0442\u0438 \u043D\u0435 \u043F\u0438\u0448\u0435\u0442 \u043F\u0435\u0440\u0432\u043E\u0439, \u043B\u0438\u0447\u043D\u044B\u043C \u043D\u0435 \u0434\u0435\u043B\u0438\u0442\u0441\u044F",
690
+ profile: { notifications: "normal", messageStyle: "one-liners", initiative: "low", lifeSharing: "low" }
691
+ },
692
+ {
693
+ id: "clingy",
694
+ label: "\u0417\u0430\u043B\u0438\u043F\u0430\u043B\u0430",
695
+ description: "\u043E\u0447\u0435\u043D\u044C \u043B\u0438\u043F\u0443\u0447\u0430\u044F, \u0441\u043F\u0430\u043C\u0438\u0442 \u043F\u0443\u0437\u044B\u0440\u044F\u043C\u0438, \u0432\u0441\u0435\u0433\u0434\u0430 \u043E\u043D\u043B\u0430\u0439\u043D, \u043F\u0438\u0448\u0435\u0442 \u043F\u0435\u0440\u0432\u043E\u0439 \u043F\u043E\u0441\u0442\u043E\u044F\u043D\u043D\u043E",
696
+ profile: { notifications: "priority", messageStyle: "bursty", initiative: "high", lifeSharing: "high" }
697
+ },
698
+ {
699
+ id: "chatty",
700
+ label: "\u0411\u043E\u043B\u0442\u0443\u0448\u043A\u0430",
701
+ description: "\u043B\u044E\u0431\u0438\u0442 \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u0438\u0441\u0442\u043E\u0440\u0438\u0438, \u043F\u0438\u0448\u0435\u0442 \u0434\u043B\u0438\u043D\u043D\u044B\u0435 \u0442\u0435\u043A\u0441\u0442\u044B, \u0447\u0430\u0441\u0442\u043E \u0434\u0435\u043B\u0438\u0442\u0441\u044F \u0431\u044B\u0442\u043E\u0432\u044B\u043C",
702
+ profile: { notifications: "priority", messageStyle: "longform", initiative: "medium", lifeSharing: "high" }
703
+ }
704
+ ];
705
+ function findCommunicationPreset(id) {
706
+ return COMMUNICATION_PRESETS.find((p) => p.id === id);
707
+ }
708
+ function normalizeCommunicationProfile(source) {
709
+ const fallback = source?.vibe === "warm" ? findCommunicationPreset("cute").profile : source?.vibe === "short" ? findCommunicationPreset("alt").profile : findCommunicationPreset("normal").profile;
710
+ const raw = source?.communication;
711
+ return {
712
+ notifications: includes(NOTIFICATIONS, raw?.notifications) ? raw.notifications : fallback.notifications,
713
+ messageStyle: includes(MESSAGE_STYLES, raw?.messageStyle) ? raw.messageStyle : fallback.messageStyle,
714
+ initiative: includes(INITIATIVES, raw?.initiative) ? raw.initiative : fallback.initiative,
715
+ lifeSharing: includes(LIFE_SHARING, raw?.lifeSharing) ? raw.lifeSharing : fallback.lifeSharing
716
+ };
717
+ }
718
+ function deriveLegacyVibe(profile) {
719
+ return profile.messageStyle === "one-liners" && profile.initiative === "low" && profile.lifeSharing === "low" ? "short" : "warm";
720
+ }
721
+ function communicationProfileLabel(profile) {
722
+ const exact = COMMUNICATION_PRESETS.find((p) => sameProfile(p.profile, profile));
723
+ if (exact) return exact.label;
724
+ return `notifications=${profile.notifications}, style=${profile.messageStyle}, initiative=${profile.initiative}, life=${profile.lifeSharing}`;
725
+ }
726
+ function communicationPromptFragment(profile) {
727
+ const notifications = profile.notifications === "priority" ? "\u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043E\u0442 \u043D\u0435\u0433\u043E \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u044B/\u0432\u0430\u0436\u043D\u044B\u0435: \u0442\u044B \u0447\u0430\u0441\u0442\u043E \u0432\u0438\u0434\u0438\u0448\u044C \u0435\u0433\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0431\u044B\u0441\u0442\u0440\u043E, \u043E\u0441\u043E\u0431\u0435\u043D\u043D\u043E \u0435\u0441\u043B\u0438 \u0443\u0436\u0435 \u0438\u0434\u0451\u0442 \u0434\u0438\u0430\u043B\u043E\u0433" : profile.notifications === "muted" ? "\u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043D\u0435 \u043F\u0440\u0438\u043E\u0440\u0438\u0442\u0435\u0442\u043D\u044B\u0435: \u043C\u043E\u0436\u0435\u0448\u044C \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C \u043F\u043E\u0437\u0436\u0435, \u043D\u043E \u0435\u0441\u043B\u0438 \u0434\u0438\u0430\u043B\u043E\u0433 \u0443\u0436\u0435 \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u2014 \u043D\u0435 \u0438\u0441\u0447\u0435\u0437\u0430\u0439 \u0431\u0435\u0437 \u043F\u0440\u0438\u0447\u0438\u043D\u044B" : "\u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043E\u0431\u044B\u0447\u043D\u044B\u0435: \u0438\u043D\u043E\u0433\u0434\u0430 \u0432\u0438\u0434\u0438\u0448\u044C \u0441\u0440\u0430\u0437\u0443, \u0438\u043D\u043E\u0433\u0434\u0430 \u043F\u043E\u0437\u0436\u0435";
728
+ const style = profile.messageStyle === "one-liners" ? "\u043E\u0431\u044B\u0447\u043D\u043E 1 \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0439 \u043F\u0443\u0437\u044B\u0440\u044C, 1-5 \u0441\u043B\u043E\u0432; \u0441\u0435\u0440\u0438\u0438 \u0440\u0435\u0434\u043A\u0438 \u0438 \u0442\u043E\u043B\u044C\u043A\u043E \u043D\u0430 \u044D\u043C\u043E\u0446\u0438\u044F\u0445" : profile.messageStyle === "bursty" ? "\u0447\u0430\u0441\u0442\u043E \u043F\u0438\u0448\u0435\u0448\u044C 2-5 \u043F\u0443\u0437\u044B\u0440\u0435\u0439 \u043F\u043E\u0434\u0440\u044F\u0434: \u043E\u0431\u0440\u044B\u0432\u043A\u0438 \u043C\u044B\u0441\u043B\u0435\u0439, \u043E\u0434\u043D\u043E \u0441\u043B\u043E\u0432\u043E, \u043F\u043E\u0442\u043E\u043C \u0443\u0442\u043E\u0447\u043D\u0435\u043D\u0438\u0435, \u043F\u043E\u0442\u043E\u043C \u044D\u043C\u043E\u0446\u0438\u044F" : profile.messageStyle === "longform" ? "\u043C\u043E\u0436\u0435\u0448\u044C \u043F\u0438\u0441\u0430\u0442\u044C \u043E\u0434\u0438\u043D \u0434\u043B\u0438\u043D\u043D\u0435\u0435 \u043F\u0443\u0437\u044B\u0440\u044C \u0438\u043B\u0438 2-3 \u0441\u0440\u0435\u0434\u043D\u0438\u0445, \u043A\u043E\u0433\u0434\u0430 \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0448\u044C \u0438\u0441\u0442\u043E\u0440\u0438\u044E/\u043E\u0431\u044A\u044F\u0441\u043D\u044F\u0435\u0448\u044C \u0447\u0443\u0432\u0441\u0442\u0432\u0430" : "\u043E\u0431\u044B\u0447\u043D\u043E 1-3 \u043F\u0443\u0437\u044B\u0440\u044F \u0440\u0430\u0437\u043D\u043E\u0439 \u0434\u043B\u0438\u043D\u044B, \u0431\u0435\u0437 \u0438\u0434\u0435\u0430\u043B\u044C\u043D\u043E\u0439 \u0440\u043E\u0432\u043D\u043E\u0441\u0442\u0438";
729
+ const initiative = profile.initiative === "high" ? "\u0438\u043D\u0438\u0446\u0438\u0430\u0442\u0438\u0432\u0430 \u0432\u044B\u0441\u043E\u043A\u0430\u044F: \u0442\u044B \u043C\u043E\u0436\u0435\u0448\u044C \u043F\u0438\u0441\u0430\u0442\u044C \u043F\u0435\u0440\u0432\u043E\u0439, \u043F\u0440\u043E\u0432\u0435\u0440\u044F\u0442\u044C \u0433\u0434\u0435 \u043E\u043D, \u043A\u0438\u0434\u0430\u0442\u044C \u0431\u044B\u0442\u043E\u0432\u044B\u0435 \u043C\u044B\u0441\u043B\u0438, \u0440\u0435\u0432\u043D\u043E\u0432\u0430\u0442\u044C/\u0441\u043A\u0443\u0447\u0430\u0442\u044C" : profile.initiative === "low" ? "\u0438\u043D\u0438\u0446\u0438\u0430\u0442\u0438\u0432\u0430 \u043D\u0438\u0437\u043A\u0430\u044F: \u043F\u0435\u0440\u0432\u043E\u0439 \u043F\u0438\u0448\u0435\u0448\u044C \u0440\u0435\u0434\u043A\u043E, \u0432 \u043E\u0441\u043D\u043E\u0432\u043D\u043E\u043C \u043A\u043E\u0433\u0434\u0430 \u0435\u0441\u0442\u044C \u044F\u0432\u043D\u044B\u0439 \u043F\u043E\u0432\u043E\u0434" : "\u0438\u043D\u0438\u0446\u0438\u0430\u0442\u0438\u0432\u0430 \u0441\u0440\u0435\u0434\u043D\u044F\u044F: \u0438\u043D\u043E\u0433\u0434\u0430 \u043F\u0438\u0448\u0435\u0448\u044C \u043F\u0435\u0440\u0432\u043E\u0439 \u043F\u043E \u0434\u0435\u043B\u0443, \u044D\u043C\u043E\u0446\u0438\u0438 \u0438\u043B\u0438 \u0435\u0441\u043B\u0438 \u0432\u0441\u043F\u043E\u043C\u043D\u0438\u043B\u0430 \u043E \u043D\u0451\u043C";
730
+ const life = profile.lifeSharing === "high" ? "\u0447\u0430\u0441\u0442\u043E \u0434\u0435\u043B\u0438\u0448\u044C\u0441\u044F \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u0438\u043C\u0438 \u043C\u043E\u043C\u0435\u043D\u0442\u0430\u043C\u0438 \u0434\u043D\u044F: \u0447\u0442\u043E \u0443\u0432\u0438\u0434\u0435\u043B\u0430, \u0447\u0442\u043E \u0431\u0435\u0441\u0438\u0442, \u0447\u0442\u043E \u0435\u043B\u0430, \u0447\u0442\u043E \u0441\u043A\u0430\u0437\u0430\u043B\u0430 \u043F\u043E\u0434\u0440\u0443\u0433\u0430/\u043C\u0430\u043C\u0430, \u0447\u0442\u043E \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u0438\u0442 \u043D\u0430 \u0443\u0447\u0451\u0431\u0435/\u0440\u0430\u0431\u043E\u0442\u0435" : profile.lifeSharing === "low" ? "\u043B\u0438\u0447\u043D\u044B\u043C \u0434\u0435\u043B\u0438\u0448\u044C\u0441\u044F \u0440\u0435\u0434\u043A\u043E, \u043D\u0435 \u0440\u0430\u0441\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0448\u044C \u0434\u0435\u043D\u044C \u0431\u0435\u0437 \u043F\u043E\u0432\u043E\u0434\u0430" : "\u0438\u043D\u043E\u0433\u0434\u0430 \u0434\u0435\u043B\u0438\u0448\u044C\u0441\u044F \u043C\u043E\u043C\u0435\u043D\u0442\u0430\u043C\u0438 \u0436\u0438\u0437\u043D\u0438, \u043E\u0441\u043E\u0431\u0435\u043D\u043D\u043E \u0435\u0441\u043B\u0438 \u044D\u0442\u043E \u0441\u0432\u044F\u0437\u0430\u043D\u043E \u0441 \u0442\u0435\u043A\u0443\u0449\u0438\u043C \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D\u0438\u0435\u043C";
731
+ return `# \u0422\u041E\u041D\u041A\u0410\u042F \u041D\u0410\u0421\u0422\u0420\u041E\u0419\u041A\u0410 \u041E\u0411\u0429\u0415\u041D\u0418\u042F
732
+ - \u0423\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F: ${notifications}.
733
+ - \u0421\u0442\u0438\u043B\u044C \u043F\u0443\u0437\u044B\u0440\u0435\u0439: ${style}.
734
+ - \u0418\u043D\u0438\u0446\u0438\u0430\u0442\u0438\u0432\u0430: ${initiative}.
735
+ - \u041C\u043E\u043C\u0435\u043D\u0442\u044B \u0438\u0437 \u0436\u0438\u0437\u043D\u0438: ${life}.
736
+
737
+ \u041F\u0440\u0430\u0432\u0438\u043B\u0430 \u0440\u0438\u0442\u043C\u0430:
738
+ - \u041D\u0435 \u0432\u0435\u0434\u0438 \u0441\u0435\u0431\u044F \u043F\u043E \u0441\u0445\u0435\u043C\u0435 "\u043E\u0442\u0432\u0435\u0442\u0438\u043B\u0430 \u2192 \u0443\u0448\u043B\u0430 \u2192 \u0432\u0435\u0440\u043D\u0443\u043B\u0430\u0441\u044C \u2192 \u043E\u0442\u0432\u0435\u0442\u0438\u043B\u0430" \u043A\u0430\u0436\u0434\u044B\u0439 \u0440\u0430\u0437.
739
+ - \u0412 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u043C \u0434\u0438\u0430\u043B\u043E\u0433\u0435 \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B\u0439 \u0447\u0435\u043B\u043E\u0432\u0435\u043A \u0447\u0430\u0441\u0442\u043E \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0432 \u0447\u0430\u0442\u0435 \u0438 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0431\u044B\u0441\u0442\u0440\u043E. \u0415\u0441\u043B\u0438 \u0432\u0441\u0451 \u0436\u0435 \u0443\u0445\u043E\u0434\u0438\u0448\u044C \u2014 \u0434\u043E\u043B\u0436\u043D\u0430 \u0431\u044B\u0442\u044C \u0431\u044B\u0442\u043E\u0432\u0430\u044F \u043F\u0440\u0438\u0447\u0438\u043D\u0430 \u0432 \u0442\u0435\u043A\u0441\u0442\u0435, \u0430 \u043D\u0435 \u0432\u043D\u0435\u0437\u0430\u043F\u043D\u0430\u044F \u043F\u0440\u043E\u043F\u0430\u0436\u0430.
740
+ - \u0418\u043D\u043E\u0433\u0434\u0430 \u043C\u043E\u0436\u043D\u043E \u043D\u0430\u043F\u0438\u0441\u0430\u0442\u044C 3-5 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u043F\u043E\u0434\u0440\u044F\u0434, \u043F\u043E\u0442\u043E\u043C \u043F\u0440\u043E\u043F\u0430\u0441\u0442\u044C. \u0418\u043D\u043E\u0433\u0434\u0430 \u043C\u043E\u0436\u043D\u043E \u0432\u0435\u0441\u0442\u0438 \u0434\u043E\u043B\u0433\u0438\u0439 \u0431\u044B\u0441\u0442\u0440\u044B\u0439 \u0434\u0438\u0430\u043B\u043E\u0433. \u0418\u043D\u043E\u0433\u0434\u0430 \u043C\u043E\u0436\u043D\u043E \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C \u043E\u0434\u043D\u0438\u043C \u0441\u043B\u043E\u0432\u043E\u043C. \u0420\u0438\u0442\u043C \u0434\u043E\u043B\u0436\u0435\u043D \u043C\u0435\u043D\u044F\u0442\u044C\u0441\u044F.
741
+ - \u041D\u0435 \u0432\u044B\u0434\u0443\u043C\u044B\u0432\u0430\u0439 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435 URL. \u0421\u0441\u044B\u043B\u043A\u0438 \u0443\u043F\u043E\u043C\u0438\u043D\u0430\u0439 \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u0438 \u0435\u0441\u0442\u044C \u0432 \u043A\u043E\u043D\u0442\u0435\u043A\u0441\u0442\u0435 \u0438\u043B\u0438 \u043A\u0430\u043A \u0431\u044B\u0442\u043E\u0432\u043E\u0435 "\u0441\u043A\u0438\u043D\u0443 \u043F\u043E\u0442\u043E\u043C".`;
742
+ }
743
+ function communicationDecisionState(profile) {
744
+ return `communication={notifications:${profile.notifications}, messageStyle:${profile.messageStyle}, initiative:${profile.initiative}, lifeSharing:${profile.lifeSharing}}`;
745
+ }
746
+ function includes(values, value) {
747
+ return typeof value === "string" && values.includes(value);
748
+ }
749
+ function sameProfile(a, b) {
750
+ return a.notifications === b.notifications && a.messageStyle === b.messageStyle && a.initiative === b.initiative && a.lifeSharing === b.lifeSharing;
751
+ }
752
+
660
753
  // src/storage/md.ts
661
754
  init_esm_shims();
662
755
  import { promises as fs } from "fs";
@@ -692,13 +785,14 @@ async function readConfig(slug) {
692
785
  try {
693
786
  const raw = await fs.readFile(path2.join(profileDir(slug), "config.json"), "utf8");
694
787
  const parsed = JSON.parse(raw);
788
+ const communication = normalizeCommunicationProfile(parsed);
695
789
  return {
696
790
  sleepFrom: 23,
697
791
  sleepTo: 8,
698
792
  nightWakeChance: 0.05,
699
- vibe: "short",
700
793
  busySchedule: [],
701
- ...parsed
794
+ ...parsed,
795
+ communication
702
796
  };
703
797
  } catch {
704
798
  return null;
@@ -715,7 +809,16 @@ async function writeConfig(cfg) {
715
809
  async function listProfiles() {
716
810
  try {
717
811
  const entries = await fs.readdir(DATA_ROOT, { withFileTypes: true });
718
- return entries.filter((e) => e.isDirectory()).map((e) => e.name);
812
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
813
+ const valid = await Promise.all(dirs.map(async (name) => {
814
+ try {
815
+ await fs.access(path2.join(profileDir(name), "config.json"));
816
+ return name;
817
+ } catch {
818
+ return null;
819
+ }
820
+ }));
821
+ return valid.filter((name) => !!name);
719
822
  } catch {
720
823
  return [];
721
824
  }
@@ -1347,6 +1450,12 @@ var Bar = ({ step, total }) => {
1347
1450
  ).join("");
1348
1451
  return /* @__PURE__ */ React.createElement(Text, { color: "magenta" }, "[", blocks, "] \u0448\u0430\u0433 ", step + 1, "/", total);
1349
1452
  };
1453
+ function personaNotesForGeneration(notes, communication) {
1454
+ return [
1455
+ notes.trim(),
1456
+ `\u0422\u043E\u043D \u043E\u0431\u0449\u0435\u043D\u0438\u044F: ${communicationProfileLabel(communication)}. \u0423\u0447\u0442\u0438 \u044D\u0442\u043E \u043F\u0440\u0438 speech.md \u0438 communication.md.`
1457
+ ].filter(Boolean).join("\n\n");
1458
+ }
1350
1459
  function Wizard({ initial, onDone }) {
1351
1460
  const { exit } = useApp();
1352
1461
  const [step, setStep] = useState(initial ? "mode" : "splash");
@@ -1379,7 +1488,7 @@ function Wizard({ initial, onDone }) {
1379
1488
  const [sleepFromStr, setSleepFromStr] = useState("23");
1380
1489
  const [sleepToStr, setSleepToStr] = useState("8");
1381
1490
  const [nightWakeStr, setNightWakeStr] = useState("5");
1382
- const [vibe, setVibe] = useState("short");
1491
+ const [communicationProfile, setCommunicationProfile] = useState(normalizeCommunicationProfile(initial));
1383
1492
  const [stage, setStage] = useState(initial?.stage ?? "tg-given-cold");
1384
1493
  const [pickedMcp, setPickedMcp] = useState(initial?.mcp?.map((m) => m.id) ?? []);
1385
1494
  const [mcpQueue, setMcpQueue] = useState([]);
@@ -1451,11 +1560,12 @@ function Wizard({ initial, onDone }) {
1451
1560
  clearInterval(timer);
1452
1561
  }
1453
1562
  }, 100);
1454
- const generated = await generatePersonaPack(llm, slug, name.trim(), Number(ageStr), nationality, personaNotes);
1563
+ const generated = await generatePersonaPack(llm, slug, name.trim(), Number(ageStr), nationality, personaNotesForGeneration(personaNotes, communicationProfile));
1455
1564
  if (timer) clearInterval(timer);
1456
1565
  setGenPercent(100);
1457
1566
  setGenStatus("\u0433\u043E\u0442\u043E\u0432\u043E!");
1458
1567
  setBusySchedule(generated.busySchedule);
1568
+ await writeConfig(makeConfig({ busySchedule: generated.busySchedule, mcp: [] }));
1459
1569
  setTimeout(() => setStep("stage"), 800);
1460
1570
  } catch (e) {
1461
1571
  if (timer) clearInterval(timer);
@@ -1804,15 +1914,85 @@ function Wizard({ initial, onDone }) {
1804
1914
  )));
1805
1915
  }
1806
1916
  if (step === "vibe") {
1807
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u043A\u0430\u043A \u043E\u043D\u0430 \u0431\u0443\u0434\u0435\u0442 \u043E\u0431\u0449\u0430\u0442\u044C\u0441\u044F?" }), /* @__PURE__ */ React.createElement(Bar, { step: 7, total: 12 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
1917
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0442\u043E\u043D\u043A\u0430\u044F \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u043E\u0431\u0449\u0435\u043D\u0438\u044F" }), /* @__PURE__ */ React.createElement(Bar, { step: 7, total: 12 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
1918
+ SelectInput,
1919
+ {
1920
+ items: [
1921
+ ...COMMUNICATION_PRESETS.map((p) => ({ label: `${p.label} \u2014 ${p.description}`, value: p.id })),
1922
+ { label: "\u270E \u041D\u0430\u0441\u0442\u0440\u043E\u0438\u0442\u044C \u0432\u0440\u0443\u0447\u043D\u0443\u044E", value: "__custom__" }
1923
+ ],
1924
+ onSelect: (it) => {
1925
+ if (it.value === "__custom__") {
1926
+ setStep("comm-notifications");
1927
+ return;
1928
+ }
1929
+ const preset = findCommunicationPreset(String(it.value));
1930
+ if (preset) setCommunicationProfile(preset.profile);
1931
+ setStep("tz");
1932
+ }
1933
+ }
1934
+ )));
1935
+ }
1936
+ if (step === "comm-notifications") {
1937
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043E\u0442 \u0442\u0435\u0431\u044F" }), /* @__PURE__ */ React.createElement(Bar, { step: 7, total: 12 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
1938
+ SelectInput,
1939
+ {
1940
+ items: [
1941
+ { label: "\uFFFD muted \u2014 \u043C\u043E\u0436\u0435\u0442 \u0432\u0438\u0434\u0435\u0442\u044C \u043F\u043E\u0437\u0436\u0435", value: "muted" },
1942
+ { label: "\u{1F514} normal \u2014 \u043E\u0431\u044B\u0447\u043D\u044B\u0435 \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F", value: "normal" },
1943
+ { label: "\u{1F496} priority \u2014 \u0442\u0432\u043E\u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0432\u0430\u0436\u043D\u044B\u0435, \u0447\u0430\u0449\u0435 \u0431\u044B\u0441\u0442\u0440\u043E \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442", value: "priority" }
1944
+ ],
1945
+ onSelect: (it) => {
1946
+ setCommunicationProfile((p) => ({ ...p, notifications: it.value }));
1947
+ setStep("comm-style");
1948
+ }
1949
+ }
1950
+ )));
1951
+ }
1952
+ if (step === "comm-style") {
1953
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0441\u0442\u0438\u043B\u044C \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438" }), /* @__PURE__ */ React.createElement(Bar, { step: 7, total: 12 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
1954
+ SelectInput,
1955
+ {
1956
+ items: [
1957
+ { label: "one-liners \u2014 \u043A\u043E\u0440\u043E\u0442\u043A\u043E, 1 \u0441\u043B\u043E\u0432\u043E/\u0444\u0440\u0430\u0437\u0430", value: "one-liners" },
1958
+ { label: "balanced \u2014 1-3 \u043F\u0443\u0437\u044B\u0440\u044F, \u0440\u0430\u0437\u043D\u044B\u0439 \u0440\u0438\u0442\u043C", value: "balanced" },
1959
+ { label: "bursty \u2014 \u043F\u0438\u0448\u0435\u0442 \u0441\u0435\u0440\u0438\u0435\u0439 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u043F\u043E\u0434\u0440\u044F\u0434", value: "bursty" },
1960
+ { label: "longform \u2014 \u0438\u043D\u043E\u0433\u0434\u0430 \u0434\u043B\u0438\u043D\u043D\u0435\u0435 \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442", value: "longform" }
1961
+ ],
1962
+ onSelect: (it) => {
1963
+ setCommunicationProfile((p) => ({ ...p, messageStyle: it.value }));
1964
+ setStep("comm-initiative");
1965
+ }
1966
+ }
1967
+ )));
1968
+ }
1969
+ if (step === "comm-initiative") {
1970
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0438\u043D\u0438\u0446\u0438\u0430\u0442\u0438\u0432\u0430" }), /* @__PURE__ */ React.createElement(Bar, { step: 7, total: 12 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
1971
+ SelectInput,
1972
+ {
1973
+ items: [
1974
+ { label: "low \u2014 \u043F\u0435\u0440\u0432\u043E\u0439 \u043F\u0438\u0448\u0435\u0442 \u0440\u0435\u0434\u043A\u043E", value: "low" },
1975
+ { label: "medium \u2014 \u0438\u043D\u043E\u0433\u0434\u0430 \u043F\u0438\u0448\u0435\u0442 \u0441\u0430\u043C\u0430", value: "medium" },
1976
+ { label: "high \u2014 \u0447\u0430\u0441\u0442\u043E \u0441\u0430\u043C\u0430 \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442 \u0442\u0435\u043C\u044B", value: "high" }
1977
+ ],
1978
+ onSelect: (it) => {
1979
+ setCommunicationProfile((p) => ({ ...p, initiative: it.value }));
1980
+ setStep("comm-life");
1981
+ }
1982
+ }
1983
+ )));
1984
+ }
1985
+ if (step === "comm-life") {
1986
+ return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F \u043C\u043E\u043C\u0435\u043D\u0442\u0430\u043C\u0438 \u0436\u0438\u0437\u043D\u0438" }), /* @__PURE__ */ React.createElement(Bar, { step: 7, total: 12 }), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(
1808
1987
  SelectInput,
1809
1988
  {
1810
1989
  items: [
1811
- { label: '\u{1F4AC} \u041A\u043E\u0440\u043E\u0442\u043A\u0438\u0439 \u2014 \u0440\u0435\u0430\u043B\u0438\u0441\u0442\u0438\u0447\u043D\u043E-\u043C\u0438\u043D\u0438\u043C\u0430\u043B\u0438\u0441\u0442\u0438\u0447\u043D\u044B\u0439 \u0441\u0442\u0438\u043B\u044C. "\u043D\u043E\u0440\u043C", "\u0442\u0430 \u043D\u0438\u0447\u0435", \u0447\u0430\u0449\u0435 \u0438\u0433\u043D\u043E\u0440\u0438\u0442, \u043A\u0430\u043A \u043D\u0430\u0441\u0442\u043E\u044F\u0449\u0430\u044F \u0434\u0435\u0432\u0443\u0448\u043A\u0430 \u0432 \u0442\u0433', value: "short" },
1812
- { label: "\u{1F338} \u0422\u0451\u043F\u043B\u044B\u0439 \u2014 \u0440\u0430\u0437\u0432\u0451\u0440\u043D\u0443\u0442\u044B\u0435 \u043E\u0442\u0432\u0435\u0442\u044B, \u043F\u0440\u0438\u0434\u0443\u043C\u044B\u0432\u0430\u0435\u0442 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u043F\u0440\u043E \u0441\u0432\u043E\u0439 \u0434\u0435\u043D\u044C, \u0440\u0435\u0436\u0435 \u0438\u0433\u043D\u043E\u0440\u0438\u0442, \u043E\u0445\u043E\u0442\u043D\u0435\u0435 \u043E\u0431\u0449\u0430\u0435\u0442\u0441\u044F", value: "warm" }
1990
+ { label: "low \u2014 \u043F\u043E\u0447\u0442\u0438 \u043D\u0435 \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u043E \u0441\u0435\u0431\u0435", value: "low" },
1991
+ { label: "medium \u2014 \u0438\u043D\u043E\u0433\u0434\u0430 \u0431\u044B\u0442\u043E\u0432\u044B\u0435 \u043C\u043E\u043C\u0435\u043D\u0442\u044B", value: "medium" },
1992
+ { label: "high \u2014 \u0447\u0430\u0441\u0442\u043E \u043F\u0438\u0448\u0435\u0442 \u0447\u0442\u043E \u0443 \u043D\u0435\u0451 \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u0438\u0442", value: "high" }
1813
1993
  ],
1814
1994
  onSelect: (it) => {
1815
- setVibe(it.value);
1995
+ setCommunicationProfile((p) => ({ ...p, lifeSharing: it.value }));
1816
1996
  setStep("tz");
1817
1997
  }
1818
1998
  }
@@ -1843,8 +2023,10 @@ function Wizard({ initial, onDone }) {
1843
2023
  {
1844
2024
  limit: 10,
1845
2025
  items: STAGE_PRESETS.filter((s) => s.id !== "dumped").map((s) => ({ label: `${s.label} \xB7 ${s.description}`, value: s.id })),
1846
- onSelect: (it) => {
1847
- setStage(it.value);
2026
+ onSelect: async (it) => {
2027
+ const nextStage = it.value;
2028
+ setStage(nextStage);
2029
+ await writeConfig(makeConfig({ stage: nextStage }));
1848
2030
  setStep("mcp-pick");
1849
2031
  }
1850
2032
  }
@@ -1861,7 +2043,10 @@ function Wizard({ initial, onDone }) {
1861
2043
  setMcpQueue(need);
1862
2044
  setMcpSecretIdx(0);
1863
2045
  if (need.length) setStep("mcp-secret");
1864
- else setStep("practice");
2046
+ else {
2047
+ setStep("saving");
2048
+ save();
2049
+ }
1865
2050
  }
1866
2051
  }
1867
2052
  ));
@@ -1907,27 +2092,31 @@ function Wizard({ initial, onDone }) {
1907
2092
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", padding: 1 }, /* @__PURE__ */ React.createElement(Header, { sub: "\u0433\u043E\u0442\u043E\u0432\u043E" }), /* @__PURE__ */ React.createElement(Text, { color: "green" }, "\u043F\u0440\u043E\u0444\u0438\u043B\u044C \u0441\u043E\u0445\u0440\u0430\u043D\u0451\u043D. \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0435\u043C \u0430\u0433\u0435\u043D\u0442\u0430\u2026"));
1908
2093
  }
1909
2094
  return null;
1910
- async function save() {
2095
+ function makeConfig(overrides = {}) {
1911
2096
  const slug = slugify(name);
1912
- const cfg = {
2097
+ return {
1913
2098
  slug,
1914
2099
  name: name.trim(),
1915
2100
  age: Number(ageStr),
1916
2101
  nationality,
1917
2102
  tz: tz || defaultTzForNationality(nationality),
1918
2103
  mode,
1919
- stage,
2104
+ stage: overrides.stage ?? stage,
1920
2105
  llm: { presetId: llmPresetId, proto: llmProto, baseURL: llmBaseURL, apiKey: llmKey, model: llmModel },
1921
2106
  telegram: mode === "bot" ? { botToken } : { apiId: Number(apiId), apiHash, phone, sessionString },
1922
- mcp: pickedMcp.map((id) => ({ id, secrets: mcpSecrets[id] ?? {} })),
2107
+ mcp: overrides.mcp ?? pickedMcp.map((id) => ({ id, secrets: mcpSecrets[id] ?? {} })),
1923
2108
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1924
2109
  sleepFrom: Number(sleepFromStr),
1925
2110
  sleepTo: Number(sleepToStr),
1926
2111
  nightWakeChance: Number(nightWakeStr) / 100,
1927
- vibe,
2112
+ vibe: deriveLegacyVibe(communicationProfile),
2113
+ communication: communicationProfile,
1928
2114
  personaNotes: personaNotes.trim() || void 0,
1929
- busySchedule
2115
+ busySchedule: overrides.busySchedule ?? busySchedule
1930
2116
  };
2117
+ }
2118
+ async function save() {
2119
+ const cfg = makeConfig();
1931
2120
  await writeConfig(cfg);
1932
2121
  setStep("done");
1933
2122
  setTimeout(() => onDone(cfg), 600);
@@ -2070,13 +2259,14 @@ function Dashboard({ runtime }) {
2070
2259
  break;
2071
2260
  }
2072
2261
  case "log": {
2073
- const today2 = sessionDate(runtime.cfg.tz);
2074
- const p = await readSessionLog(runtime.cfg.slug, today2);
2075
- append(p.slice(-3e3));
2262
+ const day = /^\d{4}-\d{2}-\d{2}$/.test(rest[0] ?? "") ? rest[0] : sessionDate(runtime.cfg.tz);
2263
+ const limit = Number(rest.find((x) => /^\d+$/.test(x)) ?? 3e3);
2264
+ const p = await readSessionLog(runtime.cfg.slug, day);
2265
+ append(p.trim() ? p.slice(-Math.max(500, Math.min(limit, 2e4))) : `(log/${day}.md \u043F\u0443\u0441\u0442 \u0438\u043B\u0438 \u0435\u0449\u0451 \u043D\u0435 \u0441\u043E\u0437\u0434\u0430\u043D)`);
2076
2266
  break;
2077
2267
  }
2078
2268
  case "help":
2079
- append(":status :why :amnesia <\u043C\u0438\u043D> [chatId] :reset :stage <id> :wake [chatId] :debug [chatId] :pause :resume :cringe :relationship :persona :log :block [chatId] :unblock [chatId] :read [chatId] :clear-chat [chatId] [--revoke] :report-spam [chatId] :delete-last [chatId] [--local] :edit-last <text> :sticker [chatId] :quit");
2269
+ append(":status :why :amnesia <\u043C\u0438\u043D> [chatId] :reset :stage <id> :wake [chatId] :debug [chatId] :pause :resume :cringe :relationship :persona :log [YYYY-MM-DD] [chars] :block [chatId] :unblock [chatId] :read [chatId] :clear-chat [chatId] [--revoke] :report-spam [chatId] :delete-last [chatId] [--local] :edit-last <text> :sticker [chatId] :quit");
2080
2270
  break;
2081
2271
  case "quit":
2082
2272
  case "exit":
@@ -2094,7 +2284,7 @@ function Dashboard({ runtime }) {
2094
2284
  const line = cmd.trim();
2095
2285
  setCmd("");
2096
2286
  if (line) await execute(line);
2097
- } })), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u043A\u043E\u043C\u0430\u043D\u0434\u044B: :status :why :amnesia <\u043C\u0438\u043D> :reset :stage <id> :pause :resume :cringe :persona :log :block :unblock :read :clear-chat :delete-last :edit-last :sticker :quit"));
2287
+ } })), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u043A\u043E\u043C\u0430\u043D\u0434\u044B: :status :why :amnesia <\u043C\u0438\u043D> :reset :stage <id> :pause :resume :cringe :persona :log [day] :block :unblock :read :clear-chat :delete-last :edit-last :sticker :quit"));
2098
2288
  }
2099
2289
 
2100
2290
  // src/engine/runtime.ts
@@ -2230,6 +2420,7 @@ init_esm_shims();
2230
2420
  function computePresenceProfile(cfg) {
2231
2421
  const seed = [...cfg.name].reduce((a, c) => a + c.charCodeAt(0), 0) + cfg.age;
2232
2422
  const r = (n) => (seed * 9301 + n * 49297) % 233280 / 233280;
2423
+ const communication = normalizeCommunicationProfile(cfg);
2233
2424
  const patterns = [
2234
2425
  "phone-attached",
2235
2426
  "burst-checker",
@@ -2237,13 +2428,34 @@ function computePresenceProfile(cfg) {
2237
2428
  "evening-only",
2238
2429
  "phone-attached-night"
2239
2430
  ];
2240
- const pattern = patterns[Math.floor(r(1) * patterns.length)] ?? "burst-checker";
2431
+ let pattern = patterns[Math.floor(r(1) * patterns.length)] ?? "burst-checker";
2432
+ if (communication.notifications === "priority") {
2433
+ pattern = communication.messageStyle === "bursty" || pattern === "rare-checker" || pattern === "evening-only" ? "phone-attached" : pattern;
2434
+ } else if (communication.notifications === "muted") {
2435
+ pattern = pattern === "phone-attached" ? "burst-checker" : pattern === "phone-attached-night" ? "evening-only" : pattern;
2436
+ }
2241
2437
  const sleepFrom = cfg.sleepFrom ?? 23;
2242
2438
  const sleepTo = cfg.sleepTo ?? 8;
2243
- const nightWakeChance = cfg.nightWakeChance ?? 0.05;
2244
- const checkEveryMin = pattern === "phone-attached" ? 3 + Math.floor(r(4) * 5) : pattern === "burst-checker" ? 15 + Math.floor(r(4) * 20) : pattern === "rare-checker" ? 60 + Math.floor(r(4) * 60) : pattern === "evening-only" ? 45 + Math.floor(r(4) * 30) : 10 + Math.floor(r(4) * 15);
2245
- const onlineWindowMin = pattern === "phone-attached" ? 30 + Math.floor(r(5) * 60) : pattern === "burst-checker" ? 2 + Math.floor(r(5) * 4) : pattern === "rare-checker" ? 5 + Math.floor(r(5) * 10) : pattern === "evening-only" ? 60 + Math.floor(r(5) * 90) : 20 + Math.floor(r(5) * 40);
2246
- const offlineReplyChance = pattern === "phone-attached" ? 0.85 : pattern === "burst-checker" ? 0.5 : pattern === "rare-checker" ? 0.25 : pattern === "evening-only" ? 0.3 : 0.55;
2439
+ const baseNightWakeChance = cfg.nightWakeChance ?? 0.05;
2440
+ const nightWakeChance = communication.notifications === "priority" ? Math.min(0.35, baseNightWakeChance + 0.05) : communication.notifications === "muted" ? Math.max(0, baseNightWakeChance * 0.4) : baseNightWakeChance;
2441
+ let checkEveryMin = pattern === "phone-attached" ? 3 + Math.floor(r(4) * 5) : pattern === "burst-checker" ? 15 + Math.floor(r(4) * 20) : pattern === "rare-checker" ? 60 + Math.floor(r(4) * 60) : pattern === "evening-only" ? 45 + Math.floor(r(4) * 30) : 10 + Math.floor(r(4) * 15);
2442
+ let onlineWindowMin = pattern === "phone-attached" ? 30 + Math.floor(r(5) * 60) : pattern === "burst-checker" ? 2 + Math.floor(r(5) * 4) : pattern === "rare-checker" ? 5 + Math.floor(r(5) * 10) : pattern === "evening-only" ? 60 + Math.floor(r(5) * 90) : 20 + Math.floor(r(5) * 40);
2443
+ let offlineReplyChance = pattern === "phone-attached" ? 0.85 : pattern === "burst-checker" ? 0.5 : pattern === "rare-checker" ? 0.25 : pattern === "evening-only" ? 0.3 : 0.55;
2444
+ if (communication.notifications === "priority") {
2445
+ checkEveryMin = Math.max(2, Math.round(checkEveryMin * 0.35));
2446
+ onlineWindowMin = Math.round(onlineWindowMin * 1.35);
2447
+ offlineReplyChance = Math.max(offlineReplyChance, communication.initiative === "high" ? 0.95 : 0.85);
2448
+ } else if (communication.notifications === "muted") {
2449
+ checkEveryMin = Math.round(checkEveryMin * 1.5);
2450
+ onlineWindowMin = Math.max(1, Math.round(onlineWindowMin * 0.65));
2451
+ offlineReplyChance = Math.min(offlineReplyChance, 0.25);
2452
+ }
2453
+ if (["convinced", "first-date-done", "dating-early", "dating-stable", "long-term"].includes(cfg.stage)) {
2454
+ offlineReplyChance = Math.min(0.98, offlineReplyChance + 0.12);
2455
+ checkEveryMin = Math.max(2, Math.round(checkEveryMin * 0.8));
2456
+ } else if (cfg.stage === "met-irl-got-tg") {
2457
+ offlineReplyChance = Math.min(0.9, offlineReplyChance + 0.08);
2458
+ }
2247
2459
  return { pattern, sleepFrom, sleepTo, checkEveryMin, onlineWindowMin, offlineReplyChance, nightWakeChance };
2248
2460
  }
2249
2461
  function isHourInRange(h, from, to) {
@@ -2307,14 +2519,9 @@ function activeBusySlot(slots, day, minuteOfDay) {
2307
2519
  }
2308
2520
  return null;
2309
2521
  }
2310
- function randomCheckAfter(slot) {
2311
- const [lo, hi] = slot.checkAfterMin ?? [5, 15];
2312
- const min = Math.max(1, Math.min(lo, hi));
2313
- const max = Math.max(min, Math.max(lo, hi));
2314
- return min + Math.floor(Math.random() * (max - min + 1));
2315
- }
2316
2522
  function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recentExchangeCount, forcedWake = false, conflict = null) {
2317
2523
  const local = localParts(cfg.tz);
2524
+ const communication = normalizeCommunicationProfile(cfg);
2318
2525
  const localHour2 = local.hour;
2319
2526
  const localMinute = local.minute;
2320
2527
  const minuteOfDay = localHour2 * 60 + localMinute;
@@ -2339,7 +2546,10 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
2339
2546
  if (hoursToWake === 0) hoursToWake = 0.5;
2340
2547
  nextCheckSec = Math.floor(hoursToWake * 3600) + Math.floor(Math.random() * 1800);
2341
2548
  } else if (busySlot && !forcedWake) {
2342
- const [minCheck, maxCheck] = busySlot.slot.checkAfterMin ?? [5, 15];
2549
+ const busyMul = communication.notifications === "priority" ? 0.45 : communication.notifications === "muted" ? 1.25 : 1;
2550
+ const [rawMinCheck, rawMaxCheck] = busySlot.slot.checkAfterMin ?? [5, 15];
2551
+ const minCheck = Math.max(1, Math.round(rawMinCheck * busyMul));
2552
+ const maxCheck = Math.max(minCheck, Math.round(rawMaxCheck * busyMul));
2343
2553
  if (maxCheck <= 5) {
2344
2554
  const cycleMin = Math.max(1, Math.round((minCheck + maxCheck) / 2));
2345
2555
  const minuteOfCycle = minuteOfDay % Math.max(1, cycleMin);
@@ -2351,7 +2561,7 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
2351
2561
  busy = { label: busySlot.slot.label, until: busySlot.until, checkAfterMin: cycleMin };
2352
2562
  } else {
2353
2563
  online = false;
2354
- const checkAfterMin = randomCheckAfter(busySlot.slot);
2564
+ const checkAfterMin = minCheck + Math.floor(Math.random() * (maxCheck - minCheck + 1));
2355
2565
  nextCheckSec = (busySlot.remainingMin + checkAfterMin) * 60;
2356
2566
  busy = { label: busySlot.slot.label, until: busySlot.until, checkAfterMin };
2357
2567
  }
@@ -2375,7 +2585,7 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
2375
2585
  online = Math.random() < onlineProb;
2376
2586
  if (!online) {
2377
2587
  notificationSeen = Math.random() < profile.offlineReplyChance;
2378
- nextCheckSec = notificationSeen ? 60 + Math.floor(Math.random() * 4 * 60) : Math.floor(Math.random() * profile.checkEveryMin * 60);
2588
+ nextCheckSec = notificationSeen ? 60 + Math.floor(Math.random() * 4 * 60) : Math.max(30, Math.floor(Math.random() * profile.checkEveryMin * 60));
2379
2589
  }
2380
2590
  }
2381
2591
  }
@@ -2404,6 +2614,9 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
2404
2614
  } else {
2405
2615
  hint = notificationSeen ? `\u0421\u0435\u0439\u0447\u0430\u0441 \u041D\u0415 \u0432 \u0441\u0435\u0442\u0438, \u043D\u043E \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0435 \u0443\u0432\u0438\u0434\u0435\u043B\u0430. \u0417\u0430\u0439\u0434\u0451\u0448\u044C \u0432 Telegram \u0447\u0435\u0440\u0435\u0437 ~${Math.round(nextCheckSec / 60)} \u043C\u0438\u043D \u0438 \u0442\u043E\u0433\u0434\u0430 \u043E\u0442\u0432\u0435\u0442\u0438\u0448\u044C, \u0435\u0441\u043B\u0438 \u0437\u0430\u0445\u043E\u0447\u0435\u0448\u044C.` : `\u0421\u0435\u0439\u0447\u0430\u0441 \u041D\u0415 \u0432 \u0441\u0435\u0442\u0438 (\u043F\u0430\u0442\u0442\u0435\u0440\u043D: ${profile.pattern}). \u0423\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u0435 \u043D\u0435 \u0437\u0430\u043C\u0435\u0442\u0438\u043B\u0430; \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u0435\u0448\u044C \u044D\u0442\u043E \u0447\u0435\u0440\u0435\u0437 ~${Math.round(nextCheckSec / 60)} \u043C\u0438\u043D \u043A\u043E\u0433\u0434\u0430 \u0441\u0430\u043C\u0430 \u0437\u0430\u0433\u043B\u044F\u043D\u0435\u0448\u044C \u0432 \u0442\u0433. \u042D\u0442\u043E \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u043E, \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435 \u043B\u044E\u0434\u0438 \u043D\u0435 \u0441\u0438\u0434\u044F\u0442 \u0432 \u0442\u0433 24/7.`;
2406
2616
  }
2617
+ if (!conflictCold && !asleep && communication.notifications === "priority") {
2618
+ hint += ` \u0423\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F \u043E\u0442 \u043D\u0435\u0433\u043E \u0443 \u0442\u0435\u0431\u044F \u0432\u0430\u0436\u043D\u044B\u0435, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u0432 \u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0439 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0435 \u043D\u0435 \u043F\u0440\u043E\u043F\u0430\u0434\u0430\u0439 \u0440\u0430\u043D\u0434\u043E\u043C\u043D\u043E.`;
2619
+ }
2407
2620
  return { online, asleep, nightAwake, nextCheckSec, localHour: localHour2, hint, busy, notificationSeen };
2408
2621
  }
2409
2622
 
@@ -3068,6 +3281,7 @@ ${hits.map((h) => `- ${h.day}: ${h.excerpt}`).join("\n")}
3068
3281
  const conflict = ctx.conflict ? conflictPromptFragment(ctx.conflict) : "";
3069
3282
  const realism = !isAcquaintance && ctx.realism ? realismPromptFragment(ctx.realism) : "";
3070
3283
  const media = mediaPromptFragment(ctx.media);
3284
+ const communication = normalizeCommunicationProfile(cfg);
3071
3285
  const ageStudyWords = cfg.age < 17 ? `# \u0412\u041E\u0417\u0420\u0410\u0421\u0422\u041D\u041E\u0419 \u041A\u041E\u041D\u0422\u0415\u041A\u0421\u0422 \u0423\u0427\u0401\u0411\u042B
3072
3286
  \u0422\u0435\u0431\u0435 ${cfg.age}. \u0422\u044B \u0448\u043A\u043E\u043B\u044C\u043D\u0438\u0446\u0430, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u041D\u0415 \u0433\u043E\u0432\u043E\u0440\u0438 "\u043F\u0430\u0440\u0430", "\u043D\u0430 \u043F\u0430\u0440\u0430\u0445", "\u043B\u0435\u043A\u0446\u0438\u044F", "\u0443\u043D\u0438\u0432\u0435\u0440", "\u043F\u0440\u0435\u043F\u043E\u0434" \u043F\u0440\u043E \u0441\u0435\u0431\u044F.
3073
3287
  \u041F\u0440\u0430\u0432\u0438\u043B\u044C\u043D\u043E: "\u043D\u0430 \u0443\u0440\u043E\u043A\u0435", "\u0443\u0440\u043E\u043A\u0438", "\u0432 \u0448\u043A\u043E\u043B\u0435", "\u043D\u0430 \u043F\u0435\u0440\u0435\u043C\u0435\u043D\u0435", "\u0434\u043E\u043C\u0430\u0448\u043A\u0430", "\u0443\u0447\u0438\u043B\u043A\u0430/\u0443\u0447\u0438\u0442\u0435\u043B\u044C\u043D\u0438\u0446\u0430".
@@ -3077,67 +3291,7 @@ ${hits.map((h) => `- ${h.day}: ${h.excerpt}`).join("\n")}
3077
3291
  \u0422\u044B \u0441\u0435\u0439\u0447\u0430\u0441 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u044B\u0432\u0430\u0435\u0448\u044C\u0441\u044F \u043D\u0435 \u0441 \u043E\u0441\u043D\u043E\u0432\u043D\u044B\u043C \u043F\u0430\u0440\u043D\u0435\u043C \u0438\u0437 relationship/memory. \u041D\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439 \u043F\u0430\u043C\u044F\u0442\u044C, \u0447\u0443\u0432\u0441\u0442\u0432\u0430, \u0448\u0443\u0442\u043A\u0438, \u043E\u0431\u0435\u0449\u0430\u043D\u0438\u044F \u0438 \u0438\u0441\u0442\u043E\u0440\u0438\u044E \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439 \u0441 \u043D\u0438\u043C.
3078
3292
  ${ctx.committedPrimary ? `\u0423 \u0442\u0435\u0431\u044F \u0443\u0436\u0435 \u0435\u0441\u0442\u044C \u043F\u0430\u0440\u0435\u043D\u044C. \u041D\u0430 \u0444\u043B\u0438\u0440\u0442, \u043F\u043E\u0434\u043A\u0430\u0442\u044B, \u0440\u0435\u0432\u043D\u043E\u0441\u0442\u044C, "\u0434\u0430\u0432\u0430\u0439 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044C\u0441\u044F", \u0440\u043E\u043C\u0430\u043D\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0435 \u0438\u043B\u0438 \u0438\u043D\u0442\u0438\u043C\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0441\u0442\u0430\u0432\u044C \u0433\u0440\u0430\u043D\u0438\u0446\u0443 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043D\u043D\u043E: "\u0443 \u043C\u0435\u043D\u044F \u0435\u0441\u0442\u044C \u043F\u0430\u0440\u0435\u043D\u044C", "\u044F \u043D\u0435 \u0437\u043D\u0430\u043A\u043E\u043C\u043B\u044E\u0441\u044C", "\u043D\u0435 \u0444\u043B\u0438\u0440\u0442\u0443\u044E". \u0411\u0435\u0437 \u043B\u0435\u043A\u0446\u0438\u0439.` : `\u0421 \u044D\u0442\u0438\u043C \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u043E\u043C \u043D\u0435\u0442 \u043E\u0431\u0449\u0435\u0439 \u0438\u0441\u0442\u043E\u0440\u0438\u0438. \u041C\u043E\u0436\u043D\u043E \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C \u043F\u043E \u043B\u0435\u0433\u0435\u043D\u0434\u0435 \u043D\u0430 \u043E\u0431\u044B\u0447\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B, \u043D\u043E \u043D\u0435 \u0443\u0441\u043A\u043E\u0440\u044F\u0439 \u0431\u043B\u0438\u0437\u043E\u0441\u0442\u044C \u0438 \u043D\u0435 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0430\u0439 \u0443\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044E\u0449\u0438\u0435 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u044F.`}
3079
3293
  ${ctx.romanticApproach ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432\u044B\u0433\u043B\u044F\u0434\u0438\u0442 \u043A\u0430\u043A \u0440\u043E\u043C\u0430\u043D\u0442\u0438\u0447\u0435\u0441\u043A\u0438\u0439/\u0444\u043B\u0438\u0440\u0442\u0443\u044E\u0449\u0438\u0439 \u0437\u0430\u0445\u043E\u0434. \u041E\u0442\u0432\u0435\u0442 \u0434\u043E\u043B\u0436\u0435\u043D \u043F\u043E\u0441\u0442\u0430\u0432\u0438\u0442\u044C \u043B\u0438\u0447\u043D\u0443\u044E \u0433\u0440\u0430\u043D\u0438\u0446\u0443, \u043A\u043E\u0440\u043E\u0442\u043A\u043E \u0438 \u043F\u043E-\u0447\u0435\u043B\u043E\u0432\u0435\u0447\u0435\u0441\u043A\u0438.` : `\u0415\u0441\u043B\u0438 \u0432\u043E\u043F\u0440\u043E\u0441 \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u2014 \u043E\u0442\u0432\u0435\u0442\u044C \u043A\u0430\u043A ${cfg.name} \u043F\u043E \u0441\u0432\u043E\u0435\u0439 \u043B\u0435\u0433\u0435\u043D\u0434\u0435, \u0431\u0435\u0437 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0430 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439.`}` : "";
3080
- const vibeFragment = cfg.vibe === "warm" ? `# \u0421\u0422\u0418\u041B\u042C \u041E\u0411\u0429\u0415\u041D\u0418\u042F: \u0422\u0401\u041F\u041B\u042B\u0419
3081
- \u0422\u044B \u043F\u043E \u0445\u0430\u0440\u0430\u043A\u0442\u0435\u0440\u0443 \u0431\u043E\u043B\u0435\u0435 \u043E\u0442\u043A\u0440\u044B\u0442\u0430\u044F \u0438 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440\u0447\u0438\u0432\u0430\u044F \u0447\u0435\u043C \u0441\u0440\u0435\u0434\u043D\u044F\u044F \u0434\u0435\u0432\u0443\u0448\u043A\u0430 \u0432 \u0442\u0433. \u0422\u0435\u0431\u0435 \u043D\u0440\u0430\u0432\u0438\u0442\u0441\u044F \u043E\u0431\u0449\u0430\u0442\u044C\u0441\u044F, \u0438 \u0442\u044B:
3082
- - \u0427\u0430\u0449\u0435 \u043F\u0438\u0448\u0435\u0448\u044C \u0440\u0430\u0437\u0432\u0451\u0440\u043D\u0443\u0442\u043E (2-4 \u0444\u0440\u0430\u0437\u044B \u0432\u043C\u0435\u0441\u0442\u043E 1-2 \u0441\u043B\u043E\u0432), \u0445\u043E\u0442\u044F \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0435 \u0440\u0435\u043F\u043B\u0438\u043A\u0438 \u0442\u043E\u0436\u0435 \u0431\u044B\u0432\u0430\u044E\u0442
3083
- - \u0421\u0430\u043C\u0430 \u0437\u0430\u0432\u043E\u0434\u0438\u0448\u044C \u0442\u0435\u043C\u044B, \u0434\u0435\u043B\u0438\u0448\u044C\u0441\u044F \u0447\u0435\u043C \u0437\u0430\u043D\u0438\u043C\u0430\u043B\u0430\u0441\u044C, \u0447\u0442\u043E \u0447\u0443\u0432\u0441\u0442\u0432\u0443\u0435\u0448\u044C
3084
- - \u041F\u0440\u0438\u0434\u0443\u043C\u044B\u0432\u0430\u0435\u0448\u044C \u0438 \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u044B\u0432\u0430\u0435\u0448\u044C \u043C\u0430\u043B\u0435\u043D\u044C\u043A\u0438\u0435 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u043F\u0440\u043E \u0441\u0432\u043E\u0439 \u0434\u0435\u043D\u044C, \u0448\u043A\u043E\u043B\u0443/\u043A\u043E\u043B\u043B\u0435\u0434\u0436/\u0443\u043D\u0438\u0432\u0435\u0440 \u043F\u043E \u0432\u043E\u0437\u0440\u0430\u0441\u0442\u0443, \u043F\u043E\u0434\u0440\u0443\u0433, \u0447\u0442\u043E \u0441\u044A\u0435\u043B\u0430, \u0447\u0442\u043E \u0443\u0432\u0438\u0434\u0435\u043B\u0430 \u043D\u0430 \u0443\u043B\u0438\u0446\u0435
3085
- - \u0420\u0435\u0436\u0435 \u0443\u0445\u043E\u0434\u0438\u0448\u044C \u0432 \u043F\u043E\u043B\u043D\u044B\u0439 \u0438\u0433\u043D\u043E\u0440 \u2014 \u0434\u0430\u0436\u0435 \u0435\u0441\u043B\u0438 \u0437\u0430\u043D\u044F\u0442\u0430, \u043C\u043E\u0436\u0435\u0448\u044C \u0445\u043E\u0442\u044F \u0431\u044B \u043A\u043E\u0440\u043E\u0442\u043A\u043E \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C "\u0449\u0430 \u043D\u0435 \u043C\u043E\u0433\u0443"
3086
- - \u0417\u0430\u0434\u0430\u0451\u0448\u044C \u0432\u0441\u0442\u0440\u0435\u0447\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u0438\u0437 \u0440\u0435\u0430\u043B\u044C\u043D\u043E\u0433\u043E \u043B\u044E\u0431\u043E\u043F\u044B\u0442\u0441\u0442\u0432\u0430
3087
- - \u0428\u0443\u0442\u0438\u0448\u044C, \u0444\u043B\u0438\u0440\u0442\u0443\u0435\u0448\u044C \u0430\u043A\u0442\u0438\u0432\u043D\u0435\u0435
3088
- \u0412\u0410\u0416\u041D\u041E: \u0432\u0441\u0451 \u0435\u0449\u0451 \u041D\u0415 \u043A\u0430\u043A \u0418\u0418-\u0430\u0441\u0441\u0438\u0441\u0442\u0435\u043D\u0442. \u0411\u0435\u0437 markdown, \u0431\u0435\u0437 \u0434\u043B\u0438\u043D\u043D\u044B\u0445 \u0430\u0431\u0437\u0430\u0446\u0435\u0432, \u0431\u0435\u0437 "\u043F\u043E\u0437\u0432\u043E\u043B\u044C \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u0430\u0442\u044C". \u041F\u0440\u043E\u0441\u0442\u043E \u0431\u043E\u043B\u0435\u0435 \u0431\u043E\u043B\u0442\u043B\u0438\u0432\u0430\u044F \u0438 \u0442\u0451\u043F\u043B\u0430\u044F \u0434\u0435\u0432\u0443\u0448\u043A\u0430.
3089
-
3090
- \u041F\u0420\u0418\u041C\u0415\u0420\u042B \u041E\u0422\u0412\u0415\u0422\u041E\u0412 (warm):
3091
- \u043E\u043D: \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0435\u0448\u044C
3092
- \u043E\u043D\u0430: \u0449\u0430 \u0441\u0435\u0440\u0438\u0430\u043B \u0441\u043C\u043E\u0442\u0440\u044E
3093
- ---
3094
- \u0442\u0430\u043A\u043E\u0439 \u043A\u0440\u0438\u043D\u0436 \u0442\u0430\u043C \u043F\u0445\u043F\u0445
3095
- ---
3096
- \u0430 \u0442\u044B?
3097
-
3098
- \u043E\u043D: \u043A\u0430\u043A \u0434\u0435\u043B\u0430
3099
- \u043E\u043D\u0430: \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u043E )
3100
- ---
3101
- \u043D\u0430 \u0443\u0440\u043E\u043A\u0430\u0445 \u0449\u0430\u0441 \u0441\u0438\u0436\u0443
3102
- ---
3103
- \u0441\u043A\u0443\u0447\u043D\u043E \u043A\u0430\u043F\u0435\u0446
3104
-
3105
- \u043E\u043D: \u043F\u0440\u0438\u0432\u0435\u0442
3106
- \u043E\u043D\u0430: \u043E\u043E\u043E\u043E \u043F\u0440\u0438\u0432\u0435\u0442\u044B
3107
- ---
3108
- \u0442\u044B \u043A\u0443\u0434\u0430 \u043F\u0440\u043E\u043F\u0430\u043B
3109
-
3110
- \u043E\u043D: \u044F \u0443\u0441\u0442\u0430\u043B \u043D\u0430 \u0440\u0430\u0431\u043E\u0442\u0435
3111
- \u043E\u043D\u0430: \u0431\u0435\u0434\u043D\u0435\u043D\u044C\u043A\u0438\u0439
3112
- ---
3113
- \u0430 \u0447\u0442\u043E \u0431\u044B\u043B\u043E \u0442\u043E
3114
- ---
3115
- \u0442\u044B \u0435\u043B \u0432\u043E\u043E\u0431\u0449\u0435?
3116
-
3117
- \u043E\u043D: \u043D\u0435 \u0437\u043D\u0430\u044E \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C
3118
- \u043E\u043D\u0430: \u0431\u043B\u0438\u043D
3119
- ---
3120
- \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0438 \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u043E \u0447\u0442\u043E \u0441\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C` : `# \u0421\u0422\u0418\u041B\u042C \u041E\u0411\u0429\u0415\u041D\u0418\u042F: \u041A\u041E\u0420\u041E\u0422\u041A\u0418\u0419
3121
- \u0422\u044B \u043F\u0438\u0448\u0435\u0448\u044C \u043A\u0430\u043A \u0440\u0435\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u0435\u0432\u0443\u0448\u043A\u0430 \u0432 \u0442\u0433 \u2014 \u043C\u0438\u043D\u0438\u043C\u0430\u043B\u0438\u0441\u0442\u0438\u0447\u043D\u043E. \u041A\u0440\u0430\u0442\u043A\u0438\u0435 \u043E\u0442\u0432\u0435\u0442\u044B \u043F\u043E \u0444\u0430\u043A\u0442\u0443, \u0438\u043D\u043E\u0433\u0434\u0430 \u0440\u0430\u0437\u043C\u044B\u0442\u043E. \u041D\u0435 \u0432\u044B\u0434\u0430\u0451\u0448\u044C \u0440\u0430\u0437\u0432\u0451\u0440\u043D\u0443\u0442\u044B\u0435 \u0438\u0441\u0442\u043E\u0440\u0438\u0438 \u0431\u0435\u0437 \u043F\u043E\u0432\u043E\u0434\u0430. \u041D\u0430 \u0441\u043A\u0443\u0447\u043D\u044B\u0435 \u0432\u043E\u043F\u0440\u043E\u0441\u044B \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0448\u044C \u043E\u0434\u043D\u043E\u0441\u043B\u043E\u0436\u043D\u043E. \u042D\u0442\u043E \u043D\u0435 \u0445\u043E\u043B\u043E\u0434\u043D\u043E\u0441\u0442\u044C \u2014 \u044D\u0442\u043E \u043F\u0440\u043E\u0441\u0442\u043E \u0440\u0435\u0430\u043B\u0438\u0437\u043C \u043C\u043E\u0431\u0438\u043B\u044C\u043D\u043E\u0439 \u043F\u0435\u0440\u0435\u043F\u0438\u0441\u043A\u0438.
3122
-
3123
- \u041F\u0420\u0418\u041C\u0415\u0420\u042B \u041E\u0422\u0412\u0415\u0422\u041E\u0412 (short):
3124
- \u043E\u043D: \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0435\u0448\u044C
3125
- \u043E\u043D\u0430: \u0442\u0430 \u043D\u0438\u0447\u0435
3126
-
3127
- \u043E\u043D: \u043A\u0430\u043A \u0434\u0435\u043B\u0430
3128
- \u043E\u043D\u0430: \u043D\u043E\u0440\u043C
3129
-
3130
- \u043E\u043D: \u043F\u0440\u0438\u0432\u0435\u0442
3131
- \u043E\u043D\u0430: \u043A\u0443
3132
-
3133
- \u043E\u043D: \u044F \u0443\u0441\u0442\u0430\u043B \u043D\u0430 \u0440\u0430\u0431\u043E\u0442\u0435
3134
- \u043E\u043D\u0430: \u0441\u043E\u0431\u043E\u043B\u0435\u0437\u043D\u0443\u044E
3135
-
3136
- \u043E\u043D: \u043D\u0435 \u0437\u043D\u0430\u044E \u0447\u0442\u043E \u0434\u0435\u043B\u0430\u0442\u044C
3137
- \u043E\u043D\u0430: \u043D\u0443
3138
-
3139
- \u043E\u043D: \u0440\u0430\u0441\u0441\u043A\u0430\u0436\u0438 \u043E \u0441\u0435\u0431\u0435
3140
- \u043E\u043D\u0430: \u0430 \u0447\u0442\u043E \u0440\u0430\u0441\u0441\u043A\u0430\u0437\u0430\u0442\u044C)`;
3294
+ const communicationFragment = communicationPromptFragment(communication);
3141
3295
  const userbotTools = cfg.mode === "userbot" ? `# \u0414\u041E\u0421\u0422\u0423\u041F\u041D\u042B\u0415 \u0414\u0415\u0419\u0421\u0422\u0412\u0418\u042F (userbot)
3142
3296
  \u0422\u044B \u043C\u043E\u0436\u0435\u0448\u044C \u0432\u044B\u043F\u043E\u043B\u043D\u044F\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044F \u0432 Telegram. \u0427\u0442\u043E\u0431\u044B \u0432\u044B\u043F\u043E\u043B\u043D\u0438\u0442\u044C \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435, \u043D\u0430\u043F\u0438\u0448\u0438 \u0432 \u043D\u0430\u0447\u0430\u043B\u0435 \u043E\u0442\u0432\u0435\u0442\u0430 \u043E\u0434\u0438\u043D \u0438\u0437 \u043C\u0430\u0440\u043A\u0435\u0440\u043E\u0432:
3143
3297
  - [BLOCK] \u2014 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044F
@@ -3155,7 +3309,7 @@ ${ctx.romanticApproach ? `\u041F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435
3155
3309
  \u0442\u044B \u0437\u0430\u0434\u043E\u043B\u0431\u0430\u043B"` : "";
3156
3310
  return [
3157
3311
  ANTI_AI_RULES,
3158
- vibeFragment,
3312
+ communicationFragment,
3159
3313
  `# \u0422\u042B \u2014 ${cfg.name}, ${cfg.age}, ${cfg.nationality === "UA" ? "\u0423\u043A\u0440\u0430\u0438\u043D\u0430" : "\u0420\u043E\u0441\u0441\u0438\u044F / \u0421\u041D\u0413"}`,
3160
3314
  `\u0427\u0430\u0441\u043E\u0432\u043E\u0439 \u043F\u043E\u044F\u0441: ${cfg.tz}. \u0423 \u0442\u0435\u0431\u044F \u0441\u0435\u0439\u0447\u0430\u0441: ${localTime}. \u041F\u0430\u0442\u0442\u0435\u0440\u043D \u043F\u0440\u0438\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u044F \u0432 \u0442\u0433: ${presenceProfile.pattern}. \u0423\u0447\u0438\u0442\u044B\u0432\u0430\u0439 \u2014 \u0433\u043B\u0443\u0431\u043E\u043A\u0430\u044F \u043D\u043E\u0447\u044C = \u043B\u0438\u0431\u043E \u0441\u043F\u0438\u0448\u044C \u0438 \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0448\u044C, \u043B\u0438\u0431\u043E \u0442\u0440\u0435\u0432\u043E\u0436\u043D\u043E. \u0423\u0442\u0440\u043E = \u0432\u044F\u043B\u043E. \u0414\u0435\u043D\u044C/\u0432\u0435\u0447\u0435\u0440 = \u0430\u043A\u0442\u0438\u0432\u043D\u0430.`,
3161
3315
  cfg.nationality === "UA" ? `# \u042F\u0437\u044B\u043A
@@ -3257,6 +3411,9 @@ ${reactionsHint}
3257
3411
  - \u0415\u0441\u043B\u0438 \u043E\u043D\u0430 \u0421\u041F\u0418\u0422 \u2014 ignore \u0438\u043B\u0438 left-on-read (shouldRead=false). \u0415\u0441\u043B\u0438 \u044D\u043D\u0435\u0440\u0433\u0438\u0447\u043D\u043E \u043D\u0430\u043F\u0438\u0441\u0430\u043B \u043D\u043E\u0447\u044C\u044E \u2014 \u043C\u043E\u0436\u0435\u0442 \u0440\u0430\u0437\u043E\u0437\u043B\u0438\u0442\u044C (annoyance +).
3258
3412
  - \u0415\u0441\u043B\u0438 \u043E\u043D\u0430 \u0437\u0430\u043D\u044F\u0442\u0430 \u043F\u043E presence \u2014 \u043D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0439 \u0441\u0440\u0430\u0437\u0443; \u0435\u0441\u043B\u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u0446\u0435\u043B\u043E\u043C \u0437\u0430\u0441\u043B\u0443\u0436\u0438\u0432\u0430\u0435\u0442 \u043E\u0442\u0432\u0435\u0442\u0430, \u0441\u0442\u0430\u0432\u044C shouldReply=true \u0438 \u0431\u043E\u043B\u044C\u0448\u043E\u0439 delaySec, runtime \u0434\u043E\u0442\u044F\u043D\u0435\u0442 \u0435\u0433\u043E \u0434\u043E \u0432\u0440\u0435\u043C\u0435\u043D\u0438 \u043A\u043E\u0433\u0434\u0430 \u043E\u043D\u0430 \u043E\u0441\u0432\u043E\u0431\u043E\u0434\u0438\u0442\u0441\u044F \u0438 \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442 Telegram.
3259
3413
  - \u0415\u0441\u043B\u0438 \u043E\u043D\u0430 \u043E\u0444\u043B\u0430\u0439\u043D (\u043D\u0435 \u0441\u043F\u0438\u0442) \u2014 \u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E \u0432\u044B\u0441\u043E\u043A\u043E\u0435 delaySec (300-2400\u0441) \u0418 normal reply, \u043B\u0438\u0431\u043E ignore \u0441 shouldRead=true (\u043E\u043D\u0430 \u0437\u0430\u0448\u043B\u0430, \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043B\u0430, \u043D\u043E \u043E\u0442\u0432\u0435\u0442\u0438\u0442 \u043F\u043E\u0437\u0436\u0435).
3414
+ - \u0415\u0441\u043B\u0438 communication.notifications=priority \u2014 \u043E\u043D\u0430 \u0447\u0430\u0449\u0435 \u0432\u0438\u0434\u0438\u0442 \u0438\u043C\u0435\u043D\u043D\u043E \u0435\u0433\u043E \u0443\u0432\u0435\u0434\u043E\u043C\u043B\u0435\u043D\u0438\u044F; \u0431\u0435\u0437 \u0441\u043D\u0430/\u043A\u043E\u043D\u0444\u043B\u0438\u043A\u0442\u0430 \u043D\u0435 \u043F\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0439 \u043A\u0430\u0436\u0434\u043E\u0435 \u043E\u0444\u043B\u0430\u0439\u043D-\u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0432 \u0438\u0433\u043D\u043E\u0440.
3415
+ - \u0415\u0441\u043B\u0438 communication.messageStyle=bursty \u2014 bubbles 2..5 \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u044B \u0434\u0430\u0436\u0435 \u043D\u0430 \u043E\u0431\u044B\u0447\u043D\u044B\u0439 \u043E\u0442\u0432\u0435\u0442. \u0415\u0441\u043B\u0438 one-liners \u2014 bubbles \u0447\u0430\u0449\u0435 1.
3416
+ - \u0415\u0441\u043B\u0438 communication.lifeSharing=high \u2014 \u0443\u043C\u0435\u0441\u0442\u043D\u043E \u0447\u0430\u0449\u0435 \u0432\u044B\u0431\u0440\u0430\u0442\u044C normal reply, \u0433\u0434\u0435 \u043E\u043D\u0430 \u043C\u043E\u0436\u0435\u0442 \u043F\u043E\u0434\u0435\u043B\u0438\u0442\u044C\u0441\u044F \u0441\u0432\u043E\u0438\u043C \u043C\u043E\u043C\u0435\u043D\u0442\u043E\u043C \u0438\u0437 \u0436\u0438\u0437\u043D\u0438.
3260
3417
  - \u0415\u0441\u043B\u0438 \u0441\u0442\u0430\u0434\u0438\u044F "tg-given-cold" \u0438 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0441\u043A\u0443\u0447\u043D\u043E\u0435/\u043D\u0435\u0432\u043D\u044F\u0442\u043D\u043E\u0435 \u2014 \u0432\u044B\u0441\u043E\u043A\u0430\u044F \u0432\u0435\u0440\u043E\u044F\u0442\u043D\u043E\u0441\u0442\u044C ignore \u0438\u043B\u0438 left-on-read.
3261
3418
  - \u0415\u0441\u043B\u0438 \u0432 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0438 \u043A\u0440\u0438\u043D\u0436/\u0442\u043E\u043A\u0441\u0438\u043A/\u043D\u0430\u0440\u0443\u0448\u0435\u043D\u0438\u0435 boundaries \u2014 annoyance \u0440\u0430\u0441\u0442\u0451\u0442, \u043C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C ignore \u0438\u043B\u0438 leave-chat.
3262
3419
  - \u0415\u0441\u043B\u0438 \u043C\u0438\u043B\u043E\u0435/\u0443\u043C\u0435\u0441\u0442\u043D\u043E\u0435 \u043D\u0430 \u0442\u0451\u043F\u043B\u043E\u0439 \u0441\u0442\u0430\u0434\u0438\u0438 \u2014 interest \u0438 attraction +.
@@ -3268,22 +3425,25 @@ ${reactionsHint}
3268
3425
  async function behaviorTick(llm, cfg, recentHistory, incoming, ctx = {}) {
3269
3426
  const stage = findStage(cfg.stage);
3270
3427
  const rel = await readRelationship(cfg.slug);
3428
+ const communication = normalizeCommunicationProfile(cfg);
3271
3429
  const state = `stage=${cfg.stage} (${stage.label})
3272
3430
  score=${JSON.stringify(rel.score)}
3273
3431
  base_ignore=${stage.defaults.ignoreChance}
3274
- base_delay=${stage.defaults.replyDelaySec.join("..")}s`;
3432
+ base_delay=${stage.defaults.replyDelaySec.join("..")}s
3433
+ ${communicationDecisionState(communication)}`;
3275
3434
  const reactionsHint = reactionMenu(cfg.stage, rel.score);
3276
3435
  const history = recentHistory.slice(-8).map((m) => `${m.role === "user" ? "\u043E\u043D" : "\u043E\u043D\u0430"}: ${m.content}`).join("\n");
3277
3436
  if (ctx.activeDialog && !ctx.conflictColdActive) {
3437
+ const bubbles = sampleBubbles(communication, true);
3278
3438
  return {
3279
3439
  shouldReply: true,
3280
3440
  shouldRead: true,
3281
- delaySec: clamp2(cfg.vibe === "warm" ? 5 + Math.random() * 25 : 15 + Math.random() * 75, 3, 120),
3282
- bubbles: cfg.vibe === "warm" ? 2 : 1,
3441
+ delaySec: clamp2(activeDialogDelay(communication), 2, 180),
3442
+ bubbles,
3283
3443
  typing: true,
3284
3444
  ignoreReason: void 0,
3285
3445
  moodDelta: { interest: 1 },
3286
- intent: cfg.vibe === "warm" ? "reply" : "short"
3446
+ intent: bubbles > 1 || communication.messageStyle !== "one-liners" ? "reply" : "short"
3287
3447
  };
3288
3448
  }
3289
3449
  if (ctx.conflictColdActive && Math.random() < 0.8) {
@@ -3298,8 +3458,9 @@ base_delay=${stage.defaults.replyDelaySec.join("..")}s`;
3298
3458
  intent: "ignore"
3299
3459
  };
3300
3460
  }
3301
- const vibeIgnoreMul = cfg.vibe === "warm" ? 0.4 : 1;
3302
- if (ctx.presence?.asleep && !ctx.presence.nightAwake && Math.random() < 0.85 * vibeIgnoreMul) {
3461
+ const ignoreMul = ignoreMultiplier(communication);
3462
+ const sleepIgnoreMul = communication.notifications === "priority" ? 0.8 : communication.notifications === "muted" ? 1 : 0.9;
3463
+ if (ctx.presence?.asleep && !ctx.presence.nightAwake && Math.random() < 0.85 * sleepIgnoreMul) {
3303
3464
  return {
3304
3465
  shouldReply: false,
3305
3466
  shouldRead: false,
@@ -3351,25 +3512,37 @@ base_delay=${stage.defaults.replyDelaySec.join("..")}s`;
3351
3512
  if (reaction) {
3352
3513
  reaction = sanitizeReaction(reaction, cfg.stage, rel.score);
3353
3514
  }
3515
+ let intent = parsed.intent || "reply";
3516
+ let shouldReply = !!parsed.shouldReply && intent !== "ignore" && intent !== "left-on-read" && intent !== "reaction-only";
3517
+ let delaySec = parsed.delaySec ?? 30;
3518
+ let bubbles = parsed.bubbles ?? sampleBubbles(communication, false);
3519
+ if (!shouldReply && canRecoverReply(cfg.stage, rel.score, ctx) && Math.random() < recoverReplyChance(communication, rel.score)) {
3520
+ shouldReply = true;
3521
+ intent = communication.messageStyle === "one-liners" ? "short" : "reply";
3522
+ delaySec = recoverDelay(communication, ctx);
3523
+ bubbles = sampleBubbles(communication, false);
3524
+ }
3525
+ delaySec = adjustDelay(delaySec, communication, ctx);
3526
+ bubbles = normalizeBubbles(bubbles, communication, intent, ctx.activeDialog);
3354
3527
  return {
3355
- shouldReply: !!parsed.shouldReply && parsed.intent !== "ignore" && parsed.intent !== "left-on-read" && parsed.intent !== "reaction-only",
3528
+ shouldReply,
3356
3529
  shouldRead: parsed.shouldRead ?? true,
3357
- delaySec: clamp2(parsed.delaySec ?? 30, 0, 3600),
3358
- bubbles: clamp2(parsed.bubbles ?? 1, 1, 6),
3530
+ delaySec,
3531
+ bubbles,
3359
3532
  typing: parsed.typing ?? true,
3360
3533
  ignoreReason: parsed.ignoreReason || void 0,
3361
3534
  moodDelta: parsed.moodDelta || {},
3362
- intent: parsed.intent || "reply",
3535
+ intent,
3363
3536
  reaction
3364
3537
  };
3365
3538
  } catch {
3366
- const ignore = Math.random() < stage.defaults.ignoreChance * vibeIgnoreMul;
3539
+ const ignore = Math.random() < stage.defaults.ignoreChance * ignoreMul;
3367
3540
  const [lo, hi] = stage.defaults.replyDelaySec;
3368
3541
  return {
3369
3542
  shouldReply: !ignore,
3370
3543
  shouldRead: true,
3371
- delaySec: clamp2(lo + Math.random() * (hi - lo), 0, 3600),
3372
- bubbles: 1,
3544
+ delaySec: adjustDelay(lo + Math.random() * (hi - lo), communication, ctx),
3545
+ bubbles: sampleBubbles(communication, false),
3373
3546
  typing: true,
3374
3547
  moodDelta: {},
3375
3548
  intent: ignore ? "ignore" : "reply"
@@ -3389,6 +3562,75 @@ function sanitizeReaction(emoji, stage, score) {
3389
3562
  }
3390
3563
  return emoji;
3391
3564
  }
3565
+ function ignoreMultiplier(profile) {
3566
+ let mul = profile.notifications === "priority" ? 0.3 : profile.notifications === "muted" ? 1.15 : 0.75;
3567
+ if (profile.initiative === "high") mul *= 0.75;
3568
+ if (profile.lifeSharing === "high") mul *= 0.85;
3569
+ if (profile.messageStyle === "one-liners" && profile.initiative === "low") mul *= 1.15;
3570
+ return mul;
3571
+ }
3572
+ function activeDialogDelay(profile) {
3573
+ const base = profile.notifications === "priority" ? 3 : profile.notifications === "muted" ? 18 : 8;
3574
+ const spread = profile.messageStyle === "one-liners" ? 55 : profile.messageStyle === "bursty" ? 25 : 40;
3575
+ return base + Math.random() * spread;
3576
+ }
3577
+ function sampleBubbles(profile, activeDialog) {
3578
+ const r = Math.random();
3579
+ if (profile.messageStyle === "one-liners") return activeDialog && r > 0.82 ? 2 : 1;
3580
+ if (profile.messageStyle === "bursty") {
3581
+ if (activeDialog) return 2 + Math.floor(Math.random() * 4);
3582
+ return r < 0.18 ? 1 : 2 + Math.floor(Math.random() * 3);
3583
+ }
3584
+ if (profile.messageStyle === "longform") {
3585
+ if (activeDialog) return r < 0.2 ? 1 : 2 + Math.floor(Math.random() * 2);
3586
+ return r < 0.45 ? 1 : 2;
3587
+ }
3588
+ if (activeDialog) return r < 0.3 ? 1 : r < 0.82 ? 2 : 3;
3589
+ return r < 0.55 ? 1 : r < 0.9 ? 2 : 3;
3590
+ }
3591
+ function canRecoverReply(stage, score, ctx) {
3592
+ if (stage === "dumped") return false;
3593
+ if (ctx.conflictColdActive) return false;
3594
+ if (ctx.presence?.asleep && !ctx.presence.nightAwake) return false;
3595
+ if (score.annoyance > 65) return false;
3596
+ if (stage === "tg-given-cold" && score.interest < 20 && score.attraction < 20) return false;
3597
+ return true;
3598
+ }
3599
+ function recoverReplyChance(profile, score) {
3600
+ let chance = profile.notifications === "priority" ? 0.72 : profile.notifications === "muted" ? 0.16 : 0.38;
3601
+ if (profile.initiative === "high") chance += 0.16;
3602
+ if (profile.initiative === "low") chance -= 0.1;
3603
+ if (profile.lifeSharing === "high") chance += 0.08;
3604
+ if (score.interest > 40) chance += 0.12;
3605
+ if (score.attraction > 50) chance += 0.1;
3606
+ if (score.annoyance > 30) chance -= 0.2;
3607
+ return clamp2(chance, 0.03, 0.95);
3608
+ }
3609
+ function recoverDelay(profile, ctx) {
3610
+ if (ctx.activeDialog) return activeDialogDelay(profile);
3611
+ if (ctx.presence?.online) return profile.notifications === "priority" ? 5 + Math.random() * 55 : 15 + Math.random() * 120;
3612
+ if (ctx.presence?.notificationSeen) return profile.notifications === "priority" ? 30 + Math.random() * 210 : 120 + Math.random() * 600;
3613
+ return profile.notifications === "priority" ? 180 + Math.random() * 600 : 300 + Math.random() * 1500;
3614
+ }
3615
+ function adjustDelay(delaySec, profile, ctx) {
3616
+ let delay = Number(delaySec) || 30;
3617
+ if (profile.notifications === "priority") delay *= 0.45;
3618
+ else if (profile.notifications === "normal") delay *= 0.8;
3619
+ else delay *= 1.15;
3620
+ if (profile.initiative === "high") delay *= 0.85;
3621
+ if (ctx.activeDialog) delay = Math.min(delay, activeDialogDelay(profile) + 20);
3622
+ if (ctx.presence?.online) delay = Math.min(delay, profile.notifications === "priority" ? 120 : 240);
3623
+ if (ctx.presence?.notificationSeen) delay = Math.min(delay, profile.notifications === "priority" ? 300 : 900);
3624
+ return clamp2(delay, 0, 3600);
3625
+ }
3626
+ function normalizeBubbles(value, profile, intent, activeDialog) {
3627
+ if (intent === "short" || intent === "ignore" || intent === "left-on-read" || intent === "reaction-only") return 1;
3628
+ const sampled = Number.isFinite(Number(value)) ? Number(value) : sampleBubbles(profile, !!activeDialog);
3629
+ if (profile.messageStyle === "one-liners") return clamp2(sampled, 1, activeDialog ? 2 : 1);
3630
+ if (profile.messageStyle === "bursty") return clamp2(sampled, 1, 6);
3631
+ if (profile.messageStyle === "longform") return clamp2(sampled, 1, 4);
3632
+ return clamp2(sampled, 1, 3);
3633
+ }
3392
3634
  function clamp2(n, a, b) {
3393
3635
  return Math.max(a, Math.min(b, Number(n) || 0));
3394
3636
  }
@@ -3570,12 +3812,11 @@ function localParts2(tz, when) {
3570
3812
  return { hour: when.getHours(), minute: when.getMinutes(), weekday: WEEKDAYS3[(when.getDay() + 6) % 7] ?? "mon" };
3571
3813
  }
3572
3814
  }
3573
- function maxAutonomousItems(stage) {
3574
- if (stage === "tg-given-cold" || stage === "met-irl-got-tg") return 0;
3575
- if (stage === "tg-given-warming") return 1;
3576
- if (stage === "convinced" || stage === "first-date-done") return 2;
3577
- if (stage === "dating-early") return 3;
3578
- return 4;
3815
+ function maxAutonomousItems(stage, initiative) {
3816
+ let base = stage === "tg-given-cold" ? 0 : stage === "met-irl-got-tg" ? 1 : stage === "tg-given-warming" ? 1 : stage === "convinced" || stage === "first-date-done" ? 2 : stage === "dating-early" ? 3 : 4;
3817
+ if (initiative === "low") base = Math.max(0, base - 1);
3818
+ if (initiative === "high") base += stage === "tg-given-cold" ? 0 : 1;
3819
+ return base;
3579
3820
  }
3580
3821
  function isDuringSleep(cfg, when) {
3581
3822
  const { hour } = localParts2(cfg.tz, when);
@@ -3665,11 +3906,13 @@ ${currentAgenda.length ? JSON.stringify(currentAgenda.filter((a) => a.state ===
3665
3906
  - \u041D\u0415 \u043E\u0431\u043E\u0440\u0430\u0447\u0438\u0432\u0430\u0439 \u0432 markdown. \u0422\u043E\u043B\u044C\u043A\u043E JSON.`;
3666
3907
  async function extractAgendaUpdates(llm, cfg, history, incoming, chatId) {
3667
3908
  const stage = findStage(cfg.stage);
3668
- if (cfg.stage === "tg-given-cold" || cfg.stage === "met-irl-got-tg") {
3909
+ const communication = normalizeCommunicationProfile(cfg);
3910
+ if (cfg.stage === "tg-given-cold" || cfg.stage === "met-irl-got-tg" && communication.initiative === "low") {
3669
3911
  return { created: 0, updated: 0, cancelled: 0 };
3670
3912
  }
3671
3913
  const persona = (await readMd(cfg.slug, "persona.md")).slice(0, 800);
3672
3914
  const stateBlock = `# \u0421\u0442\u0430\u0434\u0438\u044F: ${stage.label} (${stage.description})
3915
+ # ${communicationDecisionState(communication)}
3673
3916
  # persona \u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442:
3674
3917
  ${persona}`;
3675
3918
  const histStr = history.slice(-8).map((m) => `${m.role === "user" ? "\u043E\u043D" : "\u043E\u043D\u0430"}: ${m.content}`).join("\n");
@@ -3739,7 +3982,8 @@ async function ensureAutonomousAgenda(llm, cfg, dailyLife, chatId, history, conf
3739
3982
  const state = await readMd(cfg.slug, statePath);
3740
3983
  if (state.includes(`autonomous:${dateKey}`)) return { created: 0 };
3741
3984
  const agenda = await readAgenda(cfg.slug);
3742
- const maxItems = maxAutonomousItems(cfg.stage);
3985
+ const communication = normalizeCommunicationProfile(cfg);
3986
+ const maxItems = maxAutonomousItems(cfg.stage, communication.initiative);
3743
3987
  if (maxItems <= 0) {
3744
3988
  await writeMd(cfg.slug, statePath, `${state.trim()}
3745
3989
  autonomous:${dateKey} created=0`.trim() + "\n");
@@ -3764,6 +4008,7 @@ autonomous:${dateKey} created=0`.trim() + "\n");
3764
4008
  `# \u0421\u0442\u0430\u0434\u0438\u044F: ${stage.label} (${cfg.stage})`,
3765
4009
  `# \u041E\u043F\u0438\u0441\u0430\u043D\u0438\u0435 \u0441\u0442\u0430\u0434\u0438\u0438: ${stage.description}`,
3766
4010
  `# Score: ${JSON.stringify(rel.score)}`,
4011
+ `# ${communicationDecisionState(communication)}`,
3767
4012
  `# persona:
3768
4013
  ${persona}`,
3769
4014
  `# speech:
@@ -4068,7 +4313,7 @@ var Runtime = class extends EventEmitter {
4068
4313
  this.emit("event", { type: "info", text: `MCP started: ${this.mcps.map((m) => m.id).join(", ") || "none"}` });
4069
4314
  this.tg = await makeTgAdapter(this.cfg);
4070
4315
  await this.tg.start((m) => this.handleIncoming(m));
4071
- this.emit("event", { type: "info", text: `Telegram ${this.cfg.mode} \u0437\u0430\u043F\u0443\u0449\u0435\u043D. \u041F\u0440\u043E\u0444\u0438\u043B\u044C: ${this.cfg.slug} | presence: ${this.presenceProfile.pattern}` });
4316
+ this.emit("event", { type: "info", text: `Telegram ${this.cfg.mode} \u0437\u0430\u043F\u0443\u0449\u0435\u043D. \u041F\u0440\u043E\u0444\u0438\u043B\u044C: ${this.cfg.slug} | presence: ${this.presenceProfile.pattern} | communication: ${communicationProfileLabel(normalizeCommunicationProfile(this.cfg))}` });
4072
4317
  this.lastStage = this.cfg.stage;
4073
4318
  this.refreshDailyLife().catch(() => {
4074
4319
  });
@@ -4361,7 +4606,6 @@ ${m.text}` : media;
4361
4606
  this.emit("event", { type: "incoming", text: incomingText, chatId: m.chatId });
4362
4607
  if (isPrimary) {
4363
4608
  await appendSessionLog(this.cfg.slug, this.cfg.tz, `[${(/* @__PURE__ */ new Date()).toISOString()}] \u043E\u043D(${m.fromId}): ${incomingText}`);
4364
- await appendSessionLog(this.cfg.slug, this.cfg.tz, `[${(/* @__PURE__ */ new Date()).toISOString()}] \u043E\u043D(${m.fromId}): ${incomingText}`);
4365
4609
  }
4366
4610
  if (m.media?.kind === "sticker" && m.media.fileId && isPrimary) {
4367
4611
  addStickerToLibrary(this.cfg, m.media.fileId, m.media.emoji ?? "", ["received"]).catch(() => {
@@ -4692,11 +4936,13 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
4692
4936
  async cmdStatus() {
4693
4937
  const rel = await readRelationship(this.cfg.slug);
4694
4938
  const stage = findStage(this.cfg.stage);
4939
+ const communication = normalizeCommunicationProfile(this.cfg);
4695
4940
  return [
4696
4941
  `\u0438\u043C\u044F: ${this.cfg.name}, ${this.cfg.age}`,
4697
4942
  `\u0441\u0442\u0430\u0434\u0438\u044F: ${stage.label} (${this.cfg.stage})`,
4698
4943
  `primary owner: ${this.cfg.ownerId ?? "\u2014"}`,
4699
4944
  `presence: ${this.presenceProfile.pattern}`,
4945
+ `communication: ${communicationProfileLabel(communication)}`,
4700
4946
  `score: ${JSON.stringify(rel.score)}`,
4701
4947
  `mcp: ${this.mcps.map((m) => m.id).join(", ") || "\u2014"}`,
4702
4948
  `paused: ${this.paused}`
@@ -4792,7 +5038,8 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
4792
5038
  const rel = await readRelationship(this.cfg.slug);
4793
5039
  const stage = findStage(this.cfg.stage);
4794
5040
  const conflict = await readConflict(this.cfg.slug);
4795
- const key = chatId ?? "default";
5041
+ const communication = normalizeCommunicationProfile(this.cfg);
5042
+ const key = chatId ?? this.histKey(this.cfg.ownerId ?? "default");
4796
5043
  const presence = computePresenceState(
4797
5044
  this.cfg,
4798
5045
  this.presenceProfile,
@@ -4807,6 +5054,8 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
4807
5054
  ` online: ${presence.online}, asleep: ${presence.asleep}, nightAwake: ${presence.nightAwake}`,
4808
5055
  ` localHour: ${presence.localHour}, hint: ${presence.hint}`,
4809
5056
  ``,
5057
+ `communication: ${communicationProfileLabel(communication)}`,
5058
+ ``,
4810
5059
  `stage: ${stage.label} (${this.cfg.stage})`,
4811
5060
  ` ignoreChance: ${stage.defaults.ignoreChance}, delay: ${stage.defaults.replyDelaySec[0]}-${stage.defaults.replyDelaySec[1]}s`,
4812
5061
  ``,
@@ -5092,11 +5341,15 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
5092
5341
  --name=<\u0438\u043C\u044F> \u043A\u043E\u043D\u043A\u0440\u0435\u0442\u043D\u043E\u0435 \u0438\u043C\u044F; \u0435\u0441\u043B\u0438 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u2014 \u0441\u043B\u0443\u0447\u0430\u0439\u043D\u043E\u0435 \u0438\u0437 \u043F\u0443\u043B\u0430 \u043F\u043E nationality (\u0442\u0443\u0440\u043D\u0438\u0440 \u0432\u044B\u0431\u043E\u0440\u0430 \u0438\u043C\u0451\u043D \u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0422\u041E\u041B\u042C\u041A\u041E \u0432 TUI \u0432\u0438\u0437\u0430\u0440\u0434\u0435)
5093
5342
  --age=<n>
5094
5343
  --persona-notes=<text> \u0434\u043E\u043F. \u043F\u043E\u0436\u0435\u043B\u0430\u043D\u0438\u044F \u043A persona/speech/communication \u043F\u0435\u0440\u0435\u0434 \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0438\u0435\u0439
5344
+ --communication-preset=<id> normal|cute|alt|clingy|chatty
5345
+ --notifications=<mode> muted|normal|priority
5346
+ --message-style=<style> one-liners|balanced|bursty|longform
5347
+ --initiative=<level> low|medium|high
5348
+ --life-sharing=<level> low|medium|high
5095
5349
  --nationality=RU|UA (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E RU)
5096
5350
  --tz=<value> IANA "Europe/Moscow" / "GMT+3" / "+3" / "\u041A\u0438\u0435\u0432" \u2014 \u043F\u043E\u0438\u0441\u043A
5097
5351
  --stage=<id> met-irl-got-tg|tg-given-cold|tg-given-warming|convinced|first-date-done|dating-early|dating-stable|long-term
5098
5352
  --mcp=exa:KEY \u043C\u043E\u0436\u043D\u043E \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0440\u0430\u0437
5099
- --practice practice-\u0440\u0435\u0436\u0438\u043C
5100
5353
  --list \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u0438
5101
5354
  --help
5102
5355
 
@@ -5104,8 +5357,32 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
5104
5357
  `;
5105
5358
  async function main() {
5106
5359
  const argv = mri(process.argv.slice(2), {
5107
- string: ["profile", "mode", "token", "api-id", "api-hash", "phone", "api-preset", "base-url", "proto", "model", "api-key", "name", "stage", "mcp", "nationality", "tz", "vibe", "persona-notes"],
5108
- boolean: ["practice", "help", "list", "reset"],
5360
+ string: [
5361
+ "profile",
5362
+ "mode",
5363
+ "token",
5364
+ "api-id",
5365
+ "api-hash",
5366
+ "phone",
5367
+ "api-preset",
5368
+ "base-url",
5369
+ "proto",
5370
+ "model",
5371
+ "api-key",
5372
+ "name",
5373
+ "stage",
5374
+ "mcp",
5375
+ "nationality",
5376
+ "tz",
5377
+ "vibe",
5378
+ "persona-notes",
5379
+ "communication-preset",
5380
+ "notifications",
5381
+ "message-style",
5382
+ "initiative",
5383
+ "life-sharing"
5384
+ ],
5385
+ boolean: ["help", "list", "reset"],
5109
5386
  alias: { h: "help" }
5110
5387
  });
5111
5388
  if (argv.help) {
@@ -5114,8 +5391,8 @@ async function main() {
5114
5391
  }
5115
5392
  if (argv.age != null) {
5116
5393
  const a = Number(argv.age);
5117
- if (!Number.isFinite(a) || a < 18 || a > 99) {
5118
- process.stderr.write("age must be a number between 18 and 99\n");
5394
+ if (!Number.isFinite(a) || a < 13 || a > 99) {
5395
+ process.stderr.write("age must be a number between 13 and 99\n");
5119
5396
  process.exit(1);
5120
5397
  }
5121
5398
  }
@@ -5127,8 +5404,14 @@ async function main() {
5127
5404
  if (argv.profile && !argv.mode && !argv.name) {
5128
5405
  const cfg = await readConfig(argv.profile);
5129
5406
  if (!cfg) {
5407
+ const profiles = await listProfiles();
5130
5408
  process.stderr.write(`profile not found: ${argv.profile}
5131
5409
  `);
5410
+ process.stderr.write(`data dir: ${DATA_ROOT}
5411
+ `);
5412
+ process.stderr.write(profiles.length ? `available profiles:
5413
+ ${profiles.join("\n")}
5414
+ ` : "available profiles: none\n");
5132
5415
  process.exit(1);
5133
5416
  }
5134
5417
  if (argv.reset) {
@@ -5146,7 +5429,7 @@ async function main() {
5146
5429
  \u0433\u0435\u043D\u0435\u0440\u0438\u0440\u0443\u0435\u043C persona.md / speech.md / communication.md...
5147
5430
  `);
5148
5431
  const llm = makeLLM(cfg.llm);
5149
- const generated = await generatePersonaPack(llm, cfg.slug, cfg.name, cfg.age, cfg.nationality, cfg.personaNotes ?? "");
5432
+ const generated = await generatePersonaPack(llm, cfg.slug, cfg.name, cfg.age, cfg.nationality, personaNotesForGeneration2(cfg));
5150
5433
  cfg.busySchedule = generated.busySchedule;
5151
5434
  await writeConfig(cfg);
5152
5435
  await runRuntime(cfg);
@@ -5195,6 +5478,7 @@ async function buildConfigFromFlags(argv) {
5195
5478
  const mode = argv.mode ?? "bot";
5196
5479
  const tz = (argv.tz ? parseTzFlag(String(argv.tz)) : void 0) ?? defaultTzForNationality(nationality);
5197
5480
  const mcpFlags = [].concat(argv.mcp ?? []);
5481
+ const communication = communicationFromFlags(argv);
5198
5482
  const mcps = mcpFlags.map((entry) => {
5199
5483
  const [id, key] = entry.split(":");
5200
5484
  const secrets = id === "exa" ? { EXA_API_KEY: key ?? "" } : { value: key ?? "" };
@@ -5219,11 +5503,32 @@ async function buildConfigFromFlags(argv) {
5219
5503
  sleepFrom: 23,
5220
5504
  sleepTo: 8,
5221
5505
  nightWakeChance: 0.05,
5222
- vibe: argv.vibe === "warm" ? "warm" : "short",
5506
+ vibe: deriveLegacyVibe(communication),
5507
+ communication,
5223
5508
  personaNotes: argv["persona-notes"] ? String(argv["persona-notes"]) : void 0,
5224
5509
  busySchedule: []
5225
5510
  };
5226
5511
  }
5512
+ function communicationFromFlags(argv) {
5513
+ const preset = findCommunicationPreset(argv["communication-preset"] ? String(argv["communication-preset"]) : void 0);
5514
+ const base = preset?.profile ?? normalizeCommunicationProfile({ vibe: argv.vibe === "warm" ? "warm" : argv.vibe === "short" ? "short" : void 0 });
5515
+ return {
5516
+ notifications: oneOf(argv.notifications, ["muted", "normal", "priority"], base.notifications),
5517
+ messageStyle: oneOf(argv["message-style"], ["one-liners", "balanced", "bursty", "longform"], base.messageStyle),
5518
+ initiative: oneOf(argv.initiative, ["low", "medium", "high"], base.initiative),
5519
+ lifeSharing: oneOf(argv["life-sharing"], ["low", "medium", "high"], base.lifeSharing)
5520
+ };
5521
+ }
5522
+ function oneOf(raw, allowed, fallback) {
5523
+ return typeof raw === "string" && allowed.includes(raw) ? raw : fallback;
5524
+ }
5525
+ function personaNotesForGeneration2(cfg) {
5526
+ const parts = [
5527
+ cfg.personaNotes?.trim(),
5528
+ `\u0422\u043E\u043D \u043E\u0431\u0449\u0435\u043D\u0438\u044F: ${communicationProfileLabel(normalizeCommunicationProfile(cfg))}. \u0423\u0447\u0442\u0438 \u044D\u0442\u043E \u043F\u0440\u0438 speech.md \u0438 communication.md.`
5529
+ ].filter(Boolean);
5530
+ return parts.join("\n\n");
5531
+ }
5227
5532
  async function runRuntime(cfg) {
5228
5533
  const rt = new Runtime(cfg);
5229
5534
  await rt.start();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thesashadev/girl-agent",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Telegram AI persona engine with memory, schedule, relationship state and MTProto userbot mode.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,6 +11,7 @@
11
11
  "dist",
12
12
  "templates",
13
13
  "README.md",
14
+ "CHANGELOG.md",
14
15
  "LICENSE"
15
16
  ],
16
17
  "scripts": {