@ykurilov/agent-chat-flows 0.63.2 → 0.65.0

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/dist/react.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
2
  FACT_PREFIX,
3
+ FLOW_DEADLINE_NOTICE,
4
+ FLOW_TIMER_NOTICE,
3
5
  fieldValueText
4
- } from "./chunk-KGWXTWBZ.js";
6
+ } from "./chunk-KRZMJJKG.js";
5
7
  import {
6
8
  ANY_STATE_LAYOUT_KEY,
7
9
  DEFAULT_LATE_START_HOURS,
@@ -23,12 +25,13 @@ import {
23
25
  noteAgentAnswer,
24
26
  noteHumanMessage,
25
27
  noteSideQuestion,
28
+ parseDuration,
26
29
  parseFlow,
27
30
  sideQuestion,
28
31
  startRun,
29
32
  stateById,
30
33
  timerDueAt
31
- } from "./chunk-LT4QELAZ.js";
34
+ } from "./chunk-7VR4YNTJ.js";
32
35
 
33
36
  // src/react/flow-labels.ts
34
37
  import { createContext, createElement, useContext } from "react";
@@ -598,6 +601,53 @@ var englishFlowMigrateLabels = {
598
601
  failed: "Could not move the chats. Try again"
599
602
  };
600
603
 
604
+ // src/labels/notices.ts
605
+ var withTarget = (head, condition, stateTitle) => `${head}${condition ? ` (${condition})` : ""}${stateTitle ? ` \u2192 \xAB${stateTitle}\xBB` : ""}`;
606
+ var russianFlowNoticeLabels = {
607
+ wroteFirst: (reasons) => `\u0410\u0433\u0435\u043D\u0442 \u043D\u0430\u043F\u0438\u0441\u0430\u043B \u043F\u0435\u0440\u0432\u044B\u043C \u2014 ${reasons}`,
608
+ separator: ", ",
609
+ silentFor: (duration) => `\u0447\u0435\u043B\u043E\u0432\u0435\u043A \u043C\u043E\u043B\u0447\u0430\u043B ${duration}`,
610
+ silence: "\u043A\u0430\u0441\u0430\u043D\u0438\u0435 \u043F\u043E\u0441\u043B\u0435 \u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044F",
611
+ morning: "\u0443\u0442\u0440\u043E \u043D\u043E\u0432\u043E\u0433\u043E \u0434\u043D\u044F",
612
+ daily: (at) => `\u043A\u0430\u0441\u0430\u043D\u0438\u0435 \u043F\u043E \u0440\u0430\u0441\u043F\u0438\u0441\u0430\u043D\u0438\u044E \u0432 ${at}`,
613
+ nightMessages: (count) => `\u043E\u0442\u0432\u0435\u0442 \u043D\u0430 \u043D\u043E\u0447\u043D\u044B\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (${count})`,
614
+ farewell: "\u043F\u0440\u043E\u0449\u0430\u043D\u0438\u0435: \u0441\u0440\u043E\u043A \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B \u0432\u044B\u0448\u0435\u043B",
615
+ stateDeadline: (condition, stateTitle) => withTarget("\u0441\u0440\u043E\u043A \u0448\u0430\u0433\u0430 \u0432\u044B\u0448\u0435\u043B", condition, stateTitle),
616
+ overallDeadline: (condition, stateTitle) => withTarget("\u043E\u0431\u0449\u0438\u0439 \u0441\u0440\u043E\u043A \u0432\u044B\u0448\u0435\u043B", condition, stateTitle),
617
+ conditions: {
618
+ stateDays: (duration) => `\u0448\u0430\u0433 \u0434\u043B\u0438\u043B\u0441\u044F ${duration}`,
619
+ programDay: (day) => `\u043D\u0430\u0441\u0442\u0443\u043F\u0438\u043B \u0434\u0435\u043D\u044C ${day}`,
620
+ timeOfDay: (at) => `\u043D\u0430\u0441\u0442\u0443\u043F\u0438\u043B\u043E ${at}`,
621
+ silence: (duration) => `\u0442\u0438\u0448\u0438\u043D\u0430 ${duration}`,
622
+ inState: (duration) => `\u0432 \u0448\u0430\u0433\u0435 ${duration}`,
623
+ sinceStart: (duration) => `${duration} \u0441 \u043D\u0430\u0447\u0430\u043B\u0430 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440\u0430`
624
+ },
625
+ units: { day: "\u0434", hour: "\u0447", minute: "\u043C\u0438\u043D" },
626
+ lessThanMinute: "\u043C\u0435\u043D\u044C\u0448\u0435 \u043C\u0438\u043D\u0443\u0442\u044B"
627
+ };
628
+ var englishFlowNoticeLabels = {
629
+ wroteFirst: (reasons) => `The agent wrote first \u2014 ${reasons}`,
630
+ separator: ", ",
631
+ silentFor: (duration) => `the person was silent for ${duration}`,
632
+ silence: "a nudge after silence",
633
+ morning: "a new day's morning",
634
+ daily: (at) => `a scheduled nudge at ${at}`,
635
+ nightMessages: (count) => `a reply to night messages (${count})`,
636
+ farewell: "a farewell: the program is over",
637
+ stateDeadline: (condition, stateTitle) => withTarget("the step's time is up", condition, stateTitle),
638
+ overallDeadline: (condition, stateTitle) => withTarget("the overall time is up", condition, stateTitle),
639
+ conditions: {
640
+ stateDays: (duration) => `the step lasted ${duration}`,
641
+ programDay: (day) => `day ${day} began`,
642
+ timeOfDay: (at) => `it's ${at}`,
643
+ silence: (duration) => `silence for ${duration}`,
644
+ inState: (duration) => `${duration} in the step`,
645
+ sinceStart: (duration) => `${duration} since the start`
646
+ },
647
+ units: { day: "d", hour: "h", minute: "min" },
648
+ lessThanMinute: "less than a minute"
649
+ };
650
+
601
651
  // src/labels/test-chat.ts
602
652
  var russianFlowTestChatLabels = {
603
653
  title: "\u0422\u0435\u0441\u0442\u043E\u0432\u044B\u0439 \u0447\u0430\u0442",
@@ -610,6 +660,11 @@ var russianFlowTestChatLabels = {
610
660
  close: "\u0417\u0430\u043A\u0440\u044B\u0442\u044C",
611
661
  failed: "\u041D\u0435 \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u043E\u0441\u044C \u2014 \u043F\u043E\u043F\u0440\u043E\u0431\u0443\u0439\u0442\u0435 \u0435\u0449\u0451 \u0440\u0430\u0437",
612
662
  nextDay: "\u041A \u043D\u0430\u0447\u0430\u043B\u0443 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0433\u043E \u0434\u043D\u044F",
663
+ nextFirstMessage: "\u041A \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u043C\u0443 \u043D\u0430\u043F\u043E\u043C\u0438\u043D\u0430\u043D\u0438\u044E",
664
+ nothingScheduled: "\u0412\u043F\u0435\u0440\u0435\u0434\u0438 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439 \u0441\u0430\u043C \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u043F\u0438\u0448\u0435\u0442",
665
+ customTime: "\u0421\u0432\u043E\u0451 \u0432\u0440\u0435\u043C\u044F",
666
+ customTimePlaceholder: "15 \u043C\u0438\u043D, 1 \u0447 30 \u043C\u0438\u043D, 2 \u0434\u043D\u044F",
667
+ customTimeInvalid: "\u041D\u0435 \u043F\u043E\u043D\u044F\u043B \u0432\u0440\u0435\u043C\u044F \u2014 \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440, \xAB1 \u0447 30 \u043C\u0438\u043D\xBB \u0438\u043B\u0438 \xAB2 \u0434\u043D\u044F\xBB",
613
668
  day: (day, totalDays) => totalDays === void 0 ? `\u0414\u0435\u043D\u044C ${day}` : `\u0414\u0435\u043D\u044C ${day} \u0438\u0437 ${totalDays}`,
614
669
  beforeFirstDay: "\u0414\u043E \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0434\u043D\u044F"
615
670
  };
@@ -624,6 +679,11 @@ var englishFlowTestChatLabels = {
624
679
  close: "Close",
625
680
  failed: "Something went wrong \u2014 try again",
626
681
  nextDay: "To the start of the next day",
682
+ nextFirstMessage: "To the next reminder",
683
+ nothingScheduled: "The flow won't write first from here on",
684
+ customTime: "Custom time",
685
+ customTimePlaceholder: "15 min, 1h 30m, 2 days",
686
+ customTimeInvalid: "Couldn't read the time \u2014 try \u201C1h 30m\u201D or \u201C2 days\u201D",
627
687
  day: (day, totalDays) => totalDays === void 0 ? `Day ${day}` : `Day ${day} of ${totalDays}`,
628
688
  beforeFirstDay: "Before day one"
629
689
  };
@@ -633,13 +693,15 @@ var russianFlowLabels = {
633
693
  editor: russianFlowEditorLabels,
634
694
  clientCard: russianClientCardLabels,
635
695
  migrate: russianFlowMigrateLabels,
636
- testChat: russianFlowTestChatLabels
696
+ testChat: russianFlowTestChatLabels,
697
+ notices: russianFlowNoticeLabels
637
698
  };
638
699
  var englishFlowLabels = {
639
700
  editor: englishFlowEditorLabels,
640
701
  clientCard: englishClientCardLabels,
641
702
  migrate: englishFlowMigrateLabels,
642
- testChat: englishFlowTestChatLabels
703
+ testChat: englishFlowTestChatLabels,
704
+ notices: englishFlowNoticeLabels
643
705
  };
644
706
 
645
707
  // src/react/flow-labels.ts
@@ -2486,6 +2548,84 @@ function ClientCardPanel({ skin, ...options }) {
2486
2548
  // src/react/test-chat/flow-test-chat.tsx
2487
2549
  import { createContext as createContext3, useContext as useContext3, useMemo as useMemo5 } from "react";
2488
2550
 
2551
+ // src/react/notice-lines.ts
2552
+ var MINUTE = 6e4;
2553
+ function flowNoticeLines(labels = russianFlowNoticeLabels) {
2554
+ const line = ({ data }) => {
2555
+ const parsed = firstMessageData(data);
2556
+ return parsed ? noticeLine(parsed, labels) : void 0;
2557
+ };
2558
+ return { [FLOW_TIMER_NOTICE]: line, [FLOW_DEADLINE_NOTICE]: line };
2559
+ }
2560
+ function firstMessageData(data) {
2561
+ if (typeof data !== "object" || data === null) return void 0;
2562
+ const { reasons, silentMs, exit } = data;
2563
+ if (!Array.isArray(reasons) || reasons.length === 0) return void 0;
2564
+ return {
2565
+ reasons: reasons.filter((reason) => typeof reason === "object" && reason !== null && typeof reason.kind === "string"),
2566
+ ...typeof silentMs === "number" && Number.isFinite(silentMs) && { silentMs },
2567
+ ...typeof exit === "object" && exit !== null && { exit }
2568
+ };
2569
+ }
2570
+ function noticeLine({ reasons, silentMs, exit }, labels) {
2571
+ const silentFor = silentMs === void 0 ? void 0 : labels.silentFor(durationText2(silentMs, labels));
2572
+ const parts = reasons.flatMap((reason) => {
2573
+ switch (reason.kind) {
2574
+ case "silence":
2575
+ return [silentFor ?? labels.silence];
2576
+ case "morning":
2577
+ return [labels.morning];
2578
+ case "daily":
2579
+ return [labels.daily(reason.at)];
2580
+ case "night_messages":
2581
+ return [labels.nightMessages(reason.count)];
2582
+ case "farewell":
2583
+ return [labels.farewell];
2584
+ case "transition":
2585
+ return [deadlineLine(exit, labels)];
2586
+ default:
2587
+ return [];
2588
+ }
2589
+ });
2590
+ if (parts.length === 0) return void 0;
2591
+ const withSilence = silentFor && !reasons.some((reason) => reason.kind === "silence") ? [...parts, silentFor] : parts;
2592
+ return labels.wroteFirst(withSilence.join(labels.separator));
2593
+ }
2594
+ function deadlineLine(exit, labels) {
2595
+ const when = exit?.when;
2596
+ const title = typeof exit?.stateTitle === "string" && exit.stateTitle ? exit.stateTitle : void 0;
2597
+ const condition = when?.type === "timer" ? timerText(when, labels) : void 0;
2598
+ return when?.type === "timer" && when.from === "since_start" ? labels.overallDeadline(condition, title) : labels.stateDeadline(condition, title);
2599
+ }
2600
+ var UNIT_MS = { min: MINUTE, h: 60 * MINUTE, d: 24 * 60 * MINUTE };
2601
+ function timerText(when, labels) {
2602
+ const { conditions } = labels;
2603
+ switch (when.from) {
2604
+ case "state_days":
2605
+ return conditions.stateDays(durationText2(when.days * UNIT_MS.d, labels));
2606
+ case "program_day":
2607
+ return conditions.programDay(when.day);
2608
+ case "time_of_day":
2609
+ return conditions.timeOfDay(when.at);
2610
+ default: {
2611
+ const span = UNIT_MS[when.unit];
2612
+ if (span === void 0) return void 0;
2613
+ const duration = durationText2(when.amount * span, labels);
2614
+ return when.from === "silence" ? conditions.silence(duration) : when.from === "in_state" ? conditions.inState(duration) : conditions.sinceStart(duration);
2615
+ }
2616
+ }
2617
+ }
2618
+ function durationText2(ms, labels) {
2619
+ const minutes = Math.round(ms / MINUTE);
2620
+ if (minutes < 1) return labels.lessThanMinute;
2621
+ const { day, hour, minute } = labels.units;
2622
+ return [
2623
+ [Math.floor(minutes / (24 * 60)), day],
2624
+ [Math.floor(minutes / 60) % 24, hour],
2625
+ [minutes % 60, minute]
2626
+ ].filter(([amount]) => amount > 0).map(([amount, unit]) => `${amount} ${unit}`).join(" ");
2627
+ }
2628
+
2489
2629
  // src/react/test-chat/use-flow-test-chat.ts
2490
2630
  import { useCallback as useCallback5, useEffect as useEffect4, useRef as useRef4, useState as useState6 } from "react";
2491
2631
  var RUN_POLL_MS = 1e3;
@@ -2503,6 +2643,7 @@ function useFlowTestChat(connection, flow) {
2503
2643
  const [timeOffset, setTimeOffset] = useState6(0);
2504
2644
  const [busy, setBusy] = useState6(false);
2505
2645
  const [failed, setFailed] = useState6(false);
2646
+ const [nothingScheduled, setNothingScheduled] = useState6(false);
2506
2647
  const [run, setRun] = useState6(null);
2507
2648
  const latest = useRef4({ connection, flow, chatId });
2508
2649
  latest.current = { connection, flow, chatId };
@@ -2523,9 +2664,14 @@ function useFlowTestChat(connection, flow) {
2523
2664
  const work = useCallback5(async (job) => {
2524
2665
  setBusy(true);
2525
2666
  setFailed(false);
2667
+ setNothingScheduled(false);
2526
2668
  try {
2527
2669
  await job();
2528
2670
  } catch (error) {
2671
+ if (error?.code === "nothing_scheduled") {
2672
+ setNothingScheduled(true);
2673
+ return;
2674
+ }
2529
2675
  console.error("[flow] test chat:", error);
2530
2676
  setFailed(true);
2531
2677
  } finally {
@@ -2556,6 +2702,7 @@ function useFlowTestChat(connection, flow) {
2556
2702
  timeOffset,
2557
2703
  busy,
2558
2704
  failed,
2705
+ nothingScheduled,
2559
2706
  stateId: chatId === null ? null : run?.state ?? flow.start,
2560
2707
  visited: run?.visited ?? NOTHING_VISITED,
2561
2708
  day: chatId === null ? null : run?.day ?? null,
@@ -2566,18 +2713,19 @@ function useFlowTestChat(connection, flow) {
2566
2713
  close: () => setOpened(false),
2567
2714
  reset: () => void start(),
2568
2715
  advance: (ms) => shift2((id) => latest.current.connection.advance(id, ms)),
2569
- nextDay: connection.advanceToNextDay ? () => shift2((id) => latest.current.connection.advanceToNextDay(id)) : null
2716
+ nextDay: connection.advanceToNextDay ? () => shift2((id) => latest.current.connection.advanceToNextDay(id)) : null,
2717
+ nextFirstMessage: connection.advanceToNextFirstMessage ? () => shift2((id) => latest.current.connection.advanceToNextFirstMessage(id)) : null
2570
2718
  };
2571
2719
  }
2572
2720
 
2573
2721
  // src/react/test-chat/flow-test-chat.tsx
2574
2722
  import { jsx as jsx4 } from "react/jsx-runtime";
2575
- var MINUTE = 6e4;
2576
- var HOUR = 60 * MINUTE;
2723
+ var MINUTE2 = 6e4;
2724
+ var HOUR = 60 * MINUTE2;
2577
2725
  var DAY = 24 * HOUR;
2578
- var DEFAULT_ADVANCE_STEPS = [20 * MINUTE, 3 * HOUR, DAY];
2726
+ var DEFAULT_ADVANCE_STEPS = [20 * MINUTE2, 3 * HOUR, DAY];
2579
2727
  function formatDuration(ms, units) {
2580
- const minutes = Math.round(ms / MINUTE);
2728
+ const minutes = Math.round(ms / MINUTE2);
2581
2729
  const parts = [
2582
2730
  [Math.floor(minutes / (24 * 60)), units.day],
2583
2731
  [Math.floor(minutes / 60) % 24, units.hour],
@@ -2620,7 +2768,8 @@ function withTestChat(skin) {
2620
2768
  return { ...skin, FlowHeader: Header, FlowEditorFrame: Frame, FlowStateNode: StateNode };
2621
2769
  }
2622
2770
  function FlowTestChat({ testChat, advanceSteps = DEFAULT_ADVANCE_STEPS, skin, ...editor }) {
2623
- const labels = useFlowLabels().testChat;
2771
+ const { testChat: labels, notices } = useFlowLabels();
2772
+ const noticeLines = useMemo5(() => flowNoticeLines(notices), [notices]);
2624
2773
  const controller = useFlowTestChat(testChat, editor.value);
2625
2774
  const testSkin = useMemo5(() => withTestChat(skin), [skin]);
2626
2775
  const state = controller.stateId === null ? void 0 : stateById(editor.value, controller.stateId);
@@ -2633,11 +2782,17 @@ function FlowTestChat({ testChat, advanceSteps = DEFAULT_ADVANCE_STEPS, skin, ..
2633
2782
  onAdvance: controller.advance,
2634
2783
  day: formatDayLine(controller.day, (editor.now?.() ?? Date.now()) + controller.timeOffset, editor.timeZone ?? browserTimeZone2(), labels),
2635
2784
  ...controller.nextDay && editor.value.schedule ? { onNextDay: controller.nextDay } : {},
2785
+ ...controller.nextFirstMessage && { onNextFirstMessage: controller.nextFirstMessage, nothingScheduled: controller.nothingScheduled },
2786
+ onAdvanceText: (text) => {
2787
+ const ms = parseDuration(text);
2788
+ if (ms !== void 0) controller.advance(ms);
2789
+ return ms !== void 0;
2790
+ },
2636
2791
  busy: controller.busy,
2637
2792
  failed: controller.failed,
2638
2793
  onReset: controller.reset,
2639
2794
  onClose: controller.close,
2640
- children: controller.chatId === null ? null : testChat.renderChat(controller.chatId)
2795
+ children: controller.chatId === null ? null : testChat.renderChat(controller.chatId, { noticeLines })
2641
2796
  }
2642
2797
  ) : null;
2643
2798
  return /* @__PURE__ */ jsx4(TestChatContext.Provider, { value: { controller, panel }, children: /* @__PURE__ */ jsx4(FlowEditor, { skin: testSkin, ...editor }) });
@@ -2654,11 +2809,14 @@ export {
2654
2809
  chooseNothing,
2655
2810
  conditionText,
2656
2811
  englishFlowLabels,
2812
+ englishFlowNoticeLabels,
2813
+ flowNoticeLines,
2657
2814
  formatClockOffset,
2658
2815
  formatDayLine,
2659
2816
  formatDuration,
2660
2817
  nextQuestionField,
2661
2818
  russianFlowLabels,
2819
+ russianFlowNoticeLabels,
2662
2820
  sameFlow,
2663
2821
  setKnownValue,
2664
2822
  simulationChoices,