@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/index.js CHANGED
@@ -1,32 +1,61 @@
1
1
  import {
2
2
  TIME_ZONE_TASK_ID,
3
- dayClosedToday,
4
- firstMessageSlot,
5
3
  flowJsonSchema,
6
- frameState,
7
- hardFrameText,
8
4
  hasSideTasks,
9
- humanMessagesToday,
10
- migrateRun,
11
5
  openSideTasks,
12
- resolveFirstMessages,
13
6
  sideTaskById,
14
7
  sideTaskFieldKnown,
15
8
  sideTaskRecord,
16
9
  sideTaskStanding
17
- } from "./chunk-H3AN5YUN.js";
10
+ } from "./chunk-L7XNC3RB.js";
18
11
  import {
19
12
  FACT_PREFIX,
13
+ FIRST_MESSAGE_RETRY_MS,
14
+ FLOW_DEADLINE_NOTICE,
15
+ FLOW_EVENT_NOTICE,
16
+ FLOW_TIMER_NOTICE,
17
+ FlowTestClockError,
20
18
  MAX_FACTS,
21
19
  MAX_FACT_LENGTH,
20
+ UNKNOWN_TIME_ZONE,
21
+ advance,
22
+ advanceTestClock,
23
+ afterEndOf,
24
+ chainLimitMessage,
25
+ chatRun,
26
+ chatTime,
27
+ chatWriteBlock,
22
28
  clientCardBlock,
23
29
  clientFactStore,
30
+ createChatTimeZones,
31
+ createFirstMessageRules,
32
+ createFlowFinish,
33
+ createFlowTimeZones,
34
+ currentRun,
35
+ endedRun,
36
+ exactFirstText,
24
37
  factEntry,
25
38
  factId,
26
39
  fieldValueText,
40
+ firstReasonsNotice,
41
+ flowPrepareTurn,
42
+ flowTestChatData,
43
+ ianaTimeZone,
27
44
  isFactEntry,
28
- sameFact
29
- } from "./chunk-KGWXTWBZ.js";
45
+ judgeExits,
46
+ pageTimeZone,
47
+ personTimeZone,
48
+ reportTransition,
49
+ runOutsideTurn,
50
+ sameFact,
51
+ silentMsOf,
52
+ stateCard,
53
+ stateGoal,
54
+ storedRun,
55
+ testChatOffset,
56
+ transitionReport,
57
+ withPending
58
+ } from "./chunk-KRZMJJKG.js";
30
59
  import {
31
60
  AGENT_FLOW_FORMAT,
32
61
  AGENT_FLOW_FORMAT_VERSION,
@@ -69,17 +98,21 @@ import {
69
98
  dayStartHoldUntil,
70
99
  deadlineAt,
71
100
  deferredWake,
72
- describeCondition,
73
101
  dueReasons,
74
102
  endingWake,
75
103
  fieldById,
76
104
  fieldValueFits,
105
+ firstMessageSlot,
77
106
  flowDay,
107
+ frameState,
108
+ hardFrameText,
109
+ humanMessagesToday,
78
110
  isFieldStale,
79
111
  isFinal,
80
112
  isTouch,
81
113
  localDay,
82
114
  migrateFlow,
115
+ migrateRun,
83
116
  nextDayStartAfter,
84
117
  nextDue,
85
118
  nextFirstMessageAfter,
@@ -90,10 +123,11 @@ import {
90
123
  noteHumanMessage,
91
124
  noteRejectedClaim,
92
125
  noteSideQuestion,
93
- overdueOnEntry,
94
126
  parseBooleanAnswer,
127
+ parseDuration,
95
128
  parseFlow,
96
129
  placeOfPath,
130
+ resolveFirstMessages,
97
131
  returnTarget,
98
132
  sameText,
99
133
  sideQuestion,
@@ -105,7 +139,7 @@ import {
105
139
  withDayAnchor,
106
140
  withNudgeStep,
107
141
  withoutUndeliveredTouch
108
- } from "./chunk-LT4QELAZ.js";
142
+ } from "./chunk-7VR4YNTJ.js";
109
143
  import {
110
144
  appliedInTime,
111
145
  byVersionAndState,
@@ -172,6 +206,42 @@ function turnToolNames(source, catalog, input) {
172
206
  }
173
207
  }
174
208
 
209
+ // src/runtime/notice-screen.ts
210
+ var TIMER_FIELDS = ["type", "from", "days", "day", "at", "amount", "unit"];
211
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
212
+ var scalar = (value) => typeof value === "string" || typeof value === "number";
213
+ function screenReason(reason) {
214
+ if (!isRecord(reason) || typeof reason.kind !== "string") return void 0;
215
+ return {
216
+ kind: reason.kind,
217
+ ...reason.kind === "daily" && typeof reason.at === "string" && { at: reason.at },
218
+ ...reason.kind === "night_messages" && typeof reason.count === "number" && { count: reason.count }
219
+ };
220
+ }
221
+ function screenWhen(when) {
222
+ if (!isRecord(when) || when.type !== "timer") return void 0;
223
+ return Object.fromEntries(TIMER_FIELDS.flatMap((field) => scalar(when[field]) ? [[field, when[field]]] : []));
224
+ }
225
+ function screenExit(exit) {
226
+ if (!isRecord(exit)) return void 0;
227
+ const when = screenWhen(exit.when);
228
+ const stateTitle = typeof exit.stateTitle === "string" ? exit.stateTitle : void 0;
229
+ if (!when && stateTitle === void 0) return void 0;
230
+ return { ...stateTitle !== void 0 && { stateTitle }, ...when && { when } };
231
+ }
232
+ function flowNoticeScreenData(data) {
233
+ if (!isRecord(data) || !Array.isArray(data.reasons)) return void 0;
234
+ const reasons = data.reasons.flatMap((reason) => screenReason(reason) ?? []);
235
+ if (reasons.length === 0) return void 0;
236
+ const exit = screenExit(data.exit);
237
+ return {
238
+ reasons,
239
+ ...typeof data.silentMs === "number" && Number.isFinite(data.silentMs) && { silentMs: data.silentMs },
240
+ ...exit && { exit }
241
+ };
242
+ }
243
+ var FLOW_APP_NOTICE_VIEWS = { [FLOW_TIMER_NOTICE]: flowNoticeScreenData, [FLOW_DEADLINE_NOTICE]: flowNoticeScreenData };
244
+
175
245
  // src/prompt/rules/time-zone.ts
176
246
  var TIME_ZONE_RULES = `## \u0414\u043D\u0438 \u0438 \u0432\u0440\u0435\u043C\u044F
177
247
 
@@ -263,184 +333,9 @@ function afterEndRules(flow) {
263
333
  ${shared}` : AFTER_END_RULES;
264
334
  }
265
335
 
266
- // src/prompt/state-card.ts
267
- function judgeExits(flow, stateId) {
268
- const state = stateById(flow, stateId);
269
- if (!state) return [];
270
- return activeExits(flow, state).flatMap(({ exit }) => exit.when.type === "judge" ? [{ exitId: exit.id, text: exit.when.text }] : []);
271
- }
272
- function stateGoal(instructions) {
273
- return instructions.split("\n")[0].trim();
274
- }
275
- function stateCard(flow, run, present) {
276
- const state = stateById(flow, run.state);
277
- if (!state) return `\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \xAB${run.state}\xBB \u0432 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0438 \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E: \u0432\u0435\u0434\u0438 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440 \u043F\u043E \u043E\u0431\u0449\u0438\u043C \u043F\u0440\u0430\u0432\u0438\u043B\u0430\u043C.`;
278
- const [goal = "", ...rest] = state.instructions.split("\n");
279
- const lines = [`\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435: \xAB${state.title}\xBB`, `\u0426\u0435\u043B\u044C: ${goal.trim()}`];
280
- const how = rest.join("\n").trim();
281
- if (how) lines.push("", how);
282
- const fields = state.learn.map((id) => fieldById(flow, id)).flatMap((found) => found ? [`- ${found.field.name}: ${found.field.about}`] : []);
283
- if (fields.length > 0) lines.push("", "\u0423\u0437\u043D\u0430\u0442\u044C \u0437\u0434\u0435\u0441\u044C:", ...fields);
284
- const conditions = judgeExits(flow, state.id);
285
- if (isFinal(state)) {
286
- lines.push("", "\u042D\u0442\u043E \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F: \u0443\u0441\u043B\u043E\u0432\u0438\u0439 \u0432\u044B\u0445\u043E\u0434\u0430 \u043D\u0435\u0442.");
287
- } else if (conditions.length > 0) {
288
- lines.push("", "\u0423\u0441\u043B\u043E\u0432\u0438\u044F \u0432\u044B\u0445\u043E\u0434\u0430 (\u043D\u043E\u043C\u0435\u0440 \u2014 \u0434\u043B\u044F flow_exit):", ...conditions.map((condition, index) => `${index + 1}. ${condition.text}`));
289
- }
290
- const inSet = (name) => !present || present.has(name);
291
- const tools = state.tools.filter(inSet);
292
- if (tools.length > 0) lines.push("", `\u0418\u043D\u0441\u0442\u0440\u0443\u043C\u0435\u043D\u0442\u044B \u044D\u0442\u043E\u0433\u043E \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F: ${tools.join(", ")}.`);
293
- const unavailable = flow.tools.filter((name) => inSet(name) && !state.tools.includes(name));
294
- if (unavailable.length > 0) lines.push(`\u0421\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B: ${unavailable.join(", ")}.`);
295
- return lines.join("\n");
296
- }
297
-
298
336
  // src/runtime/note-tools.ts
299
337
  import { tool } from "ai";
300
338
  import { z } from "zod";
301
-
302
- // src/runtime/flow-events.ts
303
- async function reportTransition(handler, event, logError) {
304
- if (!handler) return;
305
- try {
306
- await handler(event);
307
- } catch (error) {
308
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${event.chatId} onTransition ${event.from} \u2192 ${event.to} \u0443\u043F\u0430\u043B: ${error instanceof Error ? error.message : String(error)}`);
309
- }
310
- }
311
- function storedRun(value) {
312
- return value && typeof value === "object" && typeof value.state === "string" ? value : null;
313
- }
314
- function currentRun(flow, values, now) {
315
- return storedRun(values?.[FLOW_STATE_KEY]) ?? startRun(flow, now);
316
- }
317
- function placedRun(flow, placement, now, timeZone) {
318
- const start = startRun(flow, now);
319
- const state = placement.stateId !== void 0 && stateById(flow, placement.stateId) || stateById(flow, flow.start);
320
- const startedAt = placement.startedAt ?? now;
321
- const enteredAt = placement.enteredAt ?? placement.startedAt ?? now;
322
- return {
323
- ...start,
324
- state: state.id,
325
- startedAt,
326
- enteredAt,
327
- path: [{ state: state.id, at: enteredAt, tags: state.tags, from: null, exitId: null, decidedBy: "start" }],
328
- ...placement.lastHumanAt !== void 0 && { lastHumanAt: placement.lastHumanAt },
329
- ...placement.lastAgentAt !== void 0 && { lastAgentAt: placement.lastAgentAt },
330
- ...placement.nudgeStep !== void 0 && { nudgeStep: placement.nudgeStep },
331
- ...placement.humanToday && flow.schedule && timeZone && { dayCounts: { [dayInProgress(placement.humanToday.at, timeZone, flow.schedule)]: placement.humanToday.count } }
332
- };
333
- }
334
- var msOf = (value) => value === void 0 ? void 0 : Date.parse(value);
335
- function recordPlacement(record) {
336
- const placement = record.placement;
337
- if (!placement) return null;
338
- const lastHumanAt = msOf(placement.lastHumanAt);
339
- const lastAgentAt = msOf(placement.lastAgentAt);
340
- return {
341
- stateId: record.stateId,
342
- startedAt: Date.parse(placement.startedAt),
343
- enteredAt: Date.parse(placement.enteredAt),
344
- ...lastHumanAt !== void 0 && { lastHumanAt },
345
- ...lastAgentAt !== void 0 && { lastAgentAt },
346
- ...placement.nudgeStep !== void 0 && { nudgeStep: placement.nudgeStep },
347
- ...placement.humanToday && { humanToday: { at: Date.parse(placement.humanToday.at), count: placement.humanToday.count } }
348
- };
349
- }
350
- function chatRun(flow, values, record, now, timeZone) {
351
- const stored = storedRun(values?.[FLOW_STATE_KEY]);
352
- if (stored) return stored;
353
- const placement = record ? recordPlacement(record) : null;
354
- return placement ? placedRun(flow, placement, now, timeZone) : startRun(flow, now);
355
- }
356
- async function personTimeZone(runtime, chat) {
357
- const known = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : (await runtime.store.getChat(chat.chatId))?.timeZone;
358
- return known || runtime.defaultTimeZone || Intl.DateTimeFormat().resolvedOptions().timeZone;
359
- }
360
- async function runOutsideTurn(runtime, chat, flow, values, now, record) {
361
- const stored = storedRun(values?.[FLOW_STATE_KEY]);
362
- if (stored) return stored;
363
- const found = record === void 0 ? await runtime.store.getChat(chat.chatId) : record;
364
- if (!found?.placement) return startRun(flow, now);
365
- const timeZone = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : found.timeZone || void 0;
366
- return chatRun(flow, values, found, now, timeZone);
367
- }
368
- function advance(flow, run, event, input) {
369
- let current = run;
370
- let step = event;
371
- let entered = false;
372
- let stopped = false;
373
- while (step) {
374
- const transition = nextTransition(flow, current, step, input.now);
375
- if (!transition) {
376
- if (current.chain >= FLOW_CHAIN_LIMIT && nextTransition(flow, { ...current, chain: 0 }, step, input.now)) {
377
- if (!stopped) input.onChainLimit(current);
378
- stopped = true;
379
- }
380
- const overdue = entered && !stopped && step.type === "data" ? overdueOnEntry(flow, current, input.now, input.timeZone) : null;
381
- step = overdue ? { type: "timer", exitId: overdue, timeZone: input.timeZone } : null;
382
- continue;
383
- }
384
- current = applyTransition(flow, current, transition);
385
- input.out.push(transition);
386
- entered = true;
387
- step = { type: "data", card: input.card };
388
- }
389
- return current;
390
- }
391
- function pendingEvent(transition, timeZone) {
392
- if (transition.decidedBy === "timer") return { type: "timer", exitId: transition.exitId, timeZone };
393
- return { type: "verdict", exitId: transition.exitId, probability: transition.probability ?? 1, decidedBy: "judge" };
394
- }
395
- function withPending(flow, run, pending, input) {
396
- let current = run;
397
- for (const change of pending) {
398
- if (change.kind === "transition") {
399
- if (change.branchHead !== run.answerId || current.state !== change.transition.from) continue;
400
- current = advance(flow, current, pendingEvent(change.transition, input.timeZone), input);
401
- } else if (change.kind === "event") {
402
- current = advance(flow, current, { type: "event", event: "app", name: change.name }, input);
403
- }
404
- }
405
- return current;
406
- }
407
- var chainLimitMessage = (chatId, run) => `[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} ${FLOW_CHAIN_LIMIT} \u043F\u0435\u0440\u0435\u0445\u043E\u0434\u043E\u0432 \u043F\u043E\u0434\u0440\u044F\u0434 \u0431\u0435\u0437 \u043E\u0442\u0432\u0435\u0442\u0430 \u0430\u0433\u0435\u043D\u0442\u0430, \u0434\u0432\u0438\u0436\u043E\u043A \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D \u0432 \xAB${run.state}\xBB`;
408
- function exitById(flow, exitId) {
409
- for (const state of flow.states) {
410
- const found = state.exits.find((exit) => exit.id === exitId);
411
- if (found) return found;
412
- }
413
- return flow.anyState.exits.find((exit) => exit.id === exitId);
414
- }
415
- function exactFirstText(flow, stateId) {
416
- return stateById(flow, stateId)?.firstMessage ?? null;
417
- }
418
- var sayExactly = (text) => `\u041F\u0435\u0440\u0432\u043E\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u044D\u0442\u043E\u0433\u043E \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u044F \u2014 \u0441\u043A\u0430\u0436\u0438 \u0440\u043E\u0432\u043D\u043E \u044D\u0442\u043E: \xAB${text}\xBB`;
419
- function transitionNotice(flow, transitions) {
420
- const lines = transitions.map((transition) => {
421
- const exit = exitById(flow, transition.exitId);
422
- const title = stateById(flow, transition.to)?.title ?? transition.to;
423
- return `\u0421\u0440\u0430\u0431\u043E\u0442\u0430\u043B\u043E: ${exit ? describeCondition(flow, exit.when) : transition.exitId} \u2192 \xAB${title}\xBB.`;
424
- });
425
- const last = transitions.at(-1);
426
- const first = exactFirstText(flow, last.to);
427
- if (first) lines.push(sayExactly(first));
428
- return {
429
- kind: "app",
430
- name: "flow_transition",
431
- title: lines.join("\n"),
432
- data: { from: transitions[0].from, to: last.to, exitIds: transitions.map((transition) => transition.exitId) }
433
- };
434
- }
435
- function transitionReport(flow, run, transitions) {
436
- const from = stateById(flow, transitions[0].from)?.title ?? transitions[0].from;
437
- const lines = [`\u041F\u0435\u0440\u0435\u0445\u043E\u0434: \xAB${from}\xBB \u2192 \xAB${stateById(flow, run.state)?.title ?? run.state}\xBB. \u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u0441\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0439 \u043F\u043E \u043D\u043E\u0432\u043E\u0439 \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0435.`, "", stateCard(flow, run)];
438
- const first = exactFirstText(flow, run.state);
439
- if (first) lines.push("", sayExactly(first));
440
- return lines.join("\n");
441
- }
442
-
443
- // src/runtime/note-tools.ts
444
339
  var FLOW_NOTE_TOOL = "flow_note";
445
340
  var CLIENT_CARD_TOOL = "client_card";
446
341
  var NO_FLOW = "\u0412 \u044D\u0442\u043E\u043C \u0447\u0430\u0442\u0435 \u043D\u0435\u0442 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F: \u0437\u0430\u043F\u0438\u0441\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u043A\u0443\u0434\u0430, \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0439 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440.";
@@ -673,105 +568,6 @@ function createClientCard(store, now, adapter, logError = () => {
673
568
  };
674
569
  }
675
570
 
676
- // src/runtime/first-message-retry.ts
677
- var FIRST_MESSAGE_RETRY_MS = 36e5;
678
- var FIRST_MESSAGE_MIN_RETRY_MS = 6e4;
679
-
680
- // src/runtime/first-message-rules.ts
681
- function chatWriteBlock(chat, at, timeZone, schedule) {
682
- if (chat.windowClosed) return { retryAt: null };
683
- const pausedUntil = chat.pausedUntil ? Date.parse(chat.pausedUntil) : 0;
684
- if (pausedUntil > at) return { retryAt: pausedUntil };
685
- if (schedule && dayClosedToday(chat.dayClosedOn, at, timeZone, schedule)) return { retryAt: nextDayStartAfter(at, timeZone, schedule) };
686
- return null;
687
- }
688
- function scenarioRules(rules, schedule) {
689
- if (!schedule) return rules;
690
- return {
691
- quietHours: { from: schedule.nightFrom ?? rules.quietHours.from, to: schedule.dayStart },
692
- maxPerDay: schedule.firstMessages?.maxPerDay ?? rules.maxPerDay
693
- };
694
- }
695
- function createFirstMessageRules(setting, store) {
696
- const rules = resolveFirstMessages(setting);
697
- if (!rules) return null;
698
- return {
699
- async check({ ownerId, chatId, at, timeZone, schedule, chat }) {
700
- const blocked = chat ? chatWriteBlock(chat, at, timeZone, schedule) : null;
701
- if (blocked) return { allowed: false, retryAt: blocked.retryAt };
702
- const sentToday = await store.countFirstMessages(ownerId, localDay(at, timeZone));
703
- const slot = firstMessageSlot(scenarioRules(rules, schedule), { at, timeZone, sentToday });
704
- if (!slot.allowed) return slot;
705
- const answer = rules.canWriteNow ? await rules.canWriteNow({ ownerId, chatId, at: new Date(at) }) : true;
706
- if (answer === true) return { allowed: true };
707
- return { allowed: false, retryAt: retryAtOf(answer, at) };
708
- },
709
- async record({ ownerId, at, timeZone }) {
710
- await store.addFirstMessage(ownerId, localDay(at, timeZone));
711
- }
712
- };
713
- }
714
- function retryAtOf(answer, at) {
715
- if (answer === "never") return null;
716
- const retryAt = typeof answer === "object" && answer !== null && answer.retryAt instanceof Date ? answer.retryAt.getTime() : Number.NaN;
717
- if (!Number.isFinite(retryAt)) return at + FIRST_MESSAGE_RETRY_MS;
718
- return Math.max(retryAt, at + FIRST_MESSAGE_MIN_RETRY_MS);
719
- }
720
- var UNKNOWN_TIME_ZONE = "";
721
- var serverTimeZone = () => Intl.DateTimeFormat().resolvedOptions().timeZone;
722
- function pageTimeZone(turnZone, appZone, sent) {
723
- if (sent !== void 0) return sent;
724
- return turnZone === (appZone ?? serverTimeZone()) ? null : turnZone;
725
- }
726
- function createChatTimeZones(input) {
727
- const warned = /* @__PURE__ */ new Set();
728
- return {
729
- of(chatId, stored) {
730
- if (stored) return stored;
731
- if (input.defaultTimeZone) return input.defaultTimeZone;
732
- const app = input.appTimeZone();
733
- if (app) return app;
734
- const server = serverTimeZone();
735
- if (!warned.has(chatId)) {
736
- warned.add(chatId);
737
- input.warn(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId}: \u043F\u043E\u044F\u0441 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043D (\u043D\u0435\u0442 \u043D\u0438 \u0441\u043E \u0441\u0442\u0440\u0430\u043D\u0438\u0446\u044B, \u043D\u0438 defaultTimeZone, \u043D\u0438 agent.timeZone) \u2014 \u0442\u0438\u0445\u0438\u0435 \u0447\u0430\u0441\u044B \u0438 \xAB\u0432\u0440\u0435\u043C\u044F \u0441\u0443\u0442\u043E\u043A\xBB \u043F\u043E \u043F\u043E\u044F\u0441\u0443 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 ${server}`);
738
- }
739
- return server;
740
- }
741
- };
742
- }
743
- function ianaTimeZone(value) {
744
- if (!value.trim()) return null;
745
- try {
746
- return new Intl.DateTimeFormat("en-US", { timeZone: value.trim() }).resolvedOptions().timeZone;
747
- } catch {
748
- return null;
749
- }
750
- }
751
- function createFlowTimeZones(input) {
752
- const { store } = input;
753
- const iso = () => new Date(input.now()).toISOString();
754
- const readPerson = (ownerId) => input.people ? input.people.readTimeZone(ownerId) : store.readTimeZone(ownerId);
755
- const writePerson = (ownerId, timeZone) => input.people ? input.people.writeTimeZone(ownerId, timeZone) : store.writeTimeZone(ownerId, timeZone, iso());
756
- async function known(ownerId, chatId) {
757
- const person = await readPerson(ownerId);
758
- if (person) return person;
759
- return (await store.getChat(chatId))?.timeZone || null;
760
- }
761
- return {
762
- known,
763
- fallback: input.fallback,
764
- async of(ownerId, chatId) {
765
- return await known(ownerId, chatId) ?? input.fallback(chatId);
766
- },
767
- async write(ownerId, chatId, timeZone) {
768
- if (await readPerson(ownerId) !== timeZone) await writePerson(ownerId, timeZone);
769
- const record = await store.getChat(chatId);
770
- if (record && record.timeZone !== timeZone) await store.updateChat(chatId, { timeZone }, iso());
771
- }
772
- };
773
- }
774
-
775
571
  // src/runtime/decisions-client.ts
776
572
  function decisionsUrl(baseUrl) {
777
573
  const base = baseUrl.replace(/\/+$/, "");
@@ -1008,334 +804,11 @@ function errorText2(error) {
1008
804
  import { randomUUID } from "crypto";
1009
805
  import { ChatNotFoundError as ChatNotFoundError2, SpendingLimitReachedError } from "@ykurilov/agent-chat-core";
1010
806
 
1011
- // src/runtime/test-clock.ts
1012
- var FLOW_TEST_CHAT_KEY = "flowTestChat";
1013
- function flowTestChatData() {
1014
- return { [FLOW_TEST_CHAT_KEY]: { timeOffset: 0 } };
1015
- }
1016
- function testChatOffset(data) {
1017
- const mark = data?.[FLOW_TEST_CHAT_KEY];
1018
- if (!mark || typeof mark !== "object") return null;
1019
- const offset = mark.timeOffset;
1020
- return typeof offset === "number" && Number.isFinite(offset) ? offset : 0;
1021
- }
1022
- var chatTime = (run, serverNow) => serverNow + (run?.timeOffset ?? 0);
1023
- var TURN_WAIT_LIMIT = 5;
1024
- async function placeRun(runtime, port, chat, flow, now) {
1025
- const { values } = await port.getChatState(chat.ownerId, chat.chatId);
1026
- const stored = storedRun(values[FLOW_STATE_KEY]);
1027
- if (stored) return stored;
1028
- const record = await runtime.store.getChat(chat.chatId);
1029
- return record ? runOutsideTurn(runtime, chat, flow, values, now, record) : null;
1030
- }
1031
- async function msToNextDay(runtime, port, ownerId, chatId, offset) {
1032
- const pinned = await runtime.chatFlow(ownerId, chatId);
1033
- const schedule = pinned?.flow.schedule;
1034
- if (!pinned || !schedule) throw new Error("\u041F\u0435\u0440\u0435\u043C\u043E\u0442\u043A\u0430 \u0434\u043E \u0443\u0442\u0440\u0430: \u0443 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \u043D\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D\u044B \xAB\u0414\u043D\u0438 \u0438 \u0432\u0440\u0435\u043C\u044F\xBB (schedule)");
1035
- const chatNow = runtime.now() + offset;
1036
- const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, chatNow);
1037
- if (!run) throw new Error("\u041F\u0435\u0440\u0435\u043C\u043E\u0442\u043A\u0430 \u0434\u043E \u0443\u0442\u0440\u0430: \u0432 \u0447\u0430\u0442\u0435 \u0435\u0449\u0451 \u043D\u0435 \u0431\u044B\u043B\u043E \u0445\u043E\u0434\u0430, \u0434\u043D\u0438 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F \u043D\u0435 \u043D\u0430\u0447\u0430\u043B\u0438\u0441\u044C");
1038
- const timeZone = runtime.timeZones ? await runtime.timeZones.of(ownerId, chatId) : "UTC";
1039
- return flowDay(run, chatNow, timeZone, schedule).nextDayStartAt - chatNow;
1040
- }
1041
- async function turnFinished(port, ownerId, chatId) {
1042
- for (let turn = 0; turn < TURN_WAIT_LIMIT; turn += 1) {
1043
- const stream = await port.subscribe?.(ownerId, chatId);
1044
- if (!stream) return;
1045
- for await (const _chunk of stream) ;
1046
- }
1047
- }
1048
- async function advanceTestClock(input) {
1049
- const { runtime, port, timers, ownerId, chatId, clock } = input;
1050
- const offset = testChatOffset(await port.getChatData(ownerId, chatId));
1051
- if (offset === null) throw new Error(`\u0427\u0430\u0442 ${chatId} \u043D\u0435 \u0442\u0435\u0441\u0442\u043E\u0432\u044B\u0439: \u0447\u0430\u0441\u044B \u043F\u0435\u0440\u0435\u043C\u0430\u0442\u044B\u0432\u0430\u044E\u0442\u0441\u044F \u0442\u043E\u043B\u044C\u043A\u043E \u0443 \u0442\u0435\u0441\u0442\u043E\u0432\u044B\u0445 \u0447\u0430\u0442\u043E\u0432`);
1052
- const ms = clock.to === "next_day_start" ? await msToNextDay(runtime, port, ownerId, chatId, offset) : clock.ms;
1053
- if (!Number.isFinite(ms) || ms <= 0) throw new Error("\u041F\u0435\u0440\u0435\u043C\u043E\u0442\u043A\u0430 \u0447\u0430\u0441\u043E\u0432: \u043D\u0443\u0436\u0435\u043D \u0441\u0434\u0432\u0438\u0433 \u0432\u043F\u0435\u0440\u0451\u0434, \u043C\u0441 \u0431\u043E\u043B\u044C\u0448\u0435 \u043D\u0443\u043B\u044F");
1054
- const timeOffset = offset + ms;
1055
- await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });
1056
- const pinned = timers && await runtime.chatFlow(ownerId, chatId);
1057
- if (!timers || !pinned) return { timeOffset };
1058
- const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);
1059
- if (!run) return { timeOffset };
1060
- await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });
1061
- await timers.tick();
1062
- await turnFinished(port, ownerId, chatId);
1063
- return { timeOffset };
1064
- }
1065
-
1066
- // src/runtime/flow-finish.ts
1067
- var FLOW_DEADLINE_NOTICE = "flow_deadline";
1068
- var DEADLINE_REASON = { kind: "timer", text: "\u0441\u0440\u043E\u043A \u0432\u044B\u0448\u0435\u043B" };
1069
- var FAREWELL_NOTICE = "\u0421\u0440\u043E\u043A \u043F\u0440\u043E\u0433\u0440\u0430\u043C\u043C\u044B \u0432\u044B\u0448\u0435\u043B. \u041F\u043E\u043F\u0440\u043E\u0449\u0430\u0439\u0441\u044F \u0441 \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u043E\u043C \u043E\u0434\u043D\u0438\u043C \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435\u043C: \u043A\u043E\u0440\u043E\u0442\u043A\u043E \u043F\u043E\u0434\u0432\u0435\u0434\u0438 \u0438\u0442\u043E\u0433 \u0438 \u0441\u043A\u0430\u0436\u0438, \u0447\u0442\u043E \u0434\u0430\u043B\u044C\u0448\u0435.";
1070
- function afterEndOf(flow) {
1071
- return flow.afterEnd ?? "reply";
1072
- }
1073
- var finalEndsChat = (flow) => flow.schedule !== void 0 || flow.afterEnd !== void 0;
1074
- function deadlineTransition(flow, run, at) {
1075
- const to = flow.schedule?.deadlineState;
1076
- const state = to === void 0 ? void 0 : stateById(flow, to);
1077
- if (!state || run.state === state.id) return null;
1078
- return { from: run.state, to: state.id, exitId: FLOW_DEADLINE_EXIT, decidedBy: "timer", tags: state.tags, at, reason: DEADLINE_REASON };
1079
- }
1080
- function endedRun(flow, run, ended) {
1081
- if (run.endedAt !== void 0) return run;
1082
- const transition = ended.endReason === "deadline" ? deadlineTransition(flow, run, ended.endedAt) : null;
1083
- const moved = transition ? applyTransition(flow, run, transition) : run;
1084
- return { ...moved, endedAt: ended.endedAt, endReason: ended.endReason };
1085
- }
1086
- function createFlowFinish(input) {
1087
- const { runtime } = input;
1088
- async function report(event) {
1089
- const { onFinish } = input;
1090
- if (!onFinish) return;
1091
- const { schedule } = event.pinned.flow;
1092
- let day = null;
1093
- if (schedule) {
1094
- const timeZone = await runtime.timeZones?.of(event.ownerId, event.chatId) ?? "UTC";
1095
- day = flowDay(event.run, event.at, timeZone, schedule).day;
1096
- if (schedule.lengthDays !== void 0) day = Math.min(day, schedule.lengthDays);
1097
- }
1098
- const { answerId: _answerId, firstReasons: _firstReasons, ...run } = event.run;
1099
- const finish = {
1100
- ownerId: event.ownerId,
1101
- chatId: event.chatId,
1102
- flowId: event.pinned.flowId,
1103
- versionId: event.pinned.versionId,
1104
- reason: event.reason,
1105
- ...event.outcome !== void 0 && { outcome: event.outcome },
1106
- day,
1107
- state: event.run.state,
1108
- tags: stateById(event.pinned.flow, event.run.state)?.tags ?? [],
1109
- run,
1110
- at: new Date(event.at)
1111
- };
1112
- const work = Promise.resolve().then(() => onFinish(finish)).catch((error) => runtime.logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${event.chatId} onFinish \u0443\u043F\u0430\u043B: ${error instanceof Error ? error.message : String(error)}`));
1113
- if (runtime.track) runtime.track(work);
1114
- else await work;
1115
- }
1116
- async function close(chatId, reason, at) {
1117
- const before = await runtime.store.getChat(chatId);
1118
- if (!before || before.endedAt !== null) return false;
1119
- const after = await runtime.store.endChat(chatId, { reason, at: new Date(at).toISOString() });
1120
- return after !== null && after.endReason === reason && Date.parse(after.endedAt ?? "") === at;
1121
- }
1122
- return {
1123
- async afterTransitions({ ownerId, chatId, pinned, run, transitions }) {
1124
- const final = transitions.find((transition) => {
1125
- const state = stateById(pinned.flow, transition.to);
1126
- return state !== void 0 && isFinal(state);
1127
- });
1128
- if (!final) return;
1129
- if (finalEndsChat(pinned.flow) && !await close(chatId, "final", final.at)) return;
1130
- await report({ ownerId, chatId, pinned, run, reason: "final", at: final.at });
1131
- },
1132
- async deadline({ ownerId, chatId, pinned, run, at }) {
1133
- const ended = await close(chatId, "deadline", at);
1134
- const transition = deadlineTransition(pinned.flow, run, at);
1135
- let next = run;
1136
- if (ended && transition) {
1137
- next = applyTransition(pinned.flow, run, transition);
1138
- await runtime.afterTransitions({ ownerId, chatId, pinned, run: next, transitions: [transition] });
1139
- }
1140
- if (ended) await report({ ownerId, chatId, pinned, run: next, reason: "deadline", at });
1141
- return { run: endedRun(pinned.flow, next, { endedAt: at, endReason: "deadline" }), ended };
1142
- },
1143
- async farewell({ ownerId, chatId, pinned, at, timeZone, port, firstMessages, delivery }) {
1144
- const schedule = pinned.flow.schedule;
1145
- if (!schedule?.farewellOnDeadline) return;
1146
- const record = await runtime.store.getChat(chatId);
1147
- const chat = record ? { dayClosedOn: record.dayClosedOn, pausedUntil: record.pausedUntil, windowClosed: record.windowClosed } : void 0;
1148
- const allowed = firstMessages ? await firstMessages.check({ ownerId, chatId, at, timeZone, schedule, ...chat && { chat } }) : { allowed: true };
1149
- if (!allowed.allowed) return;
1150
- const text = schedule.farewellText?.trim();
1151
- const result = await port.deliverNotices({
1152
- ownerId,
1153
- chatId,
1154
- notices: [{ kind: "app", name: FLOW_DEADLINE_NOTICE, title: FAREWELL_NOTICE, data: { reasons: [{ kind: "farewell" }] } }],
1155
- wakeAgent: true,
1156
- deliveryKey: `flow:${chatId}:deadline`,
1157
- usageSource: "first_message",
1158
- maxServerTurnsInRow: false,
1159
- timeZone,
1160
- ...delivery?.own() && { awaitAnswer: true },
1161
- ...text && { reply: { text } }
1162
- });
1163
- if (result.status !== "delivered" || !result.turnStarted) return;
1164
- if (delivery) await delivery.afterFirstTurn({ ownerId, chatId, answer: result.answer, reasons: [{ kind: "farewell" }], at, timeZone });
1165
- else await firstMessages?.record({ ownerId, at, timeZone });
1166
- },
1167
- async end({ ownerId, chatId, pinned, run, reason, outcome, at }) {
1168
- const ended = await close(chatId, reason, at);
1169
- if (ended) await report({ ownerId, chatId, pinned, run, reason, ...outcome !== void 0 && { outcome }, at });
1170
- return { ended };
1171
- }
1172
- };
1173
- }
1174
-
1175
- // src/runtime/prepare-turn.ts
1176
- var FLOW_TIMER_NOTICE = "flow_timer";
1177
- var FLOW_EVENT_NOTICE = "flow_event";
1178
- var HUMAN_ACTIONS = /* @__PURE__ */ new Set(["send", "edit", "regenerate", "retry"]);
1179
- var transitionKey = (transition) => `${transition.from}\0${transition.exitId}`;
1180
- var errorText3 = (error) => error instanceof Error ? error.message : String(error);
1181
- var dataOf = (notice) => notice.data && typeof notice.data === "object" ? notice.data : {};
1182
- var firstReasonsOf = (notices) => notices.flatMap((notice) => {
1183
- if (notice.kind !== "app" || notice.name !== FLOW_TIMER_NOTICE && notice.name !== FLOW_DEADLINE_NOTICE) return [];
1184
- const { reasons } = dataOf(notice);
1185
- return Array.isArray(reasons) ? reasons : [];
1186
- });
1187
- function flowPrepareTurn(runtime, appTimeZone, finisher) {
1188
- return async (ctx) => {
1189
- if (ctx.audience === "group") return;
1190
- let pinned = await runtime.chatFlow(ctx.ownerId, ctx.chatId, ctx.chat);
1191
- if (!pinned) return;
1192
- const timeOffset = testChatOffset(ctx.chat.data);
1193
- const now = runtime.now() + (timeOffset ?? 0);
1194
- const transitions = [];
1195
- let card;
1196
- const applied = [];
1197
- const step = async (run2, event, out = transitions) => advance(pinned.flow, run2, event, {
1198
- now,
1199
- card: card ??= await runtime.cardValues(ctx.ownerId),
1200
- out,
1201
- onChainLimit: (stopped) => runtime.logError(chainLimitMessage(ctx.chatId, stopped)),
1202
- timeZone
1203
- });
1204
- const record = await runtime.store.getChat(ctx.chatId);
1205
- const page = pageTimeZone(ctx.timeZone, appTimeZone(), ctx.pageTimeZone);
1206
- const known = page ?? (runtime.timeZones ? await runtime.timeZones.known(ctx.ownerId, ctx.chatId) : record?.timeZone || null);
1207
- const timeZone = known ?? runtime.defaultTimeZone ?? ctx.timeZone;
1208
- let run = currentRun(pinned.flow, ctx.state, now);
1209
- const previousAnswer = run.answerId;
1210
- const pending = await runtime.store.takePending(ctx.chatId);
1211
- const appliedInTime2 = new Set(
1212
- pending.flatMap((change) => change.kind === "transition" && change.appliedAt !== void 0 && change.branchHead === previousAnswer ? [transitionKey(change.transition)] : [])
1213
- );
1214
- let restored = null;
1215
- const restore = () => restored ??= Promise.all(pending.map((change) => runtime.store.addPending(ctx.chatId, change)));
1216
- const onAbort = () => void restore().catch((error) => runtime.logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${ctx.chatId} \u043E\u0436\u0438\u0434\u0430\u044E\u0449\u0435\u0435 \u043D\u0435 \u0432\u043E\u0437\u0432\u0440\u0430\u0449\u0435\u043D\u043E: ${errorText3(error)}`));
1217
- if (pending.length > 0) ctx.abortSignal.addEventListener("abort", onAbort, { once: true });
1218
- try {
1219
- return await applyTurn();
1220
- } catch (error) {
1221
- await restore();
1222
- throw error;
1223
- } finally {
1224
- ctx.abortSignal.removeEventListener("abort", onAbort);
1225
- }
1226
- async function applyTurn() {
1227
- const schedule = pinned.flow.schedule;
1228
- if (schedule) run = withDayAnchor(run, timeZone, schedule);
1229
- if (record?.endedAt) {
1230
- run = endedRun(pinned.flow, run, { endedAt: Date.parse(record.endedAt), endReason: record.endReason ?? "final" });
1231
- } else if (schedule && finisher) {
1232
- const deadline = deadlineAt(run, timeZone, schedule);
1233
- if (deadline !== null && deadline <= now) {
1234
- ({ run } = await finisher().deadline({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned, run, at: deadline, timeZone }));
1235
- }
1236
- }
1237
- const ended = run.endedAt !== void 0;
1238
- for (const change of ended ? [] : pending) {
1239
- if (change.kind === "migrate") {
1240
- const next = await runtime.chatFlow(ctx.ownerId, ctx.chatId);
1241
- if (next) {
1242
- run = { ...migrateRun(run, next.flow, change.map, now), answerId: previousAnswer };
1243
- pinned = next;
1244
- }
1245
- } else if (change.kind === "transition") {
1246
- if (change.branchHead !== previousAnswer || run.state !== change.transition.from) continue;
1247
- const out = [];
1248
- run = await step(run, pendingEvent(change.transition, timeZone), out);
1249
- for (const transition of out) (appliedInTime2.has(transitionKey(transition)) ? applied : transitions).push(transition);
1250
- } else {
1251
- run = await step(run, { type: "event", event: "app", name: change.name });
1252
- }
1253
- }
1254
- let touchBase = null;
1255
- for (const notice of ended ? [] : ctx.notices) {
1256
- if (notice.kind !== "app") continue;
1257
- const data = dataOf(notice);
1258
- if (notice.name === FLOW_TIMER_NOTICE) {
1259
- if (typeof data.undelivered === "string") run = withoutUndeliveredTouch(run, /* @__PURE__ */ new Set([data.undelivered])).run;
1260
- if (typeof data.exitId === "string") run = await step(run, { type: "timer", exitId: data.exitId, timeZone });
1261
- touchBase = { lastAgentAt: run.lastAgentAt, nudgeStep: run.nudgeStep ?? 0, ...run.nudgeStepOwn && { nudgeStepOwn: true }, agentAnswers: run.agentAnswers };
1262
- if (typeof data.nudgeStep === "number") run = withNudgeStep(run, data.nudgeStep, data.nudgeStepOwn === true);
1263
- } else if (notice.name === FLOW_EVENT_NOTICE && typeof data.name === "string") {
1264
- run = await step(run, { type: "event", event: "app", name: data.name });
1265
- }
1266
- }
1267
- if (!ctx.resumed && HUMAN_ACTIONS.has(ctx.action)) {
1268
- if (record?.windowClosed) await runtime.store.setWindowClosed(ctx.chatId, false);
1269
- run = noteHumanMessage(run, now, schedule ? dayInProgress(now, timeZone, schedule) : void 0, ctx.burstMessageIds?.length ?? 1);
1270
- if (!ended) run = await step(run, { type: "event", event: "message" });
1271
- }
1272
- if (ctx.action === "notice" && !ctx.resumed) run = { ...run, serverTurns: (run.serverTurns ?? 0) + 1 };
1273
- if (ctx.action !== "approval" && ctx.action !== "widget") run = noteAgentAnswer(run, now);
1274
- const { touch: earlierTouch, firstReasons: earlierReasons, ...withoutTouch } = run;
1275
- const continued = ctx.action === "approval" || ctx.action === "widget";
1276
- const touch = touchBase ? { answerId: ctx.messageId, at: now, ...touchBase } : continued ? earlierTouch : void 0;
1277
- const reasons = firstReasonsOf(ctx.notices);
1278
- const firstReasons = reasons.length > 0 ? reasons : continued ? earlierReasons : void 0;
1279
- run = { ...withoutTouch, answerId: ctx.messageId, ...timeOffset !== null && { timeOffset }, ...touch && { touch }, ...firstReasons && { firstReasons } };
1280
- if (ctx.abortSignal.aborted) {
1281
- await restore();
1282
- return void 0;
1283
- }
1284
- if (known && runtime.timeZones) await runtime.timeZones.write(ctx.ownerId, ctx.chatId, known);
1285
- else if (record && page && record.timeZone !== page) await runtime.store.updateChat(ctx.chatId, { timeZone: page }, new Date(runtime.now()).toISOString());
1286
- await runtime.afterTransitions({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned, run, transitions });
1287
- return {
1288
- set: { [FLOW_STATE_KEY]: run },
1289
- ...applied.length + transitions.length > 0 && { notices: [transitionNotice(pinned.flow, [...applied, ...transitions])] }
1290
- };
1291
- }
1292
- };
1293
- }
1294
-
1295
- // src/runtime/first-reasons-notice.ts
1296
- var REASON_LINES = {
1297
- morning: "\u041D\u0430\u0447\u0430\u043B\u0441\u044F \u043D\u043E\u0432\u044B\u0439 \u0434\u0435\u043D\u044C \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u044F: \u0443\u043C\u0435\u0441\u0442\u043D\u043E \u0434\u043E\u0431\u0440\u043E\u0435 \u0443\u0442\u0440\u043E.",
1298
- silence: (step) => `\u0427\u0435\u043B\u043E\u0432\u0435\u043A \u043C\u043E\u043B\u0447\u0438\u0442 \u043F\u043E\u0441\u043B\u0435 \u0442\u0432\u043E\u0435\u0433\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (\u043A\u0430\u0441\u0430\u043D\u0438\u0435 ${step}).`,
1299
- daily: (at) => `\u041A\u0430\u0441\u0430\u043D\u0438\u0435 \u043F\u043E \u0440\u0430\u0441\u043F\u0438\u0441\u0430\u043D\u0438\u044E, ${at}.`,
1300
- nightMessages: (count) => `\u0427\u0435\u043B\u043E\u0432\u0435\u043A \u043D\u0430\u043A\u043E\u043F\u0438\u043B \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u0431\u0435\u0437 \u043E\u0442\u0432\u0435\u0442\u0430 (${count}): \u043E\u0442\u0432\u0435\u0442\u044C \u043D\u0430 \u043D\u0438\u0445.`
1301
- };
1302
- var TASK = "\u0422\u044B \u043F\u0438\u0448\u0435\u0448\u044C \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0443 \u043F\u0435\u0440\u0432\u044B\u043C. \u041D\u0430\u043F\u0438\u0448\u0438 \u043E\u0434\u043D\u043E \u043A\u043E\u0440\u043E\u0442\u043A\u043E\u0435 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435: \u043D\u043E\u0432\u043E\u0435 \u0438 \u043F\u043E\u043B\u0435\u0437\u043D\u043E\u0435 \u0434\u043B\u044F \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0430 \u043F\u043E \u0442\u0435\u043C\u0435 \u0440\u0430\u0437\u0433\u043E\u0432\u043E\u0440\u0430, \u0431\u0435\u0437 \u043F\u043E\u0432\u0442\u043E\u0440\u0430 \u0443\u0436\u0435 \u0441\u043A\u0430\u0437\u0430\u043D\u043D\u043E\u0433\u043E \u0438 \u0431\u0435\u0437 \u0443\u043F\u0440\u0451\u043A\u043E\u0432 \u0437\u0430 \u043C\u043E\u043B\u0447\u0430\u043D\u0438\u0435.";
1303
- var transitionLine = (flow, exitId) => {
1304
- const exit = exitById(flow, exitId);
1305
- return `\u0421\u0440\u0430\u0431\u043E\u0442\u0430\u043B\u043E: ${exit ? describeCondition(flow, exit.when) : exitId}`;
1306
- };
1307
- function reasonLine(reason) {
1308
- switch (reason.kind) {
1309
- case "morning":
1310
- return REASON_LINES.morning;
1311
- case "silence":
1312
- return REASON_LINES.silence(reason.step);
1313
- case "daily":
1314
- return REASON_LINES.daily(reason.at);
1315
- case "night_messages":
1316
- return REASON_LINES.nightMessages(reason.count);
1317
- default:
1318
- return null;
1319
- }
1320
- }
1321
- function firstReasonsNotice(flow, reasons, exitId, ownNudge = false) {
1322
- const silence = reasons.find((reason) => reason.kind === "silence");
1323
- const moved = exitId !== null && reasons.some((reason) => reason.kind === "transition");
1324
- const morning = reasons.some((reason) => reason.kind === "morning");
1325
- const nudgeStep = moved || morning ? 0 : silence?.kind === "silence" ? silence.step : void 0;
1326
- const own = ownNudge && silence !== void 0 && !moved && !morning;
1327
- const data = { ...moved && { exitId }, reasons: [...reasons], ...nudgeStep !== void 0 && { nudgeStep }, ...own && { nudgeStepOwn: true } };
1328
- const others = reasons.flatMap((reason) => reasonLine(reason) ?? []);
1329
- if (others.length === 0 && moved) return { kind: "app", name: FLOW_TIMER_NOTICE, title: transitionLine(flow, exitId), data };
1330
- const lines = [...moved ? [`${transitionLine(flow, exitId)}.`] : [], ...others, TASK];
1331
- return { kind: "app", name: FLOW_TIMER_NOTICE, title: lines.join("\n"), data };
1332
- }
1333
-
1334
807
  // src/runtime/flow-delivery.ts
1335
808
  import { answerText, ChatNotFoundError, deliverableParts } from "@ykurilov/agent-chat-core";
1336
809
  var FLOW_DELIVERY_EXIT = "@delivery";
1337
810
  var RETRY_DELAYS_MS = [6e4, 5 * 6e4, 30 * 6e4];
1338
- var errorText4 = (error) => error instanceof Error ? error.message : String(error);
811
+ var errorText3 = (error) => error instanceof Error ? error.message : String(error);
1339
812
  var isoOf = (at) => new Date(at).toISOString();
1340
813
  var hasContent = (answer) => answer.text.trim() !== "" || answer.parts.some((part) => part.type !== "text");
1341
814
  function createFlowDelivery(input) {
@@ -1350,7 +823,7 @@ function createFlowDelivery(input) {
1350
823
  await (await portOf({ ownerId, chatId })).markUndelivered({ ownerId, chatId, messageId });
1351
824
  } catch (error) {
1352
825
  if (error instanceof ChatNotFoundError) return;
1353
- runtime.logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} \u043F\u043E\u043C\u0435\u0442\u043A\u0430 \xAB\u043D\u0435 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E\xBB \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u0430: ${errorText4(error)}`);
826
+ runtime.logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} \u043F\u043E\u043C\u0435\u0442\u043A\u0430 \xAB\u043D\u0435 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E\xBB \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u0430: ${errorText3(error)}`);
1354
827
  }
1355
828
  }
1356
829
  async function blocked(ownerId, chatId) {
@@ -1402,7 +875,7 @@ function createFlowDelivery(input) {
1402
875
  try {
1403
876
  return await deliver(message);
1404
877
  } catch (error) {
1405
- runtime.logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${message.chatId} \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u0430 \u0443\u043F\u0430\u043B\u0430: ${errorText4(error)}`);
878
+ runtime.logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${message.chatId} \u0434\u043E\u0441\u0442\u0430\u0432\u043A\u0430 \u0443\u043F\u0430\u043B\u0430: ${errorText3(error)}`);
1406
879
  return { status: "retry" };
1407
880
  }
1408
881
  }
@@ -1478,7 +951,7 @@ var TIMER_BATCH = 50;
1478
951
  var TIMER_PARALLEL = 5;
1479
952
  var DAY_MS = 864e5;
1480
953
  var DEFAULT_FLOW_SERVER_TURNS_IN_ROW = 50;
1481
- var errorText5 = (error) => error instanceof Error ? error.message : String(error);
954
+ var errorText4 = (error) => error instanceof Error ? error.message : String(error);
1482
955
  var isoOf2 = (at) => new Date(at).toISOString();
1483
956
  var timerKey = (chatId, exitId, run) => `flow:${chatId}:${exitId}:${run.enteredAt}`;
1484
957
  async function historyFacts(port, ownerId, chatId, answerId) {
@@ -1501,6 +974,10 @@ function createFlowTimers(input) {
1501
974
  const owner = `flow-timers:${randomUUID()}`;
1502
975
  let interval;
1503
976
  let running = null;
977
+ const watchers = /* @__PURE__ */ new Map();
978
+ const fired = (chatId, reason) => {
979
+ for (const onFired of watchers.get(chatId) ?? []) onFired(reason);
980
+ };
1504
981
  const dueInput = (due, branchHead, offset) => ({ at: isoOf2(due.at - offset), exitId: due.exitId, branchHead });
1505
982
  async function placeOf2(record, chatPort, run, failedAt) {
1506
983
  const pausedUntil = record.pausedUntil === null ? null : Date.parse(record.pausedUntil);
@@ -1625,11 +1102,14 @@ function createFlowTimers(input) {
1625
1102
  await store.setDue(chatId, null);
1626
1103
  const endAt = flow.schedule ? deadlineAt(run, timeZone, flow.schedule) ?? wake.at : wake.at;
1627
1104
  await input.onDeadline?.({ record, flow, run, at: endAt, timeZone });
1105
+ const deadlineState = flow.schedule?.deadlineState;
1106
+ fired(chatId, { kind: "deadline", stateId: deadlineState && stateById(flow, deadlineState) ? deadlineState : run.state });
1628
1107
  return;
1629
1108
  }
1630
1109
  if (due.giveUp) {
1631
1110
  await store.setDue(chatId, null);
1632
1111
  await input.onGiveUp?.({ record, flow, run, at });
1112
+ fired(chatId, { kind: "give_up", stateId: run.state });
1633
1113
  return;
1634
1114
  }
1635
1115
  const transitions = [];
@@ -1678,7 +1158,7 @@ function createFlowTimers(input) {
1678
1158
  await defer(flow.schedule ? nextDayStartAfter(at, timeZone, flow.schedule) : at + DAY_MS);
1679
1159
  return;
1680
1160
  }
1681
- const reasonsNotice = firstReasonsNotice(flow, reasons, moved ? due.exitId : null, due.ownNudge);
1161
+ const reasonsNotice = firstReasonsNotice(flow, reasons, moved ? due.exitId : null, due.ownNudge, silentMsOf(run, at));
1682
1162
  const notice = undelivered ? { ...reasonsNotice, data: { ...reasonsNotice.data, undelivered } } : reasonsNotice;
1683
1163
  const reply = moved ? exactFirstText(flow, next.state) : null;
1684
1164
  const delivery = input.delivery?.();
@@ -1710,6 +1190,7 @@ function createFlowTimers(input) {
1710
1190
  const answered = nudgeStep === void 0 ? noteAgentAnswer(next, at) : withNudgeStep(noteAgentAnswer(next, at), nudgeStep, nudgeStepOwn === true);
1711
1191
  const following = nextWake(flow, answered, at, timeZone, { pausedUntil: context.pausedUntil ?? null });
1712
1192
  await store.setDue(chatId, following ? dueInput(following, result.messageId ?? null, offset) : null, { unlessDelivery: true });
1193
+ fired(chatId, { kind: reasons[0].kind, stateId: next.state, ...moved && { exitId: due.exitId }, reasons });
1713
1194
  if (!delivery) {
1714
1195
  await firstMessages?.record({ ownerId, at, timeZone });
1715
1196
  return;
@@ -1722,7 +1203,7 @@ function createFlowTimers(input) {
1722
1203
  try {
1723
1204
  if (!await renew(record.chatId)) return;
1724
1205
  const heartbeat = setInterval(() => {
1725
- renew(record.chatId).catch((error) => logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${record.chatId} \u0430\u0440\u0435\u043D\u0434\u0430 \u043D\u0435 \u043F\u0440\u043E\u0434\u043B\u0435\u043D\u0430: ${errorText5(error)}`));
1206
+ renew(record.chatId).catch((error) => logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${record.chatId} \u0430\u0440\u0435\u043D\u0434\u0430 \u043D\u0435 \u043F\u0440\u043E\u0434\u043B\u0435\u043D\u0430: ${errorText4(error)}`));
1726
1207
  }, leaseMs / 3);
1727
1208
  heartbeat.unref?.();
1728
1209
  try {
@@ -1731,7 +1212,7 @@ function createFlowTimers(input) {
1731
1212
  clearInterval(heartbeat);
1732
1213
  }
1733
1214
  } catch (error) {
1734
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${record.chatId} \u0442\u0430\u0439\u043C\u0435\u0440 \u043D\u0435 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u043B: ${errorText5(error)}`);
1215
+ logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${record.chatId} \u0442\u0430\u0439\u043C\u0435\u0440 \u043D\u0435 \u0441\u0440\u0430\u0431\u043E\u0442\u0430\u043B: ${errorText4(error)}`);
1735
1216
  }
1736
1217
  }
1737
1218
  async function tickOnce() {
@@ -1753,7 +1234,7 @@ function createFlowTimers(input) {
1753
1234
  start() {
1754
1235
  if (interval) return;
1755
1236
  interval = setInterval(() => {
1756
- tick().catch((error) => logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u043E\u0431\u0445\u043E\u0434 \u0442\u0430\u0439\u043C\u0435\u0440\u043E\u0432 \u0443\u043F\u0430\u043B: ${errorText5(error)}`));
1237
+ tick().catch((error) => logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u043E\u0431\u0445\u043E\u0434 \u0442\u0430\u0439\u043C\u0435\u0440\u043E\u0432 \u0443\u043F\u0430\u043B: ${errorText4(error)}`));
1757
1238
  }, tickMs);
1758
1239
  interval.unref?.();
1759
1240
  },
@@ -1764,7 +1245,15 @@ function createFlowTimers(input) {
1764
1245
  });
1765
1246
  },
1766
1247
  tick,
1767
- reschedule
1248
+ reschedule,
1249
+ watch(chatId, onFired) {
1250
+ const listeners = watchers.get(chatId) ?? /* @__PURE__ */ new Set();
1251
+ watchers.set(chatId, listeners.add(onFired));
1252
+ return () => {
1253
+ listeners.delete(onFired);
1254
+ if (listeners.size === 0) watchers.delete(chatId);
1255
+ };
1256
+ }
1768
1257
  };
1769
1258
  }
1770
1259
 
@@ -2367,7 +1856,7 @@ var DELIVERY_RETRY_MS = [50, 250, 1e3, 3e3];
2367
1856
  var KNOWN_FLOWS_LIMIT = 1e4;
2368
1857
  var KNOWN_PORTS_LIMIT = 1e4;
2369
1858
  var SERVER_UNKNOWN_CODES = /* @__PURE__ */ new Set(["unknown_event"]);
2370
- var errorText6 = (error) => error instanceof Error ? error.message : String(error);
1859
+ var errorText5 = (error) => error instanceof Error ? error.message : String(error);
2371
1860
  var pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2372
1861
  function stableJson(value) {
2373
1862
  if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
@@ -2442,7 +1931,7 @@ function buildAgentFlow(options, modules = {}) {
2442
1931
  try {
2443
1932
  return make();
2444
1933
  } catch (error) {
2445
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] ${name} \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${errorText6(error)}`);
1934
+ logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] ${name} \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${errorText5(error)}`);
2446
1935
  return null;
2447
1936
  }
2448
1937
  });
@@ -2507,7 +1996,7 @@ function buildAgentFlow(options, modules = {}) {
2507
1996
  );
2508
1997
  const background = /* @__PURE__ */ new Set();
2509
1998
  function track(work) {
2510
- const tracked = work.catch((error) => logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0444\u043E\u043D\u043E\u0432\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430 \u0443\u043F\u0430\u043B\u0430: ${errorText6(error)}`)).finally(() => background.delete(tracked));
1999
+ const tracked = work.catch((error) => logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0444\u043E\u043D\u043E\u0432\u0430\u044F \u0440\u0430\u0431\u043E\u0442\u0430 \u0443\u043F\u0430\u043B\u0430: ${errorText5(error)}`)).finally(() => background.delete(tracked));
2511
2000
  background.add(tracked);
2512
2001
  }
2513
2002
  const known = /* @__PURE__ */ new Map();
@@ -2679,7 +2168,7 @@ function buildAgentFlow(options, modules = {}) {
2679
2168
  return Object.fromEntries(entries.map((entry) => [entry.fieldId, entry.value]));
2680
2169
  } catch (error) {
2681
2170
  if (!app) throw error;
2682
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u0430: ${errorText6(error)}`);
2171
+ logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430 \u043D\u0435 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u0430: ${errorText5(error)}`);
2683
2172
  return {};
2684
2173
  }
2685
2174
  },
@@ -2688,7 +2177,7 @@ function buildAgentFlow(options, modules = {}) {
2688
2177
  try {
2689
2178
  await store.updateChat(chatId, { stateId: run.state }, iso());
2690
2179
  } catch (error) {
2691
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0432 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435: ${errorText6(error)}`);
2180
+ logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0432 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435: ${errorText5(error)}`);
2692
2181
  }
2693
2182
  const { schedule } = pinned.flow;
2694
2183
  const timeZone = schedule ? await timeZones.of(ownerId, chatId) : null;
@@ -2721,7 +2210,7 @@ function buildAgentFlow(options, modules = {}) {
2721
2210
  try {
2722
2211
  await optionalTimers()?.reschedule({ ownerId, chatId, run, branchHead, ...failedAt !== void 0 && { failedAt } });
2723
2212
  } catch (error) {
2724
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} \u0442\u0430\u0439\u043C\u0435\u0440 \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u0430\u043D: ${errorText6(error)}`);
2213
+ logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} \u0442\u0430\u0439\u043C\u0435\u0440 \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u0430\u043D: ${errorText5(error)}`);
2725
2214
  }
2726
2215
  }
2727
2216
  async function rescheduleUndelivered({ ownerId, chatId, at }) {
@@ -2745,7 +2234,7 @@ function buildAgentFlow(options, modules = {}) {
2745
2234
  if (!run) return;
2746
2235
  await reschedule(record.ownerId, record.chatId, migrateRun(run, flow, map, chatTime(run, now())), run.answerId ?? null);
2747
2236
  } catch (error) {
2748
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${record.chatId} \u0442\u0430\u0439\u043C\u0435\u0440 \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u0430\u043D: ${errorText6(error)}`);
2237
+ logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${record.chatId} \u0442\u0430\u0439\u043C\u0435\u0440 \u043D\u0435 \u043F\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u0430\u043D: ${errorText5(error)}`);
2749
2238
  }
2750
2239
  }
2751
2240
  async function deliverFirstText(ownerId, chatId, messageId, condition, text) {
@@ -2867,7 +2356,7 @@ function buildAgentFlow(options, modules = {}) {
2867
2356
  await store.updateChat(chatId, { placement: null }, iso());
2868
2357
  } catch (error) {
2869
2358
  placedNow.add(chatId);
2870
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} \u0437\u0430\u043F\u0438\u0441\u044C \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0430 \u043D\u0435 \u0441\u043D\u044F\u0442\u0430: ${errorText6(error)}`);
2359
+ logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${chatId} \u0437\u0430\u043F\u0438\u0441\u044C \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u0430 \u043D\u0435 \u0441\u043D\u044F\u0442\u0430: ${errorText5(error)}`);
2871
2360
  }
2872
2361
  }
2873
2362
  async function readRun(ownerId, chatId) {
@@ -2901,6 +2390,8 @@ function buildAgentFlow(options, modules = {}) {
2901
2390
  ...chat,
2902
2391
  // Место в сценарии — служебное: посетитель его не видит, редактор и тестовый чат читают `readRun`.
2903
2392
  privateStateKeys: [.../* @__PURE__ */ new Set([...chat.privateStateKeys ?? [], FLOW_STATE_KEY])],
2393
+ // Строки «Агент написал первым» на экране (право `appNotices`): только проекция, сырые данные пометок не уходят.
2394
+ appNoticeViews: { ...chat.appNoticeViews, ...FLOW_APP_NOTICE_VIEWS },
2904
2395
  beforeTurn: async (input) => {
2905
2396
  const own = await chat.beforeTurn?.(input);
2906
2397
  return own ?? gate(input);
@@ -2969,7 +2460,7 @@ ${rules}`;
2969
2460
  try {
2970
2461
  await store.updateChat(input.chatId, { stateId: run.state }, iso());
2971
2462
  } catch (error) {
2972
- logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${input.chatId} \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0432 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435: ${errorText6(error)}`);
2463
+ logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] \u0447\u0430\u0442=${input.chatId} \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0432 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435: ${errorText5(error)}`);
2973
2464
  }
2974
2465
  await reschedule(input.ownerId, input.chatId, run, input.atMessageId);
2975
2466
  })();
@@ -3289,6 +2780,7 @@ export {
3289
2780
  FLOW_TIMER_SPANS,
3290
2781
  FLOW_TIMER_UNITS,
3291
2782
  FLOW_TIME_ZONE_TOOL,
2783
+ FlowTestClockError,
3292
2784
  RETURN_TARGET,
3293
2785
  activeExits,
3294
2786
  agentFlowSchema,
@@ -3333,6 +2825,7 @@ export {
3333
2825
  noteRejectedClaim,
3334
2826
  noteSideQuestion,
3335
2827
  openSideTasks,
2828
+ parseDuration,
3336
2829
  parseFlow,
3337
2830
  placeOfPath,
3338
2831
  resolveFirstMessages,