@thesashadev/girl-agent 0.1.9 → 0.1.11

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cli.js +80 -16
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.11
4
+
5
+ Дата: 2026-05-07
6
+
7
+ - Merge pull request #53 from TheSashaDev/devin/1778183666-smart-busy-notify
8
+ - feat: smart busy-transition notification based on context, persona, stage, duration
9
+
10
+ ## 0.1.10
11
+
12
+ Дата: 2026-05-07
13
+
14
+ - Merge pull request #51 from TheSashaDev/devin/1778179420-proxy-wss-support
15
+ - refactor: remove proxy support, WSS enabled by default
16
+ - feat: add numeric identifiers to stages for convenience
17
+ - feat: add proxy/WSS support for Telegram (fixes #38, #32)
18
+
3
19
  ## 0.1.9
4
20
 
5
21
  Дата: 2026-05-07
package/dist/cli.js CHANGED
@@ -59,13 +59,15 @@ function makeUserbotAdapter(cfg) {
59
59
  const apiHash = cfg.telegram.apiHash;
60
60
  const session = cfg.telegram.sessionString ?? "";
61
61
  if (!apiId || !apiHash) throw new Error("API_ID/API_HASH missing for userbot");
62
- debug("[userbot] creating TelegramClient\u2026");
62
+ const useWSS = cfg.telegram.useWSS !== false;
63
+ debug(`[userbot] creating TelegramClient (useWSS=${useWSS})\u2026`);
63
64
  const client = new TelegramClient(new StringSession(session), apiId, apiHash, {
64
65
  connectionRetries: 5,
65
66
  requestRetries: 5,
66
67
  retryDelay: 3e3,
67
68
  autoReconnect: true,
68
- floodSleepThreshold: 120
69
+ floodSleepThreshold: 120,
70
+ useWSS
69
71
  });
70
72
  client.onError = async () => {
71
73
  };
@@ -214,7 +216,8 @@ function makeUserbotAdapter(cfg) {
214
216
  }
215
217
  async function userbotLogin(opts) {
216
218
  const client = new TelegramClient(new StringSession(""), opts.apiId, opts.apiHash, {
217
- connectionRetries: 5
219
+ connectionRetries: 5,
220
+ useWSS: true
218
221
  });
219
222
  await client.start({
220
223
  phoneNumber: async () => opts.phone,
@@ -590,6 +593,7 @@ init_esm_shims();
590
593
  var STAGE_PRESETS = [
591
594
  {
592
595
  id: "met-irl-got-tg",
596
+ num: 1,
593
597
  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",
594
598
  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.",
595
599
  defaults: {
@@ -604,6 +608,7 @@ var STAGE_PRESETS = [
604
608
  },
605
609
  {
606
610
  id: "tg-given-cold",
611
+ num: 2,
607
612
  label: "\u0414\u0430\u043B\u0430 \u0442\u0433, \u043D\u043E \u043D\u0435 \u0443\u0431\u0435\u0434\u0438\u043B \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C",
608
613
  description: "\u0421\u043E\u043C\u043D\u0435\u0432\u0430\u0435\u0442\u0441\u044F. \u0427\u0430\u0441\u0442\u043E \u0438\u0433\u043D\u043E\u0440\u0438\u0442, \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043E\u0434\u043D\u043E\u0441\u043B\u043E\u0436\u043D\u043E. \u041D\u0443\u0436\u043D\u043E \u0434\u043E\u0431\u0438\u0432\u0430\u0442\u044C\u0441\u044F.",
609
614
  defaults: {
@@ -618,6 +623,7 @@ var STAGE_PRESETS = [
618
623
  },
619
624
  {
620
625
  id: "tg-given-warming",
626
+ num: 3,
621
627
  label: "\u0414\u0430\u043B\u0430 \u0442\u0433, \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043E\u0441\u0442\u043E\u0440\u043E\u0436\u043D\u043E",
622
628
  description: "\u041E\u0442\u0442\u0430\u0438\u0432\u0430\u0435\u0442. \u041E\u0442\u0432\u0435\u0447\u0430\u0435\u0442, \u043D\u043E \u043A\u043E\u0440\u043E\u0442\u043A\u043E. \u0422\u0435\u0441\u0442\u0438\u0442 \u0442\u0435\u0431\u044F.",
623
629
  defaults: {
@@ -632,6 +638,7 @@ var STAGE_PRESETS = [
632
638
  },
633
639
  {
634
640
  id: "convinced",
641
+ num: 4,
635
642
  label: "\u0423\u0431\u0435\u0434\u0438\u043B \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C \u0441\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u043E",
636
643
  description: "\u041E\u0431\u0449\u0430\u0435\u0442\u0435\u0441\u044C \u0440\u0435\u0433\u0443\u043B\u044F\u0440\u043D\u043E, \u0444\u043B\u0438\u0440\u0442\u0443\u0435\u0442, \u0435\u0449\u0451 \u043D\u0435 \u0432\u0438\u0434\u0435\u043B\u0438\u0441\u044C \u043F\u043E\u0441\u043B\u0435 \u0437\u043D\u0430\u043A\u043E\u043C\u0441\u0442\u0432\u0430.",
637
644
  defaults: {
@@ -646,6 +653,7 @@ var STAGE_PRESETS = [
646
653
  },
647
654
  {
648
655
  id: "first-date-done",
656
+ num: 5,
649
657
  label: "\u0421\u0445\u043E\u0434\u0438\u043B\u0438 \u043E\u0434\u0438\u043D \u0440\u0430\u0437",
650
658
  description: "\u041F\u0435\u0440\u0432\u043E\u0435 \u0441\u0432\u0438\u0434\u0430\u043D\u0438\u0435 \u0431\u044B\u043B\u043E, \u0432 \u043F\u043E\u0434\u0432\u0435\u0448\u0435\u043D\u043D\u043E\u043C \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0438 \u2014 \u043D\u0440\u0430\u0432\u0438\u0442\u0441\u044F, \u043D\u043E \u043D\u0435 \u043F\u0430\u0440\u0430.",
651
659
  defaults: {
@@ -660,6 +668,7 @@ var STAGE_PRESETS = [
660
668
  },
661
669
  {
662
670
  id: "dating-early",
671
+ num: 6,
663
672
  label: "\u0422\u043E\u043B\u044C\u043A\u043E \u043D\u0430\u0447\u0430\u043B\u0438 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044C\u0441\u044F",
664
673
  description: "\u041E\u043A\u043E\u043B\u043E \u043C\u0435\u0441\u044F\u0446\u0430 \u0432\u043C\u0435\u0441\u0442\u0435. \u0411\u0430\u0431\u043E\u0447\u043A\u0438, \u0432\u0441\u0451 \u0432\u043D\u043E\u0432\u0435, \u043D\u043E \u0433\u0440\u0430\u043D\u0438\u0446\u044B \u0435\u0449\u0451 \u0445\u0440\u0443\u043F\u043A\u0438\u0435.",
665
674
  defaults: {
@@ -674,6 +683,7 @@ var STAGE_PRESETS = [
674
683
  },
675
684
  {
676
685
  id: "dating-stable",
686
+ num: 7,
677
687
  label: "\u041F\u0430\u0440\u0430, \u043E\u0431\u0449\u0430\u0435\u0442\u0435\u0441\u044C \u0441\u0432\u043E\u0431\u043E\u0434\u043D\u043E",
678
688
  description: "\u0421\u0442\u0430\u0431\u0438\u043B\u044C\u043D\u044B\u0435 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u044F, \u0448\u0443\u0442\u043A\u0438, \u0431\u044B\u0442\u043E\u0432\u0443\u0445\u0430, \u0434\u043E\u0432\u0435\u0440\u0438\u0435.",
679
689
  defaults: {
@@ -688,6 +698,7 @@ var STAGE_PRESETS = [
688
698
  },
689
699
  {
690
700
  id: "long-term",
701
+ num: 8,
691
702
  label: "\u0414\u0430\u0432\u043D\u043E \u0432\u043C\u0435\u0441\u0442\u0435",
692
703
  description: "\u0413\u043E\u0434+ \u0432\u043C\u0435\u0441\u0442\u0435. \u0418\u043D\u043E\u0433\u0434\u0430 \u0440\u0430\u0437\u0434\u0440\u0430\u0436\u0435\u043D\u0438\u0435, \u0440\u0443\u0442\u0438\u043D\u0430, \u0433\u043B\u0443\u0431\u043E\u043A\u043E\u0435 \u0434\u043E\u0432\u0435\u0440\u0438\u0435.",
693
704
  defaults: {
@@ -702,6 +713,7 @@ var STAGE_PRESETS = [
702
713
  },
703
714
  {
704
715
  id: "dumped",
716
+ num: 9,
705
717
  label: "\u041E\u0442\u0448\u0438\u043B\u0430 (\u0441\u043B\u0443\u0436\u0435\u0431\u043D\u043E\u0435)",
706
718
  description: "\u041D\u0435 \u043E\u0442\u0432\u0435\u0447\u0430\u0435\u0442. \u0421\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u043E\u0439 :reset.",
707
719
  defaults: {
@@ -716,6 +728,10 @@ var STAGE_PRESETS = [
716
728
  }
717
729
  ];
718
730
  function findStage(id) {
731
+ if (typeof id === "number" || /^\d+$/.test(String(id))) {
732
+ const num = Number(id);
733
+ return STAGE_PRESETS.find((s) => s.num === num) ?? STAGE_PRESETS[1];
734
+ }
719
735
  return STAGE_PRESETS.find((s) => s.id === id) ?? STAGE_PRESETS[1];
720
736
  }
721
737
 
@@ -2434,7 +2450,7 @@ function Wizard({ initial, onDone }) {
2434
2450
  SelectInput,
2435
2451
  {
2436
2452
  limit: 10,
2437
- items: STAGE_PRESETS.filter((s) => s.id !== "dumped").map((s) => ({ label: `${s.label} \xB7 ${s.description}`, value: s.id })),
2453
+ items: STAGE_PRESETS.filter((s) => s.id !== "dumped").map((s) => ({ label: `${s.num}. ${s.label} \xB7 ${s.description}`, value: s.id })),
2438
2454
  onSelect: async (it) => {
2439
2455
  const nextStage = it.value;
2440
2456
  setStage(nextStage);
@@ -2679,7 +2695,7 @@ function Dashboard({ runtime }) {
2679
2695
  break;
2680
2696
  }
2681
2697
  case "help":
2682
- 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");
2698
+ append(":status :why :amnesia <\u043C\u0438\u043D> [chatId] :reset :stage <id|num> :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");
2683
2699
  break;
2684
2700
  case "quit":
2685
2701
  case "exit":
@@ -2697,7 +2713,7 @@ function Dashboard({ runtime }) {
2697
2713
  const line = cmd.trim();
2698
2714
  setCmd("");
2699
2715
  if (line) await execute(line);
2700
- } })), /* @__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"));
2716
+ } })), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u043A\u043E\u043C\u0430\u043D\u0434\u044B: :status :why :amnesia <\u043C\u0438\u043D> :reset :stage <id|num> :pause :resume :cringe :persona :log [day] :block :unblock :read :clear-chat :delete-last :edit-last :sticker :quit"));
2701
2717
  }
2702
2718
 
2703
2719
  // src/engine/runtime.ts
@@ -2932,6 +2948,49 @@ function activeBusySlot(slots, day, minuteOfDay) {
2932
2948
  }
2933
2949
  return null;
2934
2950
  }
2951
+ function busyTransitionHint(stage, comm, remainingMin, checkAfterMin, recentExchangeCount) {
2952
+ const stageChance = {
2953
+ "met-irl-got-tg": 0.05,
2954
+ "tg-given-cold": 0.08,
2955
+ "tg-given-warming": 0.18,
2956
+ "convinced": 0.4,
2957
+ "first-date-done": 0.55,
2958
+ "dating-early": 0.7,
2959
+ "dating-stable": 0.8,
2960
+ "long-term": 0.75,
2961
+ "dumped": 0
2962
+ };
2963
+ let chance = stageChance[stage] ?? 0.3;
2964
+ if (comm.initiative === "high") chance += 0.15;
2965
+ else if (comm.initiative === "low") chance -= 0.15;
2966
+ if (comm.notifications === "priority") chance += 0.1;
2967
+ else if (comm.notifications === "muted") chance -= 0.1;
2968
+ if (comm.lifeSharing === "high") chance += 0.05;
2969
+ if (remainingMin <= 10) chance -= 0.2;
2970
+ else if (remainingMin <= 20) chance -= 0.05;
2971
+ else if (remainingMin >= 90) chance += 0.15;
2972
+ else if (remainingMin >= 45) chance += 0.08;
2973
+ if (checkAfterMin >= 30) chance += 0.15;
2974
+ else if (checkAfterMin >= 15) chance += 0.05;
2975
+ else if (checkAfterMin <= 8) chance -= 0.1;
2976
+ if (recentExchangeCount >= 6) chance += 0.1;
2977
+ else if (recentExchangeCount >= 4) chance += 0.05;
2978
+ chance = Math.max(0, Math.min(1, chance));
2979
+ if (Math.random() >= chance) return null;
2980
+ const isClose = ["dating-early", "dating-stable", "long-term"].includes(stage);
2981
+ const isWarm = comm.initiative === "high" || comm.lifeSharing === "high";
2982
+ const isCold = comm.initiative === "low" && comm.messageStyle === "one-liners";
2983
+ if (isCold) {
2984
+ return `\u041F\u0415\u0420\u0415\u0425\u041E\u0414 \u0412 \u0417\u0410\u041D\u042F\u0422\u041E\u0421\u0422\u042C: \u0443 \u0432\u0430\u0441 \u0431\u044B\u043B \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0434\u0438\u0430\u043B\u043E\u0433, \u043D\u043E \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F ${remainingMin > 30 ? "\u0434\u043E\u043B\u0433\u043E\u0435" : "\u043A\u043E\u0440\u043E\u0442\u043A\u043E\u0435"} \u0434\u0435\u043B\u043E. \u041F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0434\u0438 \u041A\u0420\u0410\u0422\u041A\u041E \u0438 \u0441\u0443\u0445\u043E, \u0432 \u0441\u0432\u043E\u0451\u043C \u0441\u0442\u0438\u043B\u0435 \u2014 \u043E\u0434\u043D\u043E-\u0434\u0432\u0430 \u0441\u043B\u043E\u0432\u0430 ("\u0443\u0448\u043B\u0430", "\u043F\u043E\u0442\u043E\u043C", "\u0437\u0430\u043D\u044F\u0442\u0430"). \u041D\u0435 \u043E\u0431\u044A\u044F\u0441\u043D\u044F\u0439 \u043F\u043E\u0434\u0440\u043E\u0431\u043D\u043E.`;
2985
+ }
2986
+ if (isClose && isWarm) {
2987
+ return `\u041F\u0415\u0420\u0415\u0425\u041E\u0414 \u0412 \u0417\u0410\u041D\u042F\u0422\u041E\u0421\u0422\u042C: \u0443 \u0432\u0430\u0441 \u0431\u044B\u043B \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0434\u0438\u0430\u043B\u043E\u0433, \u043D\u043E \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F \u0437\u0430\u043D\u044F\u0442\u0438\u0435. \u041F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0434\u0438 \u0435\u0433\u043E \u0442\u0435\u043F\u043B\u043E \u0438 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043D\u043D\u043E, \u0432 \u0441\u0442\u0438\u043B\u0435 \u0432\u0430\u0448\u0435\u0439 \u0431\u043B\u0438\u0437\u043E\u0441\u0442\u0438 (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: "\u043C\u0430\u043B\u044B\u0448 \u043C\u043D\u0435 \u043D\u0430\u0434\u043E \u0431\u0435\u0436\u0430\u0442\u044C \u043D\u0430 ${remainingMin > 45 ? "\u043F\u0430\u0440\u0443 \u0447\u0430\u0441\u043E\u0432" : "\u043D\u0435\u043C\u043D\u043E\u0433\u043E"}, \u043F\u043E\u0442\u043E\u043C \u043D\u0430\u043F\u0438\u0448\u0443" \u0438\u043B\u0438 "\u044F \u043F\u043E \u0434\u0435\u043B\u0430\u043C, \u0441\u043A\u0443\u0447\u0430\u0439"). \u0421\u0442\u0438\u043B\u044C \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043E\u0442 persona.`;
2988
+ }
2989
+ if (isClose) {
2990
+ return `\u041F\u0415\u0420\u0415\u0425\u041E\u0414 \u0412 \u0417\u0410\u041D\u042F\u0422\u041E\u0421\u0422\u042C: \u0443 \u0432\u0430\u0441 \u0431\u044B\u043B \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0434\u0438\u0430\u043B\u043E\u0433, \u043D\u043E \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F \u0437\u0430\u043D\u044F\u0442\u0438\u0435. \u041F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0434\u0438 \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043D\u043D\u043E (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: "\u043C\u043D\u0435 \u043D\u0430\u0434\u043E \u043E\u0442\u043E\u0439\u0442\u0438, \u043F\u043E\u0437\u0436\u0435 \u043D\u0430\u043F\u0438\u0448\u0443" \u0438\u043B\u0438 "\u044F \u0437\u0430\u043D\u044F\u0442\u0430 \u0431\u0443\u0434\u0443 ${remainingMin > 45 ? "\u043F\u0430\u0440\u0443 \u0447\u0430\u0441\u043E\u0432" : "\u043D\u0435\u043C\u043D\u043E\u0433\u043E"}, \u043F\u043E\u0442\u043E\u043C \u043E\u0442\u0432\u0435\u0447\u0443").`;
2991
+ }
2992
+ return `\u041F\u0415\u0420\u0415\u0425\u041E\u0414 \u0412 \u0417\u0410\u041D\u042F\u0422\u041E\u0421\u0422\u042C: \u0443 \u0432\u0430\u0441 \u0431\u044B\u043B \u0430\u043A\u0442\u0438\u0432\u043D\u044B\u0439 \u0434\u0438\u0430\u043B\u043E\u0433, \u043D\u043E \u043D\u0430\u0447\u0438\u043D\u0430\u0435\u0442\u0441\u044F \u0434\u0435\u043B\u043E. \u041C\u043E\u0436\u0435\u0448\u044C \u043A\u043E\u0440\u043E\u0442\u043A\u043E \u043F\u0440\u0435\u0434\u0443\u043F\u0440\u0435\u0434\u0438\u0442\u044C \u043E\u0431 \u0443\u0445\u043E\u0434\u0435 (\u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440: "\u044F \u043F\u043E\u0431\u0435\u0436\u0430\u043B\u0430, \u043F\u043E\u0442\u043E\u043C \u043E\u0442\u0432\u0435\u0447\u0443" \u0438\u043B\u0438 "\u043C\u043D\u0435 \u043F\u043E\u0440\u0430, \u043F\u043E\u0437\u0436\u0435"). \u0421\u0442\u0438\u043B\u044C \u0437\u0430\u0432\u0438\u0441\u0438\u0442 \u043E\u0442 \u0443\u0440\u043E\u0432\u043D\u044F \u0432\u0430\u0448\u0438\u0445 \u043E\u0442\u043D\u043E\u0448\u0435\u043D\u0438\u0439 \u0438 persona.`;
2993
+ }
2935
2994
  function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recentExchangeCount, forcedWake = false, conflict = null) {
2936
2995
  const local = localParts(cfg.tz);
2937
2996
  const communication = normalizeCommunicationProfile(cfg);
@@ -3017,6 +3076,10 @@ function computePresenceState(cfg, profile, lastUserMsgTs, lastHerReplyTs, recen
3017
3076
  hint = `\u041D\u0430 ${busy.label} (\u0434\u043E ${busy.until}), \u043D\u043E \u0441\u043A\u0443\u0447\u043D\u043E \u2014 \u043F\u0435\u0440\u0438\u043E\u0434\u0438\u0447\u0435\u0441\u043A\u0438 \u0437\u0430\u0445\u043E\u0434\u0438\u0448\u044C \u0432 Telegram \u043C\u0435\u0436\u0434\u0443 \u0434\u0435\u043B\u043E\u043C. \u041C\u043E\u0436\u0435\u0448\u044C \u0431\u044B\u0441\u0442\u0440\u043E \u043E\u0442\u0432\u0435\u0442\u0438\u0442\u044C, \u043D\u043E \u043C\u043E\u0436\u0435\u0448\u044C \u0438 \u043F\u0440\u043E\u043F\u0443\u0441\u0442\u0438\u0442\u044C \u043C\u043E\u043C\u0435\u043D\u0442.`;
3018
3077
  } else {
3019
3078
  hint = `\u0421\u0435\u0439\u0447\u0430\u0441 \u0437\u0430\u043D\u044F\u0442\u0430: ${busy.label} \u0434\u043E ${busy.until}. \u041D\u0435 \u043C\u043E\u0436\u0435\u0448\u044C \u043D\u043E\u0440\u043C\u0430\u043B\u044C\u043D\u043E \u043E\u0442\u0432\u0435\u0447\u0430\u0442\u044C. \u041F\u043E\u0441\u043B\u0435 \u044D\u0442\u043E\u0433\u043E \u0437\u0430\u0439\u0434\u0451\u0448\u044C \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C Telegram \u0447\u0435\u0440\u0435\u0437 ${busy.checkAfterMin} \u043C\u0438\u043D; \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043E \u0438 \u043E\u0442\u0432\u0435\u0442 \u0431\u0443\u0434\u0435\u0442 \u0442\u043E\u043B\u044C\u043A\u043E \u0442\u043E\u0433\u0434\u0430.`;
3079
+ if (inActiveDialog && busySlot) {
3080
+ const notifyHint = busyTransitionHint(cfg.stage, communication, busySlot.remainingMin, busy.checkAfterMin ?? 15, recentExchangeCount);
3081
+ if (notifyHint) hint += "\n\n" + notifyHint;
3082
+ }
3020
3083
  }
3021
3084
  } else if (online) {
3022
3085
  if (inActiveDialog) {
@@ -5389,10 +5452,11 @@ ${tick.intent === "short" ? "\u041E\u0442\u0432\u0435\u0447\u0430\u0439 \u043E\u
5389
5452
  }
5390
5453
  async cmdSetStage(stageId) {
5391
5454
  const prev = this.cfg.stage;
5392
- this.cfg.stage = stageId;
5455
+ const resolved = findStage(stageId);
5456
+ this.cfg.stage = resolved.id;
5393
5457
  await writeConfig(this.cfg);
5394
- await maybeAdvanceRelationshipTimeline(this.cfg, prev, stageId);
5395
- return `\u0441\u0442\u0430\u0434\u0438\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430: ${stageId}`;
5458
+ await maybeAdvanceRelationshipTimeline(this.cfg, prev, resolved.id);
5459
+ return `\u0441\u0442\u0430\u0434\u0438\u044F \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430: ${resolved.num}=${resolved.id}`;
5396
5460
  }
5397
5461
  async cmdWake(chatId) {
5398
5462
  const now = Date.now();
@@ -5852,7 +5916,7 @@ async function runHeadlessJsonEvents(rt) {
5852
5916
  t: Date.now(),
5853
5917
  paused,
5854
5918
  profile: profileSummary(rt.cfg),
5855
- stage: { id: r.stage, label: findStage(r.stage).label },
5919
+ stage: { id: r.stage, num: findStage(r.stage).num, label: findStage(r.stage).label },
5856
5920
  score: r.score
5857
5921
  });
5858
5922
  return;
@@ -5896,7 +5960,7 @@ function profileSummary(cfg) {
5896
5960
  mode: cfg.mode,
5897
5961
  nationality: cfg.nationality,
5898
5962
  tz: cfg.tz,
5899
- stage: { id: cfg.stage, label: stage.label }
5963
+ stage: { id: cfg.stage, num: stage.num, label: stage.label }
5900
5964
  };
5901
5965
  }
5902
5966
 
@@ -5925,7 +5989,7 @@ env-vars (\u0434\u043B\u044F CI / docker secrets / k8s):
5925
5989
  GIRL_AGENT_TOKEN telegram bot token
5926
5990
  GIRL_AGENT_API_PRESET openai|anthropic|claudehub|...
5927
5991
  GIRL_AGENT_API_KEY \u043A\u043B\u044E\u0447 \u043E\u0442 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440\u0430
5928
- GIRL_AGENT_MODEL, _NAME, _AGE, _NATIONALITY, _TZ, _STAGE, _COMM_PRESET
5992
+ GIRL_AGENT_MODEL, _NAME, _AGE, _NATIONALITY, _TZ, _STAGE (id \u0438\u043B\u0438 \u043D\u043E\u043C\u0435\u0440 1-8), _COMM_PRESET
5929
5993
 
5930
5994
  \u0434\u043B\u044F \u0438\u043D\u0442\u0435\u0440\u0430\u043A\u0442\u0438\u0432\u043D\u043E\u0439 \u043F\u0435\u0440\u0432\u0438\u0447\u043D\u043E\u0439 \u043D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0437\u0430\u043F\u0443\u0441\u043A\u0430\u0439 \u0431\u0435\u0437 \u0444\u043B\u0430\u0433\u043E\u0432 \u0432 \u043E\u0431\u044B\u0447\u043D\u043E\u043C \u0442\u0435\u0440\u043C\u0438\u043D\u0430\u043B\u0435 \u2014
5931
5995
  \u043E\u0442\u043A\u0440\u043E\u0435\u0442\u0441\u044F ink-\u0432\u0438\u0437\u0430\u0440\u0434.
@@ -6062,7 +6126,7 @@ function configFromEnv() {
6062
6126
  const name = e.GIRL_AGENT_NAME || pickRandomNames(nationality, 1)[0];
6063
6127
  const age = Number(e.GIRL_AGENT_AGE ?? 18);
6064
6128
  const tz = e.GIRL_AGENT_TZ ? parseTzFlag(e.GIRL_AGENT_TZ) ?? defaultTzForNationality(nationality) : defaultTzForNationality(nationality);
6065
- const stage = e.GIRL_AGENT_STAGE || "tg-given-cold";
6129
+ const stage = e.GIRL_AGENT_STAGE ? findStage(e.GIRL_AGENT_STAGE).id : "tg-given-cold";
6066
6130
  const commPreset = COMMUNICATION_PRESETS.find((c) => c.id === (e.GIRL_AGENT_COMM_PRESET ?? "normal")) ?? COMMUNICATION_PRESETS[0];
6067
6131
  return {
6068
6132
  slug: slugify(name),
@@ -6309,13 +6373,13 @@ required flags \u0434\u043B\u044F headless setup (--name --age --stage --api-pre
6309
6373
  --privacy=<mode> owner-only|allow-strangers (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E owner-only)
6310
6374
  --nationality=RU|UA (\u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E RU)
6311
6375
  --tz=<value> IANA "Europe/Moscow" / "GMT+3" / "+3" / "\u041A\u0438\u0435\u0432" \u2014 \u043F\u043E\u0438\u0441\u043A
6312
- --stage=<id> met-irl-got-tg|tg-given-cold|tg-given-warming|convinced|first-date-done|dating-early|dating-stable|long-term
6376
+ --stage=<id|num> 1=met-irl-got-tg 2=tg-given-cold 3=tg-given-warming 4=convinced 5=first-date-done 6=dating-early 7=dating-stable 8=long-term
6313
6377
  --mcp=exa:KEY \u043C\u043E\u0436\u043D\u043E \u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u0440\u0430\u0437
6314
6378
  --new \u043F\u0440\u0438\u043D\u0443\u0434\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u043E\u0442\u043A\u0440\u044B\u0442\u044C \u0432\u0438\u0437\u0430\u0440\u0434 \u0434\u043B\u044F \u043D\u043E\u0432\u043E\u0433\u043E \u043F\u0440\u043E\u0444\u0438\u043B\u044F
6315
6379
  --list \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0440\u043E\u0444\u0438\u043B\u0438
6316
6380
  --help
6317
6381
 
6318
- \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0432 \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0435\u043C \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435: :status :reset :stage <id> :pause :resume :cringe :persona :log :quit
6382
+ \u043A\u043E\u043C\u0430\u043D\u0434\u044B \u0432 \u0440\u0430\u0431\u043E\u0442\u0430\u044E\u0449\u0435\u043C \u0434\u0430\u0448\u0431\u043E\u0440\u0434\u0435: :status :reset :stage <id|num> :pause :resume :cringe :persona :log :quit
6319
6383
  `;
6320
6384
  async function main() {
6321
6385
  const argv = mri(process.argv.slice(2), {
@@ -6513,7 +6577,7 @@ async function buildConfigFromFlags(argv) {
6513
6577
  nationality,
6514
6578
  tz,
6515
6579
  mode,
6516
- stage: argv.stage,
6580
+ stage: findStage(argv.stage).id,
6517
6581
  llm: { presetId, proto, baseURL, apiKey: String(argv["api-key"] ?? preset?.defaultApiKey ?? ""), model },
6518
6582
  telegram: mode === "bot" ? { botToken: String(argv.token ?? "") } : {
6519
6583
  apiId: Number(argv["api-id"] ?? 0),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thesashadev/girl-agent",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Telegram AI persona engine with memory, schedule, relationship state and MTProto userbot mode.",
5
5
  "type": "module",
6
6
  "bin": {