@ykurilov/agent-chat-flows 0.64.0 → 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.
@@ -0,0 +1,817 @@
1
+ import {
2
+ FLOW_CHAIN_LIMIT,
3
+ FLOW_DEADLINE_EXIT,
4
+ FLOW_FIRST_EXIT,
5
+ FLOW_STATE_KEY,
6
+ activeExits,
7
+ applyTransition,
8
+ dayClosedToday,
9
+ dayInProgress,
10
+ deadlineAt,
11
+ describeCondition,
12
+ fieldById,
13
+ fieldValueFits,
14
+ firstMessageSlot,
15
+ flowDay,
16
+ isFieldStale,
17
+ isFinal,
18
+ localDay,
19
+ migrateRun,
20
+ nextDayStartAfter,
21
+ nextTransition,
22
+ noteAgentAnswer,
23
+ noteHumanMessage,
24
+ overdueOnEntry,
25
+ resolveFirstMessages,
26
+ sideQuestion,
27
+ startRun,
28
+ stateById,
29
+ withDayAnchor,
30
+ withNudgeStep,
31
+ withoutUndeliveredTouch
32
+ } from "./chunk-7VR4YNTJ.js";
33
+
34
+ // src/prompt/state-card.ts
35
+ function judgeExits(flow, stateId) {
36
+ const state = stateById(flow, stateId);
37
+ if (!state) return [];
38
+ return activeExits(flow, state).flatMap(({ exit }) => exit.when.type === "judge" ? [{ exitId: exit.id, text: exit.when.text }] : []);
39
+ }
40
+ function stateGoal(instructions) {
41
+ return instructions.split("\n")[0].trim();
42
+ }
43
+ function stateCard(flow, run, present) {
44
+ const state = stateById(flow, run.state);
45
+ 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.`;
46
+ const [goal = "", ...rest] = state.instructions.split("\n");
47
+ const lines = [`\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435: \xAB${state.title}\xBB`, `\u0426\u0435\u043B\u044C: ${goal.trim()}`];
48
+ const how = rest.join("\n").trim();
49
+ if (how) lines.push("", how);
50
+ const fields = state.learn.map((id) => fieldById(flow, id)).flatMap((found) => found ? [`- ${found.field.name}: ${found.field.about}`] : []);
51
+ if (fields.length > 0) lines.push("", "\u0423\u0437\u043D\u0430\u0442\u044C \u0437\u0434\u0435\u0441\u044C:", ...fields);
52
+ const conditions = judgeExits(flow, state.id);
53
+ if (isFinal(state)) {
54
+ 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.");
55
+ } else if (conditions.length > 0) {
56
+ 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}`));
57
+ }
58
+ const inSet = (name) => !present || present.has(name);
59
+ const tools = state.tools.filter(inSet);
60
+ 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(", ")}.`);
61
+ const unavailable = flow.tools.filter((name) => inSet(name) && !state.tools.includes(name));
62
+ if (unavailable.length > 0) lines.push(`\u0421\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B: ${unavailable.join(", ")}.`);
63
+ return lines.join("\n");
64
+ }
65
+
66
+ // src/runtime/flow-events.ts
67
+ async function reportTransition(handler, event, logError) {
68
+ if (!handler) return;
69
+ try {
70
+ await handler(event);
71
+ } catch (error) {
72
+ 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)}`);
73
+ }
74
+ }
75
+ function storedRun(value) {
76
+ return value && typeof value === "object" && typeof value.state === "string" ? value : null;
77
+ }
78
+ function currentRun(flow, values, now) {
79
+ return storedRun(values?.[FLOW_STATE_KEY]) ?? startRun(flow, now);
80
+ }
81
+ function placedRun(flow, placement, now, timeZone) {
82
+ const start = startRun(flow, now);
83
+ const state = placement.stateId !== void 0 && stateById(flow, placement.stateId) || stateById(flow, flow.start);
84
+ const startedAt = placement.startedAt ?? now;
85
+ const enteredAt = placement.enteredAt ?? placement.startedAt ?? now;
86
+ return {
87
+ ...start,
88
+ state: state.id,
89
+ startedAt,
90
+ enteredAt,
91
+ path: [{ state: state.id, at: enteredAt, tags: state.tags, from: null, exitId: null, decidedBy: "start" }],
92
+ ...placement.lastHumanAt !== void 0 && { lastHumanAt: placement.lastHumanAt },
93
+ ...placement.lastAgentAt !== void 0 && { lastAgentAt: placement.lastAgentAt },
94
+ ...placement.nudgeStep !== void 0 && { nudgeStep: placement.nudgeStep },
95
+ ...placement.humanToday && flow.schedule && timeZone && { dayCounts: { [dayInProgress(placement.humanToday.at, timeZone, flow.schedule)]: placement.humanToday.count } }
96
+ };
97
+ }
98
+ var msOf = (value) => value === void 0 ? void 0 : Date.parse(value);
99
+ function recordPlacement(record) {
100
+ const placement = record.placement;
101
+ if (!placement) return null;
102
+ const lastHumanAt = msOf(placement.lastHumanAt);
103
+ const lastAgentAt = msOf(placement.lastAgentAt);
104
+ return {
105
+ stateId: record.stateId,
106
+ startedAt: Date.parse(placement.startedAt),
107
+ enteredAt: Date.parse(placement.enteredAt),
108
+ ...lastHumanAt !== void 0 && { lastHumanAt },
109
+ ...lastAgentAt !== void 0 && { lastAgentAt },
110
+ ...placement.nudgeStep !== void 0 && { nudgeStep: placement.nudgeStep },
111
+ ...placement.humanToday && { humanToday: { at: Date.parse(placement.humanToday.at), count: placement.humanToday.count } }
112
+ };
113
+ }
114
+ function chatRun(flow, values, record, now, timeZone) {
115
+ const stored = storedRun(values?.[FLOW_STATE_KEY]);
116
+ if (stored) return stored;
117
+ const placement = record ? recordPlacement(record) : null;
118
+ return placement ? placedRun(flow, placement, now, timeZone) : startRun(flow, now);
119
+ }
120
+ async function personTimeZone(runtime, chat) {
121
+ const known = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : (await runtime.store.getChat(chat.chatId))?.timeZone;
122
+ return known || runtime.defaultTimeZone || Intl.DateTimeFormat().resolvedOptions().timeZone;
123
+ }
124
+ async function runOutsideTurn(runtime, chat, flow, values, now, record) {
125
+ const stored = storedRun(values?.[FLOW_STATE_KEY]);
126
+ if (stored) return stored;
127
+ const found = record === void 0 ? await runtime.store.getChat(chat.chatId) : record;
128
+ if (!found?.placement) return startRun(flow, now);
129
+ const timeZone = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : found.timeZone || void 0;
130
+ return chatRun(flow, values, found, now, timeZone);
131
+ }
132
+ function advance(flow, run, event, input) {
133
+ let current = run;
134
+ let step = event;
135
+ let entered = false;
136
+ let stopped = false;
137
+ while (step) {
138
+ const transition = nextTransition(flow, current, step, input.now);
139
+ if (!transition) {
140
+ if (current.chain >= FLOW_CHAIN_LIMIT && nextTransition(flow, { ...current, chain: 0 }, step, input.now)) {
141
+ if (!stopped) input.onChainLimit(current);
142
+ stopped = true;
143
+ }
144
+ const overdue = entered && !stopped && step.type === "data" ? overdueOnEntry(flow, current, input.now, input.timeZone) : null;
145
+ step = overdue ? { type: "timer", exitId: overdue, timeZone: input.timeZone } : null;
146
+ continue;
147
+ }
148
+ current = applyTransition(flow, current, transition);
149
+ input.out.push(transition);
150
+ entered = true;
151
+ step = { type: "data", card: input.card };
152
+ }
153
+ return current;
154
+ }
155
+ function pendingEvent(transition, timeZone) {
156
+ if (transition.decidedBy === "timer") return { type: "timer", exitId: transition.exitId, timeZone };
157
+ return { type: "verdict", exitId: transition.exitId, probability: transition.probability ?? 1, decidedBy: "judge" };
158
+ }
159
+ function withPending(flow, run, pending, input) {
160
+ let current = run;
161
+ for (const change of pending) {
162
+ if (change.kind === "transition") {
163
+ if (change.branchHead !== run.answerId || current.state !== change.transition.from) continue;
164
+ current = advance(flow, current, pendingEvent(change.transition, input.timeZone), input);
165
+ } else if (change.kind === "event") {
166
+ current = advance(flow, current, { type: "event", event: "app", name: change.name }, input);
167
+ }
168
+ }
169
+ return current;
170
+ }
171
+ 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`;
172
+ function exitById(flow, exitId) {
173
+ for (const state of flow.states) {
174
+ const found = state.exits.find((exit) => exit.id === exitId);
175
+ if (found) return found;
176
+ }
177
+ return flow.anyState.exits.find((exit) => exit.id === exitId);
178
+ }
179
+ function exactFirstText(flow, stateId) {
180
+ return stateById(flow, stateId)?.firstMessage ?? null;
181
+ }
182
+ 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`;
183
+ function transitionNotice(flow, transitions) {
184
+ const lines = transitions.map((transition) => {
185
+ const exit = exitById(flow, transition.exitId);
186
+ const title = stateById(flow, transition.to)?.title ?? transition.to;
187
+ return `\u0421\u0440\u0430\u0431\u043E\u0442\u0430\u043B\u043E: ${exit ? describeCondition(flow, exit.when) : transition.exitId} \u2192 \xAB${title}\xBB.`;
188
+ });
189
+ const last = transitions.at(-1);
190
+ const first = exactFirstText(flow, last.to);
191
+ if (first) lines.push(sayExactly(first));
192
+ return {
193
+ kind: "app",
194
+ name: "flow_transition",
195
+ title: lines.join("\n"),
196
+ data: { from: transitions[0].from, to: last.to, exitIds: transitions.map((transition) => transition.exitId) }
197
+ };
198
+ }
199
+ function transitionReport(flow, run, transitions) {
200
+ const from = stateById(flow, transitions[0].from)?.title ?? transitions[0].from;
201
+ 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)];
202
+ const first = exactFirstText(flow, run.state);
203
+ if (first) lines.push("", sayExactly(first));
204
+ return lines.join("\n");
205
+ }
206
+
207
+ // src/runtime/test-clock.ts
208
+ var FLOW_TEST_CHAT_KEY = "flowTestChat";
209
+ function flowTestChatData() {
210
+ return { [FLOW_TEST_CHAT_KEY]: { timeOffset: 0 } };
211
+ }
212
+ function testChatOffset(data) {
213
+ const mark = data?.[FLOW_TEST_CHAT_KEY];
214
+ if (!mark || typeof mark !== "object") return null;
215
+ const offset = mark.timeOffset;
216
+ return typeof offset === "number" && Number.isFinite(offset) ? offset : 0;
217
+ }
218
+ var chatTime = (run, serverNow) => serverNow + (run?.timeOffset ?? 0);
219
+ var FlowTestClockError = class extends Error {
220
+ constructor(code, message) {
221
+ super(message);
222
+ this.code = code;
223
+ this.name = "FlowTestClockError";
224
+ }
225
+ code;
226
+ };
227
+ var NEXT_FIRST_MESSAGE_STEPS = 50;
228
+ var TURN_WAIT_LIMIT = 5;
229
+ async function placeRun(runtime, port, chat, flow, now) {
230
+ const { values } = await port.getChatState(chat.ownerId, chat.chatId);
231
+ const stored = storedRun(values[FLOW_STATE_KEY]);
232
+ if (stored) return stored;
233
+ const record = await runtime.store.getChat(chat.chatId);
234
+ return record ? runOutsideTurn(runtime, chat, flow, values, now, record) : null;
235
+ }
236
+ async function msToNextDay(runtime, port, ownerId, chatId, offset) {
237
+ const pinned = await runtime.chatFlow(ownerId, chatId);
238
+ const schedule = pinned?.flow.schedule;
239
+ 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)");
240
+ const chatNow = runtime.now() + offset;
241
+ const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, chatNow);
242
+ 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");
243
+ const timeZone = runtime.timeZones ? await runtime.timeZones.of(ownerId, chatId) : "UTC";
244
+ return flowDay(run, chatNow, timeZone, schedule).nextDayStartAt - chatNow;
245
+ }
246
+ async function turnFinished(port, ownerId, chatId) {
247
+ for (let turn = 0; turn < TURN_WAIT_LIMIT; turn += 1) {
248
+ const stream = await port.subscribe?.(ownerId, chatId);
249
+ if (!stream) return;
250
+ for await (const _chunk of stream) ;
251
+ }
252
+ }
253
+ async function advanceTestClock(input) {
254
+ const { runtime, port, timers, ownerId, chatId, clock } = input;
255
+ const offset = testChatOffset(await port.getChatData(ownerId, chatId));
256
+ 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`);
257
+ if (clock.to === "next_first_message") return advanceToNextFirstMessage({ runtime, port, timers, ownerId, chatId, offset });
258
+ const ms = clock.to === "next_day_start" ? await msToNextDay(runtime, port, ownerId, chatId, offset) : clock.ms ?? Number.NaN;
259
+ 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");
260
+ const timeOffset = offset + ms;
261
+ await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });
262
+ const pinned = timers && await runtime.chatFlow(ownerId, chatId);
263
+ if (!timers || !pinned) return { timeOffset };
264
+ const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);
265
+ if (!run) return { timeOffset };
266
+ await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });
267
+ await timers.tick();
268
+ await turnFinished(port, ownerId, chatId);
269
+ return { timeOffset };
270
+ }
271
+ var nothingScheduled = (chatId) => new FlowTestClockError("nothing_scheduled", `\u0427\u0430\u0442 ${chatId}: \u0432\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 \u2014 \u043F\u0435\u0440\u0435\u043C\u0430\u0442\u044B\u0432\u0430\u0442\u044C \u043D\u0435 \u043A \u0447\u0435\u043C\u0443`);
272
+ async function advanceToNextFirstMessage(input) {
273
+ const { runtime, port, timers, ownerId, chatId } = input;
274
+ const pinned = timers && await runtime.chatFlow(ownerId, chatId);
275
+ if (!timers?.watch || !pinned) throw nothingScheduled(chatId);
276
+ let timeOffset = input.offset;
277
+ for (let step = 0; step < NEXT_FIRST_MESSAGE_STEPS; step += 1) {
278
+ const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);
279
+ if (!run) throw nothingScheduled(chatId);
280
+ if (step === 0) await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });
281
+ const record = await runtime.store.getChat(chatId);
282
+ if (!record || record.endedAt !== null || record.dueAt === null) throw nothingScheduled(chatId);
283
+ const serverNow = runtime.now();
284
+ const shift = Math.max(0, Date.parse(record.dueAt) - serverNow);
285
+ if (shift > 0) {
286
+ timeOffset += shift;
287
+ await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });
288
+ await runtime.store.setDue(chatId, { at: new Date(serverNow).toISOString(), exitId: record.dueExitId ?? FLOW_FIRST_EXIT, branchHead: record.dueBranchHead });
289
+ }
290
+ let reason = null;
291
+ const unwatch = timers.watch(chatId, (fired) => reason ??= fired);
292
+ try {
293
+ await timers.tick();
294
+ await turnFinished(port, ownerId, chatId);
295
+ } finally {
296
+ unwatch();
297
+ }
298
+ if (reason) return { timeOffset, reason };
299
+ }
300
+ throw new FlowTestClockError("not_reached", `\u0427\u0430\u0442 ${chatId}: \u0441\u0440\u043E\u043A \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F \u043E\u0442\u043A\u043B\u0430\u0434\u044B\u0432\u0430\u043B\u0441\u044F ${NEXT_FIRST_MESSAGE_STEPS} \u0440\u0430\u0437 \u043F\u043E\u0434\u0440\u044F\u0434 \u2014 \u043F\u0435\u0440\u0435\u043C\u043E\u0442\u043A\u0430 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D\u0430`);
301
+ }
302
+
303
+ // src/runtime/client-facts.ts
304
+ var FACT_PREFIX = "fact:";
305
+ var MAX_FACTS = 30;
306
+ var MAX_FACT_LENGTH = 200;
307
+ var normalized = (text) => text.trim().toLowerCase().replace(/\s+/g, " ");
308
+ function factId(text) {
309
+ let hash = 2166136261;
310
+ for (const char of normalized(text)) {
311
+ hash ^= char.codePointAt(0);
312
+ hash = Math.imul(hash, 16777619) >>> 0;
313
+ }
314
+ return `f${hash.toString(16).padStart(8, "0")}`;
315
+ }
316
+ var sameFact = (left, right) => normalized(left) === normalized(right);
317
+ var isFactEntry = (fieldId) => fieldId.startsWith(FACT_PREFIX);
318
+ var byWriteOrder = (left, right) => left.updatedAt.localeCompare(right.updatedAt) || left.id.localeCompare(right.id);
319
+ function moduleFacts(store) {
320
+ const read = async (ownerId) => {
321
+ const entries = await store.readCard(ownerId);
322
+ return entries.filter((entry) => isFactEntry(entry.fieldId)).map((entry) => ({ id: entry.fieldId.slice(FACT_PREFIX.length), text: String(entry.value), updatedAt: entry.updatedAt, chatId: entry.chatId, flowId: entry.flowId })).sort(byWriteOrder);
323
+ };
324
+ return {
325
+ read,
326
+ add: ({ ownerId, fact, chatId, flowId, at }) => store.writeCardField({ ownerId, fieldId: `${FACT_PREFIX}${fact.id}`, value: fact.text, chatId, flowId, at: at.toISOString() }),
327
+ async forget(ownerId, id) {
328
+ if (id !== "all") return store.forgetCard(ownerId, `${FACT_PREFIX}${id}`);
329
+ for (const fact of await read(ownerId)) await store.forgetCard(ownerId, `${FACT_PREFIX}${fact.id}`);
330
+ }
331
+ };
332
+ }
333
+ function clientFactStore(store, adapter) {
334
+ const own = adapter?.facts;
335
+ if (!own) return moduleFacts(store);
336
+ return {
337
+ read: async (ownerId) => [...await own.read(ownerId)].sort(byWriteOrder),
338
+ add: (input) => own.add(input),
339
+ forget: (ownerId, id) => own.forget(ownerId, id)
340
+ };
341
+ }
342
+ var factEntry = (fact) => ({ fieldId: `${FACT_PREFIX}${fact.id}`, value: fact.text, updatedAt: fact.updatedAt, chatId: fact.chatId, flowId: fact.flowId });
343
+
344
+ // src/runtime/first-message-retry.ts
345
+ var FIRST_MESSAGE_RETRY_MS = 36e5;
346
+ var FIRST_MESSAGE_MIN_RETRY_MS = 6e4;
347
+
348
+ // src/runtime/first-message-rules.ts
349
+ function chatWriteBlock(chat, at, timeZone, schedule) {
350
+ if (chat.windowClosed) return { retryAt: null };
351
+ const pausedUntil = chat.pausedUntil ? Date.parse(chat.pausedUntil) : 0;
352
+ if (pausedUntil > at) return { retryAt: pausedUntil };
353
+ if (schedule && dayClosedToday(chat.dayClosedOn, at, timeZone, schedule)) return { retryAt: nextDayStartAfter(at, timeZone, schedule) };
354
+ return null;
355
+ }
356
+ function scenarioRules(rules, schedule) {
357
+ if (!schedule) return rules;
358
+ return {
359
+ quietHours: { from: schedule.nightFrom ?? rules.quietHours.from, to: schedule.dayStart },
360
+ maxPerDay: schedule.firstMessages?.maxPerDay ?? rules.maxPerDay
361
+ };
362
+ }
363
+ function createFirstMessageRules(setting, store) {
364
+ const rules = resolveFirstMessages(setting);
365
+ if (!rules) return null;
366
+ return {
367
+ async check({ ownerId, chatId, at, timeZone, schedule, chat }) {
368
+ const blocked = chat ? chatWriteBlock(chat, at, timeZone, schedule) : null;
369
+ if (blocked) return { allowed: false, retryAt: blocked.retryAt };
370
+ const sentToday = await store.countFirstMessages(ownerId, localDay(at, timeZone));
371
+ const slot = firstMessageSlot(scenarioRules(rules, schedule), { at, timeZone, sentToday });
372
+ if (!slot.allowed) return slot;
373
+ const answer = rules.canWriteNow ? await rules.canWriteNow({ ownerId, chatId, at: new Date(at) }) : true;
374
+ if (answer === true) return { allowed: true };
375
+ return { allowed: false, retryAt: retryAtOf(answer, at) };
376
+ },
377
+ async record({ ownerId, at, timeZone }) {
378
+ await store.addFirstMessage(ownerId, localDay(at, timeZone));
379
+ }
380
+ };
381
+ }
382
+ function retryAtOf(answer, at) {
383
+ if (answer === "never") return null;
384
+ const retryAt = typeof answer === "object" && answer !== null && answer.retryAt instanceof Date ? answer.retryAt.getTime() : Number.NaN;
385
+ if (!Number.isFinite(retryAt)) return at + FIRST_MESSAGE_RETRY_MS;
386
+ return Math.max(retryAt, at + FIRST_MESSAGE_MIN_RETRY_MS);
387
+ }
388
+ var UNKNOWN_TIME_ZONE = "";
389
+ var serverTimeZone = () => Intl.DateTimeFormat().resolvedOptions().timeZone;
390
+ function pageTimeZone(turnZone, appZone, sent) {
391
+ if (sent !== void 0) return sent;
392
+ return turnZone === (appZone ?? serverTimeZone()) ? null : turnZone;
393
+ }
394
+ function createChatTimeZones(input) {
395
+ const warned = /* @__PURE__ */ new Set();
396
+ return {
397
+ of(chatId, stored) {
398
+ if (stored) return stored;
399
+ if (input.defaultTimeZone) return input.defaultTimeZone;
400
+ const app = input.appTimeZone();
401
+ if (app) return app;
402
+ const server = serverTimeZone();
403
+ if (!warned.has(chatId)) {
404
+ warned.add(chatId);
405
+ 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}`);
406
+ }
407
+ return server;
408
+ }
409
+ };
410
+ }
411
+ function ianaTimeZone(value) {
412
+ if (!value.trim()) return null;
413
+ try {
414
+ return new Intl.DateTimeFormat("en-US", { timeZone: value.trim() }).resolvedOptions().timeZone;
415
+ } catch {
416
+ return null;
417
+ }
418
+ }
419
+ function createFlowTimeZones(input) {
420
+ const { store } = input;
421
+ const iso = () => new Date(input.now()).toISOString();
422
+ const readPerson = (ownerId) => input.people ? input.people.readTimeZone(ownerId) : store.readTimeZone(ownerId);
423
+ const writePerson = (ownerId, timeZone) => input.people ? input.people.writeTimeZone(ownerId, timeZone) : store.writeTimeZone(ownerId, timeZone, iso());
424
+ async function known(ownerId, chatId) {
425
+ const person = await readPerson(ownerId);
426
+ if (person) return person;
427
+ return (await store.getChat(chatId))?.timeZone || null;
428
+ }
429
+ return {
430
+ known,
431
+ fallback: input.fallback,
432
+ async of(ownerId, chatId) {
433
+ return await known(ownerId, chatId) ?? input.fallback(chatId);
434
+ },
435
+ async write(ownerId, chatId, timeZone) {
436
+ if (await readPerson(ownerId) !== timeZone) await writePerson(ownerId, timeZone);
437
+ const record = await store.getChat(chatId);
438
+ if (record && record.timeZone !== timeZone) await store.updateChat(chatId, { timeZone }, iso());
439
+ }
440
+ };
441
+ }
442
+
443
+ // src/runtime/first-reasons-notice.ts
444
+ var REASON_LINES = {
445
+ 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.",
446
+ 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}).`,
447
+ daily: (at) => `\u041A\u0430\u0441\u0430\u043D\u0438\u0435 \u043F\u043E \u0440\u0430\u0441\u043F\u0438\u0441\u0430\u043D\u0438\u044E, ${at}.`,
448
+ 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.`
449
+ };
450
+ 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.";
451
+ var transitionLine = (flow, exitId) => {
452
+ const exit = exitById(flow, exitId);
453
+ return `\u0421\u0440\u0430\u0431\u043E\u0442\u0430\u043B\u043E: ${exit ? describeCondition(flow, exit.when) : exitId}`;
454
+ };
455
+ function reasonLine(reason) {
456
+ switch (reason.kind) {
457
+ case "morning":
458
+ return REASON_LINES.morning;
459
+ case "silence":
460
+ return REASON_LINES.silence(reason.step);
461
+ case "daily":
462
+ return REASON_LINES.daily(reason.at);
463
+ case "night_messages":
464
+ return REASON_LINES.nightMessages(reason.count);
465
+ default:
466
+ return null;
467
+ }
468
+ }
469
+ function silentMsOf(run, at) {
470
+ return run.lastHumanAt === null ? void 0 : Math.max(0, at - run.lastHumanAt);
471
+ }
472
+ function firstReasonsNotice(flow, reasons, exitId, ownNudge = false, silentMs) {
473
+ const silence = reasons.find((reason) => reason.kind === "silence");
474
+ const moved = exitId !== null && reasons.some((reason) => reason.kind === "transition");
475
+ const morning = reasons.some((reason) => reason.kind === "morning");
476
+ const nudgeStep = moved || morning ? 0 : silence?.kind === "silence" ? silence.step : void 0;
477
+ const own = ownNudge && silence !== void 0 && !moved && !morning;
478
+ const exit = moved ? exitOnScreen(flow, exitId, reasons) : void 0;
479
+ const data = {
480
+ ...moved && { exitId },
481
+ reasons: [...reasons],
482
+ ...nudgeStep !== void 0 && { nudgeStep },
483
+ ...own && { nudgeStepOwn: true },
484
+ ...silentMs !== void 0 && { silentMs },
485
+ ...exit && { exit }
486
+ };
487
+ const others = reasons.flatMap((reason) => reasonLine(reason) ?? []);
488
+ if (others.length === 0 && moved) return { kind: "app", name: FLOW_TIMER_NOTICE, title: transitionLine(flow, exitId), data };
489
+ const lines = [...moved ? [`${transitionLine(flow, exitId)}.`] : [], ...others, TASK];
490
+ return { kind: "app", name: FLOW_TIMER_NOTICE, title: lines.join("\n"), data };
491
+ }
492
+ function exitOnScreen(flow, exitId, reasons) {
493
+ const to = reasons.find((reason) => reason.kind === "transition");
494
+ const state = to?.kind === "transition" ? stateById(flow, to.stateId) : void 0;
495
+ const when = exitById(flow, exitId)?.when;
496
+ if (!state && !when) return void 0;
497
+ return { ...state && { stateTitle: state.title }, ...when && { when } };
498
+ }
499
+
500
+ // src/runtime/flow-finish.ts
501
+ var FLOW_DEADLINE_NOTICE = "flow_deadline";
502
+ var DEADLINE_REASON = { kind: "timer", text: "\u0441\u0440\u043E\u043A \u0432\u044B\u0448\u0435\u043B" };
503
+ 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.";
504
+ function afterEndOf(flow) {
505
+ return flow.afterEnd ?? "reply";
506
+ }
507
+ var finalEndsChat = (flow) => flow.schedule !== void 0 || flow.afterEnd !== void 0;
508
+ function deadlineTransition(flow, run, at) {
509
+ const to = flow.schedule?.deadlineState;
510
+ const state = to === void 0 ? void 0 : stateById(flow, to);
511
+ if (!state || run.state === state.id) return null;
512
+ return { from: run.state, to: state.id, exitId: FLOW_DEADLINE_EXIT, decidedBy: "timer", tags: state.tags, at, reason: DEADLINE_REASON };
513
+ }
514
+ function endedRun(flow, run, ended) {
515
+ if (run.endedAt !== void 0) return run;
516
+ const transition = ended.endReason === "deadline" ? deadlineTransition(flow, run, ended.endedAt) : null;
517
+ const moved = transition ? applyTransition(flow, run, transition) : run;
518
+ return { ...moved, endedAt: ended.endedAt, endReason: ended.endReason };
519
+ }
520
+ function createFlowFinish(input) {
521
+ const { runtime } = input;
522
+ async function report(event) {
523
+ const { onFinish } = input;
524
+ if (!onFinish) return;
525
+ const { schedule } = event.pinned.flow;
526
+ let day = null;
527
+ if (schedule) {
528
+ const timeZone = await runtime.timeZones?.of(event.ownerId, event.chatId) ?? "UTC";
529
+ day = flowDay(event.run, event.at, timeZone, schedule).day;
530
+ if (schedule.lengthDays !== void 0) day = Math.min(day, schedule.lengthDays);
531
+ }
532
+ const { answerId: _answerId, firstReasons: _firstReasons, ...run } = event.run;
533
+ const finish = {
534
+ ownerId: event.ownerId,
535
+ chatId: event.chatId,
536
+ flowId: event.pinned.flowId,
537
+ versionId: event.pinned.versionId,
538
+ reason: event.reason,
539
+ ...event.outcome !== void 0 && { outcome: event.outcome },
540
+ day,
541
+ state: event.run.state,
542
+ tags: stateById(event.pinned.flow, event.run.state)?.tags ?? [],
543
+ run,
544
+ at: new Date(event.at)
545
+ };
546
+ 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)}`));
547
+ if (runtime.track) runtime.track(work);
548
+ else await work;
549
+ }
550
+ async function close(chatId, reason, at) {
551
+ const before = await runtime.store.getChat(chatId);
552
+ if (!before || before.endedAt !== null) return false;
553
+ const after = await runtime.store.endChat(chatId, { reason, at: new Date(at).toISOString() });
554
+ return after !== null && after.endReason === reason && Date.parse(after.endedAt ?? "") === at;
555
+ }
556
+ return {
557
+ async afterTransitions({ ownerId, chatId, pinned, run, transitions }) {
558
+ const final = transitions.find((transition) => {
559
+ const state = stateById(pinned.flow, transition.to);
560
+ return state !== void 0 && isFinal(state);
561
+ });
562
+ if (!final) return;
563
+ if (finalEndsChat(pinned.flow) && !await close(chatId, "final", final.at)) return;
564
+ await report({ ownerId, chatId, pinned, run, reason: "final", at: final.at });
565
+ },
566
+ async deadline({ ownerId, chatId, pinned, run, at }) {
567
+ const ended = await close(chatId, "deadline", at);
568
+ const transition = deadlineTransition(pinned.flow, run, at);
569
+ let next = run;
570
+ if (ended && transition) {
571
+ next = applyTransition(pinned.flow, run, transition);
572
+ await runtime.afterTransitions({ ownerId, chatId, pinned, run: next, transitions: [transition] });
573
+ }
574
+ if (ended) await report({ ownerId, chatId, pinned, run: next, reason: "deadline", at });
575
+ return { run: endedRun(pinned.flow, next, { endedAt: at, endReason: "deadline" }), ended };
576
+ },
577
+ async farewell({ ownerId, chatId, pinned, run, at, timeZone, port, firstMessages, delivery }) {
578
+ const schedule = pinned.flow.schedule;
579
+ if (!schedule?.farewellOnDeadline) return;
580
+ const record = await runtime.store.getChat(chatId);
581
+ const chat = record ? { dayClosedOn: record.dayClosedOn, pausedUntil: record.pausedUntil, windowClosed: record.windowClosed } : void 0;
582
+ const allowed = firstMessages ? await firstMessages.check({ ownerId, chatId, at, timeZone, schedule, ...chat && { chat } }) : { allowed: true };
583
+ if (!allowed.allowed) return;
584
+ const text = schedule.farewellText?.trim();
585
+ const silentMs = silentMsOf(run, at);
586
+ const result = await port.deliverNotices({
587
+ ownerId,
588
+ chatId,
589
+ notices: [{ kind: "app", name: FLOW_DEADLINE_NOTICE, title: FAREWELL_NOTICE, data: { reasons: [{ kind: "farewell" }], ...silentMs !== void 0 && { silentMs } } }],
590
+ wakeAgent: true,
591
+ deliveryKey: `flow:${chatId}:deadline`,
592
+ usageSource: "first_message",
593
+ maxServerTurnsInRow: false,
594
+ timeZone,
595
+ ...delivery?.own() && { awaitAnswer: true },
596
+ ...text && { reply: { text } }
597
+ });
598
+ if (result.status !== "delivered" || !result.turnStarted) return;
599
+ if (delivery) await delivery.afterFirstTurn({ ownerId, chatId, answer: result.answer, reasons: [{ kind: "farewell" }], at, timeZone });
600
+ else await firstMessages?.record({ ownerId, at, timeZone });
601
+ },
602
+ async end({ ownerId, chatId, pinned, run, reason, outcome, at }) {
603
+ const ended = await close(chatId, reason, at);
604
+ if (ended) await report({ ownerId, chatId, pinned, run, reason, ...outcome !== void 0 && { outcome }, at });
605
+ return { ended };
606
+ }
607
+ };
608
+ }
609
+
610
+ // src/runtime/prepare-turn.ts
611
+ var FLOW_TIMER_NOTICE = "flow_timer";
612
+ var FLOW_EVENT_NOTICE = "flow_event";
613
+ var HUMAN_ACTIONS = /* @__PURE__ */ new Set(["send", "edit", "regenerate", "retry"]);
614
+ var transitionKey = (transition) => `${transition.from}\0${transition.exitId}`;
615
+ var errorText = (error) => error instanceof Error ? error.message : String(error);
616
+ var dataOf = (notice) => notice.data && typeof notice.data === "object" ? notice.data : {};
617
+ var firstReasonsOf = (notices) => notices.flatMap((notice) => {
618
+ if (notice.kind !== "app" || notice.name !== FLOW_TIMER_NOTICE && notice.name !== FLOW_DEADLINE_NOTICE) return [];
619
+ const { reasons } = dataOf(notice);
620
+ return Array.isArray(reasons) ? reasons : [];
621
+ });
622
+ function flowPrepareTurn(runtime, appTimeZone, finisher) {
623
+ return async (ctx) => {
624
+ if (ctx.audience === "group") return;
625
+ let pinned = await runtime.chatFlow(ctx.ownerId, ctx.chatId, ctx.chat);
626
+ if (!pinned) return;
627
+ const timeOffset = testChatOffset(ctx.chat.data);
628
+ const now = runtime.now() + (timeOffset ?? 0);
629
+ const transitions = [];
630
+ let card;
631
+ const applied = [];
632
+ const step = async (run2, event, out = transitions) => advance(pinned.flow, run2, event, {
633
+ now,
634
+ card: card ??= await runtime.cardValues(ctx.ownerId),
635
+ out,
636
+ onChainLimit: (stopped) => runtime.logError(chainLimitMessage(ctx.chatId, stopped)),
637
+ timeZone
638
+ });
639
+ const record = await runtime.store.getChat(ctx.chatId);
640
+ const page = pageTimeZone(ctx.timeZone, appTimeZone(), ctx.pageTimeZone);
641
+ const known = page ?? (runtime.timeZones ? await runtime.timeZones.known(ctx.ownerId, ctx.chatId) : record?.timeZone || null);
642
+ const timeZone = known ?? runtime.defaultTimeZone ?? ctx.timeZone;
643
+ let run = currentRun(pinned.flow, ctx.state, now);
644
+ const previousAnswer = run.answerId;
645
+ const pending = await runtime.store.takePending(ctx.chatId);
646
+ const appliedInTime = new Set(
647
+ pending.flatMap((change) => change.kind === "transition" && change.appliedAt !== void 0 && change.branchHead === previousAnswer ? [transitionKey(change.transition)] : [])
648
+ );
649
+ let restored = null;
650
+ const restore = () => restored ??= Promise.all(pending.map((change) => runtime.store.addPending(ctx.chatId, change)));
651
+ 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: ${errorText(error)}`));
652
+ if (pending.length > 0) ctx.abortSignal.addEventListener("abort", onAbort, { once: true });
653
+ try {
654
+ return await applyTurn();
655
+ } catch (error) {
656
+ await restore();
657
+ throw error;
658
+ } finally {
659
+ ctx.abortSignal.removeEventListener("abort", onAbort);
660
+ }
661
+ async function applyTurn() {
662
+ const schedule = pinned.flow.schedule;
663
+ if (schedule) run = withDayAnchor(run, timeZone, schedule);
664
+ if (record?.endedAt) {
665
+ run = endedRun(pinned.flow, run, { endedAt: Date.parse(record.endedAt), endReason: record.endReason ?? "final" });
666
+ } else if (schedule && finisher) {
667
+ const deadline = deadlineAt(run, timeZone, schedule);
668
+ if (deadline !== null && deadline <= now) {
669
+ ({ run } = await finisher().deadline({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned, run, at: deadline, timeZone }));
670
+ }
671
+ }
672
+ const ended = run.endedAt !== void 0;
673
+ for (const change of ended ? [] : pending) {
674
+ if (change.kind === "migrate") {
675
+ const next = await runtime.chatFlow(ctx.ownerId, ctx.chatId);
676
+ if (next) {
677
+ run = { ...migrateRun(run, next.flow, change.map, now), answerId: previousAnswer };
678
+ pinned = next;
679
+ }
680
+ } else if (change.kind === "transition") {
681
+ if (change.branchHead !== previousAnswer || run.state !== change.transition.from) continue;
682
+ const out = [];
683
+ run = await step(run, pendingEvent(change.transition, timeZone), out);
684
+ for (const transition of out) (appliedInTime.has(transitionKey(transition)) ? applied : transitions).push(transition);
685
+ } else {
686
+ run = await step(run, { type: "event", event: "app", name: change.name });
687
+ }
688
+ }
689
+ let touchBase = null;
690
+ for (const notice of ended ? [] : ctx.notices) {
691
+ if (notice.kind !== "app") continue;
692
+ const data = dataOf(notice);
693
+ if (notice.name === FLOW_TIMER_NOTICE) {
694
+ if (typeof data.undelivered === "string") run = withoutUndeliveredTouch(run, /* @__PURE__ */ new Set([data.undelivered])).run;
695
+ if (typeof data.exitId === "string") run = await step(run, { type: "timer", exitId: data.exitId, timeZone });
696
+ touchBase = { lastAgentAt: run.lastAgentAt, nudgeStep: run.nudgeStep ?? 0, ...run.nudgeStepOwn && { nudgeStepOwn: true }, agentAnswers: run.agentAnswers };
697
+ if (typeof data.nudgeStep === "number") run = withNudgeStep(run, data.nudgeStep, data.nudgeStepOwn === true);
698
+ } else if (notice.name === FLOW_EVENT_NOTICE && typeof data.name === "string") {
699
+ run = await step(run, { type: "event", event: "app", name: data.name });
700
+ }
701
+ }
702
+ if (!ctx.resumed && HUMAN_ACTIONS.has(ctx.action)) {
703
+ if (record?.windowClosed) await runtime.store.setWindowClosed(ctx.chatId, false);
704
+ run = noteHumanMessage(run, now, schedule ? dayInProgress(now, timeZone, schedule) : void 0, ctx.burstMessageIds?.length ?? 1);
705
+ if (!ended) run = await step(run, { type: "event", event: "message" });
706
+ }
707
+ if (ctx.action === "notice" && !ctx.resumed) run = { ...run, serverTurns: (run.serverTurns ?? 0) + 1 };
708
+ if (ctx.action !== "approval" && ctx.action !== "widget") run = noteAgentAnswer(run, now);
709
+ const { touch: earlierTouch, firstReasons: earlierReasons, ...withoutTouch } = run;
710
+ const continued = ctx.action === "approval" || ctx.action === "widget";
711
+ const touch = touchBase ? { answerId: ctx.messageId, at: now, ...touchBase } : continued ? earlierTouch : void 0;
712
+ const reasons = firstReasonsOf(ctx.notices);
713
+ const firstReasons = reasons.length > 0 ? reasons : continued ? earlierReasons : void 0;
714
+ run = { ...withoutTouch, answerId: ctx.messageId, ...timeOffset !== null && { timeOffset }, ...touch && { touch }, ...firstReasons && { firstReasons } };
715
+ if (ctx.abortSignal.aborted) {
716
+ await restore();
717
+ return void 0;
718
+ }
719
+ if (known && runtime.timeZones) await runtime.timeZones.write(ctx.ownerId, ctx.chatId, known);
720
+ else if (record && page && record.timeZone !== page) await runtime.store.updateChat(ctx.chatId, { timeZone: page }, new Date(runtime.now()).toISOString());
721
+ await runtime.afterTransitions({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned, run, transitions });
722
+ return {
723
+ set: { [FLOW_STATE_KEY]: run },
724
+ ...applied.length + transitions.length > 0 && { notices: [transitionNotice(pinned.flow, [...applied, ...transitions])] }
725
+ };
726
+ }
727
+ };
728
+ }
729
+
730
+ // src/prompt/client-card-block.ts
731
+ function fieldValueText(value) {
732
+ if (typeof value === "string") return value.trim();
733
+ if (typeof value === "number") return String(value);
734
+ if (typeof value === "boolean") return value ? "\u0434\u0430" : "\u043D\u0435\u0442";
735
+ return JSON.stringify(value);
736
+ }
737
+ function questionLine(flow, run, card, now) {
738
+ const question = sideQuestion(flow, run, { card, now });
739
+ const found = question && fieldById(flow, question.fieldId);
740
+ if (!question || !found) return "\u0421\u0435\u0439\u0447\u0430\u0441 \u043D\u0435 \u0441\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u0439 \u043D\u0438\u0447\u0435\u0433\u043E \u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0435 \u043C\u0435\u0436\u0434\u0443 \u0434\u0435\u043B\u043E\u043C: \u043E\u0442\u0432\u0435\u0442\u044C \u043D\u0430 \u0442\u043E, \u0447\u0442\u043E \u043E\u043D \u043F\u0438\u0448\u0435\u0442.";
741
+ const { field } = found;
742
+ const noted = `flow_note { asked: "${field.id}" }`;
743
+ if (question.reason === "learn") return `\u0421\u043D\u0430\u0447\u0430\u043B\u0430 \u0443\u0437\u043D\u0430\u0439: \xAB${field.name}\xBB \u2014 ${field.about} \u0417\u0430\u043F\u0438\u0448\u0438 \u043E\u0442\u0432\u0435\u0442 \u0447\u0435\u0440\u0435\u0437 flow_note.`;
744
+ if (question.reason === "stale") {
745
+ return `\u0414\u0430\u0432\u043D\u043E \u043D\u0435 \u0443\u0442\u043E\u0447\u043D\u044F\u043B\u0438: \xAB${field.name}\xBB (\u0441\u0435\u0439\u0447\u0430\u0441 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E: ${fieldValueText(card[field.id].value)}). \u041C\u044F\u0433\u043A\u043E \u0441\u0432\u0435\u0440\u044C \u043E\u0434\u043D\u0438\u043C \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u043C \u0432\u043E\u043F\u0440\u043E\u0441\u043E\u043C \u043A \u043C\u0435\u0441\u0442\u0443. \u0421\u043F\u0440\u043E\u0441\u0438\u043B \u2014 \u043E\u0442\u043C\u0435\u0442\u044C ${noted}; \u0435\u0441\u043B\u0438 \u0438\u0437\u043C\u0435\u043D\u0438\u043B\u043E\u0441\u044C \u2014 \u0437\u0430\u043F\u0438\u0448\u0438 \u043D\u043E\u0432\u043E\u0435 \u0437\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0447\u0435\u0440\u0435\u0437 flow_note.`;
746
+ }
747
+ return `\u041C\u043E\u0436\u043D\u043E \u0441\u043F\u0440\u043E\u0441\u0438\u0442\u044C \u043C\u0435\u0436\u0434\u0443 \u0434\u0435\u043B\u043E\u043C: \xAB${field.name}\xBB \u2014 ${field.about} \u041E\u0434\u0438\u043D \u043A\u043E\u0440\u043E\u0442\u043A\u0438\u0439 \u0432\u043E\u043F\u0440\u043E\u0441 \u043A \u043C\u0435\u0441\u0442\u0443, \u043D\u0435 \u0432 \u043B\u043E\u0431. \u0421\u043F\u0440\u043E\u0441\u0438\u043B \u2014 \u043E\u0442\u043C\u0435\u0442\u044C ${noted}.`;
748
+ }
749
+ function clientCardBlock(input) {
750
+ const { flow, run, now } = input;
751
+ const facts = input.facts ?? [];
752
+ if (flow.clientCard.length === 0 && flow.chatNotes.length === 0 && facts.length === 0) return "";
753
+ const byId = new Map(input.card.map((entry) => [entry.fieldId, { value: entry.value, updatedAt: Date.parse(entry.updatedAt) }]));
754
+ const known = (field, value) => fieldValueFits(field, value);
755
+ const cardLines = flow.clientCard.flatMap((field) => {
756
+ const entry = byId.get(field.id);
757
+ if (!entry || !known(field, entry.value)) return [];
758
+ const stale = isFieldStale(field, entry.updatedAt, now) ? " (\u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E \u0434\u0430\u0432\u043D\u043E, \u043C\u043E\u0433\u043B\u043E \u0438\u0437\u043C\u0435\u043D\u0438\u0442\u044C\u0441\u044F)" : "";
759
+ return [`- ${field.name}: ${fieldValueText(entry.value)}${stale}`];
760
+ });
761
+ const noteLines = flow.chatNotes.flatMap((field) => known(field, run.notes[field.id]) ? [`- ${field.name}: ${fieldValueText(run.notes[field.id])}`] : []);
762
+ const lines = ["\u0427\u0442\u043E \u0438\u0437\u0432\u0435\u0441\u0442\u043D\u043E \u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0435 (\u043A\u0430\u0440\u0442\u043E\u0447\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0430):", ...cardLines.length > 0 ? cardLines : facts.length > 0 ? [] : ["- \u043F\u043E\u043A\u0430 \u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u0437\u0430\u043F\u0438\u0441\u0430\u043D\u043E"]];
763
+ if (facts.length > 0) lines.push("", "\u0424\u0430\u043A\u0442\u044B \u043E \u0447\u0435\u043B\u043E\u0432\u0435\u043A\u0435:", ...facts.map((fact) => `- ${fact.text}`));
764
+ if (noteLines.length > 0) lines.push("", "\u0417\u0430\u043C\u0435\u0442\u043A\u0438 \u044D\u0442\u043E\u0433\u043E \u0447\u0430\u0442\u0430:", ...noteLines);
765
+ lines.push("", questionLine(flow, run, Object.fromEntries(byId), now));
766
+ return lines.join("\n");
767
+ }
768
+
769
+ export {
770
+ judgeExits,
771
+ stateGoal,
772
+ stateCard,
773
+ reportTransition,
774
+ storedRun,
775
+ currentRun,
776
+ chatRun,
777
+ personTimeZone,
778
+ runOutsideTurn,
779
+ advance,
780
+ withPending,
781
+ chainLimitMessage,
782
+ exactFirstText,
783
+ transitionReport,
784
+ FIRST_MESSAGE_RETRY_MS,
785
+ chatWriteBlock,
786
+ createFirstMessageRules,
787
+ UNKNOWN_TIME_ZONE,
788
+ pageTimeZone,
789
+ createChatTimeZones,
790
+ ianaTimeZone,
791
+ createFlowTimeZones,
792
+ flowTestChatData,
793
+ testChatOffset,
794
+ chatTime,
795
+ FlowTestClockError,
796
+ advanceTestClock,
797
+ FLOW_TIMER_NOTICE,
798
+ FLOW_EVENT_NOTICE,
799
+ flowPrepareTurn,
800
+ silentMsOf,
801
+ firstReasonsNotice,
802
+ FLOW_DEADLINE_NOTICE,
803
+ afterEndOf,
804
+ endedRun,
805
+ createFlowFinish,
806
+ fieldValueText,
807
+ clientCardBlock,
808
+ FACT_PREFIX,
809
+ MAX_FACTS,
810
+ MAX_FACT_LENGTH,
811
+ factId,
812
+ sameFact,
813
+ isFactEntry,
814
+ clientFactStore,
815
+ factEntry
816
+ };
817
+ //# sourceMappingURL=chunk-KRZMJJKG.js.map