@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/CHANGELOG.md +24 -0
- package/dist/{chunk-LT4QELAZ.js → chunk-7VR4YNTJ.js} +121 -14
- package/dist/chunk-7VR4YNTJ.js.map +1 -0
- package/dist/chunk-KRZMJJKG.js +817 -0
- package/dist/chunk-KRZMJJKG.js.map +1 -0
- package/dist/chunk-L7XNC3RB.js +71 -0
- package/dist/chunk-L7XNC3RB.js.map +1 -0
- package/dist/format.js +9 -7
- package/dist/index.js +121 -628
- package/dist/index.js.map +1 -1
- package/dist/react.js +169 -11
- package/dist/react.js.map +1 -1
- package/dist/types/format/index.d.ts +1 -0
- package/dist/types/format/parse-duration.d.ts +5 -0
- package/dist/types/labels/index.d.ts +5 -1
- package/dist/types/labels/notices.d.ts +43 -0
- package/dist/types/labels/test-chat.d.ts +8 -0
- package/dist/types/react/flow-editor-skin.d.ts +6 -0
- package/dist/types/react/index.d.ts +2 -0
- package/dist/types/react/notice-lines.d.ts +14 -0
- package/dist/types/react/test-chat/use-flow-test-chat.d.ts +21 -2
- package/dist/types/react.d.ts +2 -2
- package/dist/types/runtime/first-reasons-notice.d.ts +6 -1
- package/dist/types/runtime/flow-types.d.ts +30 -7
- package/dist/types/runtime/index.d.ts +1 -1
- package/dist/types/runtime/notice-screen.d.ts +5 -0
- package/dist/types/runtime/test-clock.d.ts +7 -4
- package/package.json +4 -4
- package/dist/chunk-H3AN5YUN.js +0 -165
- package/dist/chunk-H3AN5YUN.js.map +0 -1
- package/dist/chunk-KGWXTWBZ.js +0 -100
- package/dist/chunk-KGWXTWBZ.js.map +0 -1
- package/dist/chunk-LT4QELAZ.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @ykurilov/agent-chat-flows
|
|
2
2
|
|
|
3
|
+
## 0.65.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 814ee7f: Пометка «Агент написал первым — человек молчал 1 ч 7 мин» перед ответом, который агент написал сам. Ядро: право чата `appNotices` (по умолчанию выключено) — служебные пометки приложения (`kind: "app"`) уходят в историю без текста для модели и только через белый список `appNoticeViews` (имя → безопасные данные для экрана; без функции пометка не уходит); `modelDetails: false` выключает и `appNotices`. React: `noticeLines` у `AgentChat` (имя пометки → строка), договор скина 27 — необязательный `ServiceLine`. Сценарии: в данных пометки `silentMs` (от последнего сообщения человека) и `exit`, проекция для экрана регистрируется в `withChat`, готовые строки `flowNoticeLines()` с подписями `russianFlowNoticeLabels`/`englishFlowNoticeLabels` (`FlowLabels.notices`); `FlowTestChat` передаёт их в `renderChat(chatId, { noticeLines })`. Тандем рисует `ServiceLine`. Как подключить — CHANGES.md, раздел 0.65.0.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [814ee7f]
|
|
12
|
+
- @ykurilov/agent-chat-core@0.65.0
|
|
13
|
+
- @ykurilov/agent-chat-react@0.65.0
|
|
14
|
+
|
|
15
|
+
## 0.64.0
|
|
16
|
+
|
|
17
|
+
### Minor Changes
|
|
18
|
+
|
|
19
|
+
- 1ea05c0: Тестовый чат: кнопка «К следующему напоминанию» проматывает часы ровно к моменту, когда сценарий сам напишет первым (касание, утреннее, выход по сроку, «срок вышел», «не завершил»; ночь и потолки учтены), и поле «своё время» («15 мин», «1 ч 30 мин», «2 дня»). Сервер: `flow.advanceTestClock({ ownerId, chatId, to: "next_first_message" })` → `{ timeOffset, reason }`, впереди ничего — `FlowTestClockError` с `code: "nothing_scheduled"`. Браузер: `FlowTestChatConnection.advanceToNextFirstMessage`. Разбор длительности — `parseDuration` из `/format`. Скин: необязательные `onNextFirstMessage`, `nothingScheduled`, `onAdvanceText` и пять надписей. Как подключить — CHANGES.md, раздел 0.64.0.
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [99cded9]
|
|
24
|
+
- @ykurilov/agent-chat-core@0.64.0
|
|
25
|
+
- @ykurilov/agent-chat-react@0.64.0
|
|
26
|
+
|
|
3
27
|
## 0.63.2
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
|
@@ -276,6 +276,30 @@ function withoutEmpty(object, keep = []) {
|
|
|
276
276
|
);
|
|
277
277
|
}
|
|
278
278
|
|
|
279
|
+
// src/format/parse-duration.ts
|
|
280
|
+
var MINUTE_MS = 6e4;
|
|
281
|
+
var HOUR_MS = 60 * MINUTE_MS;
|
|
282
|
+
var DAY_MS = 24 * HOUR_MS;
|
|
283
|
+
var UNIT_MS = Object.fromEntries([
|
|
284
|
+
...["\u043C", "\u043C\u0438\u043D", "\u043C\u0438\u043D\u0443\u0442\u0430", "\u043C\u0438\u043D\u0443\u0442\u044B", "\u043C\u0438\u043D\u0443\u0442", "\u043C\u0438\u043D\u0443\u0442\u0443", "m", "min", "mins", "minute", "minutes"].map((word) => [word, MINUTE_MS]),
|
|
285
|
+
...["\u0447", "\u0447\u0430\u0441", "\u0447\u0430\u0441\u0430", "\u0447\u0430\u0441\u043E\u0432", "h", "hr", "hrs", "hour", "hours"].map((word) => [word, HOUR_MS]),
|
|
286
|
+
...["\u0434", "\u0434\u043D", "\u0434\u0435\u043D\u044C", "\u0434\u043D\u044F", "\u0434\u043D\u0435\u0439", "d", "day", "days"].map((word) => [word, DAY_MS])
|
|
287
|
+
]);
|
|
288
|
+
var PART = /\s*(\d+(?:[.,]\d+)?)\s*([a-zа-яё]+)\.?\s*(?:(?:,|и(?![a-zа-яё])|and(?![a-zа-яё]))\s*)?/uy;
|
|
289
|
+
function parseDuration(text) {
|
|
290
|
+
const input = text.trim().toLowerCase();
|
|
291
|
+
if (!input) return void 0;
|
|
292
|
+
PART.lastIndex = 0;
|
|
293
|
+
let total = 0;
|
|
294
|
+
while (PART.lastIndex < input.length) {
|
|
295
|
+
const match = PART.exec(input);
|
|
296
|
+
const unit = match ? UNIT_MS[match[2]] : void 0;
|
|
297
|
+
if (!match || unit === void 0) return void 0;
|
|
298
|
+
total += Number(match[1].replace(",", ".")) * unit;
|
|
299
|
+
}
|
|
300
|
+
return total > 0 ? Math.round(total) : void 0;
|
|
301
|
+
}
|
|
302
|
+
|
|
279
303
|
// src/engine/flow-run.ts
|
|
280
304
|
var FLOW_STATE_KEY = "flow";
|
|
281
305
|
var FLOW_PATH_LIMIT = 50;
|
|
@@ -454,7 +478,7 @@ function localDay(at, timeZone) {
|
|
|
454
478
|
|
|
455
479
|
// src/engine/flow-days.ts
|
|
456
480
|
var DAY_MINUTES = 24 * 60;
|
|
457
|
-
var
|
|
481
|
+
var DAY_MS2 = 864e5;
|
|
458
482
|
var dayStartMinutes = (schedule2) => parseClock(schedule2.dayStart) ?? 0;
|
|
459
483
|
var nightMinutes = (schedule2) => parseClock(schedule2.nightFrom ?? DEFAULT_FIRST_MESSAGES.quietHours.from) ?? 0;
|
|
460
484
|
var dateKey = ({ year, month, day }) => `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
@@ -465,7 +489,7 @@ var dateOf = (key) => {
|
|
|
465
489
|
var shiftDate = (key, days) => dateKey(nextDate(dateOf(key), days));
|
|
466
490
|
var daysBetween = (from, to) => {
|
|
467
491
|
const [a, b] = [dateOf(from), dateOf(to)];
|
|
468
|
-
return Math.round((Date.UTC(b.year, b.month - 1, b.day) - Date.UTC(a.year, a.month - 1, a.day)) /
|
|
492
|
+
return Math.round((Date.UTC(b.year, b.month - 1, b.day) - Date.UTC(a.year, a.month - 1, a.day)) / DAY_MS2);
|
|
469
493
|
};
|
|
470
494
|
var later = (a, b) => a > b ? a : b;
|
|
471
495
|
var eveningNight = (schedule2) => nightMinutes(schedule2) > dayStartMinutes(schedule2);
|
|
@@ -559,8 +583,8 @@ function nudgeRings(flow, run, timeZone) {
|
|
|
559
583
|
const entryDay = flowDay(run, run.enteredAt, timeZone, schedule2).day;
|
|
560
584
|
return { ring: own, until: programDayDue(run, entryDay + own.days, timeZone, schedule2), after: common };
|
|
561
585
|
}
|
|
562
|
-
var
|
|
563
|
-
var durationMs = (duration2) => duration2.amount *
|
|
586
|
+
var UNIT_MS2 = { min: 6e4, h: 36e5, d: 864e5 };
|
|
587
|
+
var durationMs = (duration2) => duration2.amount * UNIT_MS2[duration2.unit];
|
|
564
588
|
var DEFAULT_SKIP_MORNING_MIN = 180;
|
|
565
589
|
function nextNudge(flow, run, now, timeZone, context = {}) {
|
|
566
590
|
const rings = nudgesRunning(flow, run, timeZone, context);
|
|
@@ -632,7 +656,7 @@ function nextNightReply(flow, run, timeZone, context = {}) {
|
|
|
632
656
|
|
|
633
657
|
// src/engine/timers.ts
|
|
634
658
|
var FLOW_DEADLINE_EXIT = "@deadline";
|
|
635
|
-
var
|
|
659
|
+
var UNIT_MS3 = { min: 6e4, h: 36e5, d: 864e5 };
|
|
636
660
|
function actualEntry(run) {
|
|
637
661
|
const last = run.path.at(-1);
|
|
638
662
|
return last && last.state === run.state ? last.at : run.enteredAt;
|
|
@@ -640,13 +664,13 @@ function actualEntry(run) {
|
|
|
640
664
|
function timerDueAt(condition, run, timeZone, schedule2) {
|
|
641
665
|
switch (condition.from) {
|
|
642
666
|
case "in_state":
|
|
643
|
-
return run.enteredAt + condition.amount *
|
|
667
|
+
return run.enteredAt + condition.amount * UNIT_MS3[condition.unit];
|
|
644
668
|
case "since_start":
|
|
645
|
-
return run.startedAt + condition.amount *
|
|
669
|
+
return run.startedAt + condition.amount * UNIT_MS3[condition.unit];
|
|
646
670
|
case "silence":
|
|
647
671
|
if (run.lastAgentAt === null) return null;
|
|
648
672
|
if (run.lastHumanAt !== null && run.lastHumanAt > run.lastAgentAt) return null;
|
|
649
|
-
return run.lastAgentAt + condition.amount *
|
|
673
|
+
return run.lastAgentAt + condition.amount * UNIT_MS3[condition.unit];
|
|
650
674
|
case "time_of_day": {
|
|
651
675
|
const minutes = parseClock(condition.at);
|
|
652
676
|
return minutes === null ? null : nextClockTime(actualEntry(run), minutes, timeZone);
|
|
@@ -921,6 +945,38 @@ function withNudgeStep(run, step, own) {
|
|
|
921
945
|
return { ...withoutOwnStep(run), nudgeStep: step, ...own && { nudgeStepOwn: true } };
|
|
922
946
|
}
|
|
923
947
|
|
|
948
|
+
// src/engine/first-messages.ts
|
|
949
|
+
function resolveFirstMessages(setting) {
|
|
950
|
+
if (setting === false) return null;
|
|
951
|
+
const rules = {
|
|
952
|
+
quietHours: { ...DEFAULT_FIRST_MESSAGES.quietHours, ...setting?.quietHours },
|
|
953
|
+
maxPerDay: setting?.maxPerDay ?? DEFAULT_FIRST_MESSAGES.maxPerDay,
|
|
954
|
+
...setting?.canWriteNow ? { canWriteNow: setting.canWriteNow } : {}
|
|
955
|
+
};
|
|
956
|
+
for (const clock2 of [rules.quietHours.from, rules.quietHours.to]) {
|
|
957
|
+
if (parseClock(clock2) === null) throw new Error(`\u0422\u0438\u0445\u0438\u0435 \u0447\u0430\u0441\u044B \u043F\u0438\u0448\u0443\u0442\u0441\u044F \u043A\u0430\u043A \xAB\u0427\u0427:\u041C\u041C\xBB, \u043D\u0430\u043F\u0440\u0438\u043C\u0435\u0440 \xAB21:00\xBB, \u0430 \u043D\u0435 \xAB${clock2}\xBB.`);
|
|
958
|
+
}
|
|
959
|
+
return rules;
|
|
960
|
+
}
|
|
961
|
+
function isQuiet(quietHours, at, timeZone) {
|
|
962
|
+
const from = parseClock(quietHours.from);
|
|
963
|
+
const to = parseClock(quietHours.to);
|
|
964
|
+
const now = clockMinutes(at, timeZone);
|
|
965
|
+
if (from === to) return false;
|
|
966
|
+
return from < to ? now >= from && now < to : now >= from || now < to;
|
|
967
|
+
}
|
|
968
|
+
function firstAwake(quietHours, at, timeZone) {
|
|
969
|
+
return isQuiet(quietHours, at, timeZone) ? nextClockTime(at, parseClock(quietHours.to), timeZone) : at;
|
|
970
|
+
}
|
|
971
|
+
function firstMessageSlot(rules, input) {
|
|
972
|
+
const { at, timeZone } = input;
|
|
973
|
+
if (input.sentToday >= rules.maxPerDay) {
|
|
974
|
+
return { allowed: false, retryAt: firstAwake(rules.quietHours, startOfNextDay(at, timeZone), timeZone) };
|
|
975
|
+
}
|
|
976
|
+
const awake = firstAwake(rules.quietHours, at, timeZone);
|
|
977
|
+
return awake === at ? { allowed: true } : { allowed: false, retryAt: awake };
|
|
978
|
+
}
|
|
979
|
+
|
|
924
980
|
// src/engine/side-questions.ts
|
|
925
981
|
function isFieldStale(field, updatedAt, now) {
|
|
926
982
|
if (field.refreshAfterMonths === null) return false;
|
|
@@ -950,6 +1006,54 @@ function sideQuestion(flow, run, input) {
|
|
|
950
1006
|
return unknown ? { fieldId: unknown.id, reason: "unknown" } : null;
|
|
951
1007
|
}
|
|
952
1008
|
|
|
1009
|
+
// src/engine/migrate-run.ts
|
|
1010
|
+
function migrateRun(run, next, map, now) {
|
|
1011
|
+
const exists = (id2) => stateById(next, id2) !== void 0;
|
|
1012
|
+
const renamed = (id2) => !exists(id2) && map[id2] !== void 0 && exists(map[id2]) ? map[id2] : id2;
|
|
1013
|
+
const noteIds = new Set(next.chatNotes.map((field) => field.id));
|
|
1014
|
+
const notes = Object.fromEntries(Object.entries(run.notes).filter(([id2]) => noteIds.has(id2)));
|
|
1015
|
+
const path = run.path.map((entry) => ({ ...entry, state: renamed(entry.state), from: entry.from === null ? null : renamed(entry.from) }));
|
|
1016
|
+
const pausedClocks = Object.fromEntries(
|
|
1017
|
+
Object.entries(run.pausedClocks).map(([id2, ms]) => [renamed(id2), ms]).filter(([id2]) => exists(id2))
|
|
1018
|
+
);
|
|
1019
|
+
if (exists(run.state)) return { ...run, path, pausedClocks, notes };
|
|
1020
|
+
const targetId = map[run.state];
|
|
1021
|
+
if (targetId === void 0) {
|
|
1022
|
+
throw new Error(`\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \xAB${run.state}\xBB \u043D\u0435\u0442 \u0432 \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \xAB${next.id}\xBB, \u0430 \u043A\u0443\u0434\u0430 \u043F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0447\u0430\u0442 \u2014 \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u043E.`);
|
|
1023
|
+
}
|
|
1024
|
+
const target = stateById(next, targetId);
|
|
1025
|
+
if (!target) throw new Error(`\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \xAB${targetId}\xBB \u043D\u0435\u0442 \u0432 \u043D\u043E\u0432\u043E\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \xAB${next.id}\xBB: \u0442\u0443\u0434\u0430 \u043D\u0435 \u043F\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0447\u0430\u0442 \u0438\u0437 \xAB${run.state}\xBB.`);
|
|
1026
|
+
return {
|
|
1027
|
+
...run,
|
|
1028
|
+
state: target.id,
|
|
1029
|
+
enteredAt: now,
|
|
1030
|
+
pausedClocks: {},
|
|
1031
|
+
notes,
|
|
1032
|
+
path: appendPath(path, { state: target.id, at: now, tags: target.tags, from: run.state, exitId: null, decidedBy: "migrate" }),
|
|
1033
|
+
lastRejected: null
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
// src/engine/day-frame.ts
|
|
1038
|
+
var DEFAULT_HARD_FRAME_TEXT = "\u041D\u0430 \u0441\u0435\u0433\u043E\u0434\u043D\u044F \u0432\u0441\u0451 \u2014 \u0443\u0442\u0440\u043E\u043C \u043E\u0431\u044F\u0437\u0430\u0442\u0435\u043B\u044C\u043D\u043E \u043E\u0442\u0432\u0435\u0447\u0443!";
|
|
1039
|
+
function frameState(humanMessagesToday2, frame) {
|
|
1040
|
+
if (!frame) return "normal";
|
|
1041
|
+
const { softFrom, hardAt } = frame;
|
|
1042
|
+
if (hardAt !== void 0 && humanMessagesToday2 > hardAt) return "closed";
|
|
1043
|
+
if (hardAt !== void 0 && humanMessagesToday2 === hardAt) return "last";
|
|
1044
|
+
if (softFrom !== void 0 && humanMessagesToday2 >= softFrom) return "soft";
|
|
1045
|
+
return "normal";
|
|
1046
|
+
}
|
|
1047
|
+
function hardFrameText(frame) {
|
|
1048
|
+
return frame?.hardText?.trim() || DEFAULT_HARD_FRAME_TEXT;
|
|
1049
|
+
}
|
|
1050
|
+
function humanMessagesToday(run, at, timeZone, schedule2) {
|
|
1051
|
+
return run.dayCounts?.[dayInProgress(at, timeZone, schedule2)] ?? 0;
|
|
1052
|
+
}
|
|
1053
|
+
function dayClosedToday(dayClosedOn, at, timeZone, schedule2) {
|
|
1054
|
+
return dayClosedOn !== null && dayClosedOn === dayInProgress(at, timeZone, schedule2);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
953
1057
|
// src/check/app-checks.ts
|
|
954
1058
|
function pick(items, key) {
|
|
955
1059
|
if (!items || key === void 0) return void 0;
|
|
@@ -1223,6 +1327,7 @@ export {
|
|
|
1223
1327
|
catalogEventNames,
|
|
1224
1328
|
catalogEventLabel,
|
|
1225
1329
|
compactFlow,
|
|
1330
|
+
parseDuration,
|
|
1226
1331
|
FLOW_STATE_KEY,
|
|
1227
1332
|
FLOW_PATH_LIMIT,
|
|
1228
1333
|
FLOW_CHAIN_LIMIT,
|
|
@@ -1238,10 +1343,6 @@ export {
|
|
|
1238
1343
|
fieldValueFits,
|
|
1239
1344
|
dataConditionHolds,
|
|
1240
1345
|
returnTarget,
|
|
1241
|
-
parseClock,
|
|
1242
|
-
clockMinutes,
|
|
1243
|
-
nextClockTime,
|
|
1244
|
-
startOfNextDay,
|
|
1245
1346
|
localDay,
|
|
1246
1347
|
dayOf,
|
|
1247
1348
|
dayInProgress,
|
|
@@ -1271,7 +1372,6 @@ export {
|
|
|
1271
1372
|
describeCondition,
|
|
1272
1373
|
nextTransition,
|
|
1273
1374
|
startRun,
|
|
1274
|
-
appendPath,
|
|
1275
1375
|
applyTransition,
|
|
1276
1376
|
noteHumanMessage,
|
|
1277
1377
|
noteAgentAnswer,
|
|
@@ -1280,10 +1380,17 @@ export {
|
|
|
1280
1380
|
claimAllowed,
|
|
1281
1381
|
noteSideQuestion,
|
|
1282
1382
|
withNudgeStep,
|
|
1383
|
+
resolveFirstMessages,
|
|
1384
|
+
firstMessageSlot,
|
|
1283
1385
|
isFieldStale,
|
|
1284
1386
|
sideQuestion,
|
|
1387
|
+
migrateRun,
|
|
1388
|
+
frameState,
|
|
1389
|
+
hardFrameText,
|
|
1390
|
+
humanMessagesToday,
|
|
1391
|
+
dayClosedToday,
|
|
1285
1392
|
placeOfPath,
|
|
1286
1393
|
appIssues,
|
|
1287
1394
|
checkFlow
|
|
1288
1395
|
};
|
|
1289
|
-
//# sourceMappingURL=chunk-
|
|
1396
|
+
//# sourceMappingURL=chunk-7VR4YNTJ.js.map
|