@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.
- package/CHANGELOG.md +12 -0
- package/dist/{chunk-5RDY7QBA.js → chunk-7VR4YNTJ.js} +88 -6
- package/dist/chunk-7VR4YNTJ.js.map +1 -0
- package/dist/chunk-KRZMJJKG.js +817 -0
- package/dist/chunk-KRZMJJKG.js.map +1 -0
- package/dist/chunk-L7XNC3RB.js +71 -0
- package/dist/chunk-L7XNC3RB.js.map +1 -0
- package/dist/format.js +7 -7
- package/dist/index.js +101 -668
- package/dist/index.js.map +1 -1
- package/dist/react.js +143 -10
- package/dist/react.js.map +1 -1
- package/dist/types/labels/index.d.ts +5 -1
- package/dist/types/labels/notices.d.ts +43 -0
- package/dist/types/react/index.d.ts +2 -0
- package/dist/types/react/notice-lines.d.ts +14 -0
- package/dist/types/react/test-chat/use-flow-test-chat.d.ts +9 -2
- package/dist/types/react.d.ts +2 -2
- package/dist/types/runtime/first-reasons-notice.d.ts +6 -1
- package/dist/types/runtime/flow-types.d.ts +3 -1
- package/dist/types/runtime/notice-screen.d.ts +5 -0
- package/package.json +4 -4
- package/dist/chunk-22FWN7NU.js +0 -100
- package/dist/chunk-22FWN7NU.js.map +0 -1
- package/dist/chunk-3K72EVAT.js +0 -165
- package/dist/chunk-3K72EVAT.js.map +0 -1
- package/dist/chunk-5RDY7QBA.js.map +0 -1
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-
|
|
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
|
-
|
|
29
|
-
|
|
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,11 +123,11 @@ import {
|
|
|
90
123
|
noteHumanMessage,
|
|
91
124
|
noteRejectedClaim,
|
|
92
125
|
noteSideQuestion,
|
|
93
|
-
overdueOnEntry,
|
|
94
126
|
parseBooleanAnswer,
|
|
95
127
|
parseDuration,
|
|
96
128
|
parseFlow,
|
|
97
129
|
placeOfPath,
|
|
130
|
+
resolveFirstMessages,
|
|
98
131
|
returnTarget,
|
|
99
132
|
sameText,
|
|
100
133
|
sideQuestion,
|
|
@@ -106,7 +139,7 @@ import {
|
|
|
106
139
|
withDayAnchor,
|
|
107
140
|
withNudgeStep,
|
|
108
141
|
withoutUndeliveredTouch
|
|
109
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-7VR4YNTJ.js";
|
|
110
143
|
import {
|
|
111
144
|
appliedInTime,
|
|
112
145
|
byVersionAndState,
|
|
@@ -173,6 +206,42 @@ function turnToolNames(source, catalog, input) {
|
|
|
173
206
|
}
|
|
174
207
|
}
|
|
175
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
|
+
|
|
176
245
|
// src/prompt/rules/time-zone.ts
|
|
177
246
|
var TIME_ZONE_RULES = `## \u0414\u043D\u0438 \u0438 \u0432\u0440\u0435\u043C\u044F
|
|
178
247
|
|
|
@@ -264,184 +333,9 @@ function afterEndRules(flow) {
|
|
|
264
333
|
${shared}` : AFTER_END_RULES;
|
|
265
334
|
}
|
|
266
335
|
|
|
267
|
-
// src/prompt/state-card.ts
|
|
268
|
-
function judgeExits(flow, stateId) {
|
|
269
|
-
const state = stateById(flow, stateId);
|
|
270
|
-
if (!state) return [];
|
|
271
|
-
return activeExits(flow, state).flatMap(({ exit }) => exit.when.type === "judge" ? [{ exitId: exit.id, text: exit.when.text }] : []);
|
|
272
|
-
}
|
|
273
|
-
function stateGoal(instructions) {
|
|
274
|
-
return instructions.split("\n")[0].trim();
|
|
275
|
-
}
|
|
276
|
-
function stateCard(flow, run, present) {
|
|
277
|
-
const state = stateById(flow, run.state);
|
|
278
|
-
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.`;
|
|
279
|
-
const [goal = "", ...rest] = state.instructions.split("\n");
|
|
280
|
-
const lines = [`\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435: \xAB${state.title}\xBB`, `\u0426\u0435\u043B\u044C: ${goal.trim()}`];
|
|
281
|
-
const how = rest.join("\n").trim();
|
|
282
|
-
if (how) lines.push("", how);
|
|
283
|
-
const fields = state.learn.map((id) => fieldById(flow, id)).flatMap((found) => found ? [`- ${found.field.name}: ${found.field.about}`] : []);
|
|
284
|
-
if (fields.length > 0) lines.push("", "\u0423\u0437\u043D\u0430\u0442\u044C \u0437\u0434\u0435\u0441\u044C:", ...fields);
|
|
285
|
-
const conditions = judgeExits(flow, state.id);
|
|
286
|
-
if (isFinal(state)) {
|
|
287
|
-
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.");
|
|
288
|
-
} else if (conditions.length > 0) {
|
|
289
|
-
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}`));
|
|
290
|
-
}
|
|
291
|
-
const inSet = (name) => !present || present.has(name);
|
|
292
|
-
const tools = state.tools.filter(inSet);
|
|
293
|
-
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(", ")}.`);
|
|
294
|
-
const unavailable = flow.tools.filter((name) => inSet(name) && !state.tools.includes(name));
|
|
295
|
-
if (unavailable.length > 0) lines.push(`\u0421\u0435\u0439\u0447\u0430\u0441 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B: ${unavailable.join(", ")}.`);
|
|
296
|
-
return lines.join("\n");
|
|
297
|
-
}
|
|
298
|
-
|
|
299
336
|
// src/runtime/note-tools.ts
|
|
300
337
|
import { tool } from "ai";
|
|
301
338
|
import { z } from "zod";
|
|
302
|
-
|
|
303
|
-
// src/runtime/flow-events.ts
|
|
304
|
-
async function reportTransition(handler, event, logError) {
|
|
305
|
-
if (!handler) return;
|
|
306
|
-
try {
|
|
307
|
-
await handler(event);
|
|
308
|
-
} catch (error) {
|
|
309
|
-
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)}`);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
function storedRun(value) {
|
|
313
|
-
return value && typeof value === "object" && typeof value.state === "string" ? value : null;
|
|
314
|
-
}
|
|
315
|
-
function currentRun(flow, values, now) {
|
|
316
|
-
return storedRun(values?.[FLOW_STATE_KEY]) ?? startRun(flow, now);
|
|
317
|
-
}
|
|
318
|
-
function placedRun(flow, placement, now, timeZone) {
|
|
319
|
-
const start = startRun(flow, now);
|
|
320
|
-
const state = placement.stateId !== void 0 && stateById(flow, placement.stateId) || stateById(flow, flow.start);
|
|
321
|
-
const startedAt = placement.startedAt ?? now;
|
|
322
|
-
const enteredAt = placement.enteredAt ?? placement.startedAt ?? now;
|
|
323
|
-
return {
|
|
324
|
-
...start,
|
|
325
|
-
state: state.id,
|
|
326
|
-
startedAt,
|
|
327
|
-
enteredAt,
|
|
328
|
-
path: [{ state: state.id, at: enteredAt, tags: state.tags, from: null, exitId: null, decidedBy: "start" }],
|
|
329
|
-
...placement.lastHumanAt !== void 0 && { lastHumanAt: placement.lastHumanAt },
|
|
330
|
-
...placement.lastAgentAt !== void 0 && { lastAgentAt: placement.lastAgentAt },
|
|
331
|
-
...placement.nudgeStep !== void 0 && { nudgeStep: placement.nudgeStep },
|
|
332
|
-
...placement.humanToday && flow.schedule && timeZone && { dayCounts: { [dayInProgress(placement.humanToday.at, timeZone, flow.schedule)]: placement.humanToday.count } }
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
var msOf = (value) => value === void 0 ? void 0 : Date.parse(value);
|
|
336
|
-
function recordPlacement(record) {
|
|
337
|
-
const placement = record.placement;
|
|
338
|
-
if (!placement) return null;
|
|
339
|
-
const lastHumanAt = msOf(placement.lastHumanAt);
|
|
340
|
-
const lastAgentAt = msOf(placement.lastAgentAt);
|
|
341
|
-
return {
|
|
342
|
-
stateId: record.stateId,
|
|
343
|
-
startedAt: Date.parse(placement.startedAt),
|
|
344
|
-
enteredAt: Date.parse(placement.enteredAt),
|
|
345
|
-
...lastHumanAt !== void 0 && { lastHumanAt },
|
|
346
|
-
...lastAgentAt !== void 0 && { lastAgentAt },
|
|
347
|
-
...placement.nudgeStep !== void 0 && { nudgeStep: placement.nudgeStep },
|
|
348
|
-
...placement.humanToday && { humanToday: { at: Date.parse(placement.humanToday.at), count: placement.humanToday.count } }
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
function chatRun(flow, values, record, now, timeZone) {
|
|
352
|
-
const stored = storedRun(values?.[FLOW_STATE_KEY]);
|
|
353
|
-
if (stored) return stored;
|
|
354
|
-
const placement = record ? recordPlacement(record) : null;
|
|
355
|
-
return placement ? placedRun(flow, placement, now, timeZone) : startRun(flow, now);
|
|
356
|
-
}
|
|
357
|
-
async function personTimeZone(runtime, chat) {
|
|
358
|
-
const known = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : (await runtime.store.getChat(chat.chatId))?.timeZone;
|
|
359
|
-
return known || runtime.defaultTimeZone || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
360
|
-
}
|
|
361
|
-
async function runOutsideTurn(runtime, chat, flow, values, now, record) {
|
|
362
|
-
const stored = storedRun(values?.[FLOW_STATE_KEY]);
|
|
363
|
-
if (stored) return stored;
|
|
364
|
-
const found = record === void 0 ? await runtime.store.getChat(chat.chatId) : record;
|
|
365
|
-
if (!found?.placement) return startRun(flow, now);
|
|
366
|
-
const timeZone = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : found.timeZone || void 0;
|
|
367
|
-
return chatRun(flow, values, found, now, timeZone);
|
|
368
|
-
}
|
|
369
|
-
function advance(flow, run, event, input) {
|
|
370
|
-
let current = run;
|
|
371
|
-
let step = event;
|
|
372
|
-
let entered = false;
|
|
373
|
-
let stopped = false;
|
|
374
|
-
while (step) {
|
|
375
|
-
const transition = nextTransition(flow, current, step, input.now);
|
|
376
|
-
if (!transition) {
|
|
377
|
-
if (current.chain >= FLOW_CHAIN_LIMIT && nextTransition(flow, { ...current, chain: 0 }, step, input.now)) {
|
|
378
|
-
if (!stopped) input.onChainLimit(current);
|
|
379
|
-
stopped = true;
|
|
380
|
-
}
|
|
381
|
-
const overdue = entered && !stopped && step.type === "data" ? overdueOnEntry(flow, current, input.now, input.timeZone) : null;
|
|
382
|
-
step = overdue ? { type: "timer", exitId: overdue, timeZone: input.timeZone } : null;
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
current = applyTransition(flow, current, transition);
|
|
386
|
-
input.out.push(transition);
|
|
387
|
-
entered = true;
|
|
388
|
-
step = { type: "data", card: input.card };
|
|
389
|
-
}
|
|
390
|
-
return current;
|
|
391
|
-
}
|
|
392
|
-
function pendingEvent(transition, timeZone) {
|
|
393
|
-
if (transition.decidedBy === "timer") return { type: "timer", exitId: transition.exitId, timeZone };
|
|
394
|
-
return { type: "verdict", exitId: transition.exitId, probability: transition.probability ?? 1, decidedBy: "judge" };
|
|
395
|
-
}
|
|
396
|
-
function withPending(flow, run, pending, input) {
|
|
397
|
-
let current = run;
|
|
398
|
-
for (const change of pending) {
|
|
399
|
-
if (change.kind === "transition") {
|
|
400
|
-
if (change.branchHead !== run.answerId || current.state !== change.transition.from) continue;
|
|
401
|
-
current = advance(flow, current, pendingEvent(change.transition, input.timeZone), input);
|
|
402
|
-
} else if (change.kind === "event") {
|
|
403
|
-
current = advance(flow, current, { type: "event", event: "app", name: change.name }, input);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
return current;
|
|
407
|
-
}
|
|
408
|
-
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`;
|
|
409
|
-
function exitById(flow, exitId) {
|
|
410
|
-
for (const state of flow.states) {
|
|
411
|
-
const found = state.exits.find((exit) => exit.id === exitId);
|
|
412
|
-
if (found) return found;
|
|
413
|
-
}
|
|
414
|
-
return flow.anyState.exits.find((exit) => exit.id === exitId);
|
|
415
|
-
}
|
|
416
|
-
function exactFirstText(flow, stateId) {
|
|
417
|
-
return stateById(flow, stateId)?.firstMessage ?? null;
|
|
418
|
-
}
|
|
419
|
-
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`;
|
|
420
|
-
function transitionNotice(flow, transitions) {
|
|
421
|
-
const lines = transitions.map((transition) => {
|
|
422
|
-
const exit = exitById(flow, transition.exitId);
|
|
423
|
-
const title = stateById(flow, transition.to)?.title ?? transition.to;
|
|
424
|
-
return `\u0421\u0440\u0430\u0431\u043E\u0442\u0430\u043B\u043E: ${exit ? describeCondition(flow, exit.when) : transition.exitId} \u2192 \xAB${title}\xBB.`;
|
|
425
|
-
});
|
|
426
|
-
const last = transitions.at(-1);
|
|
427
|
-
const first = exactFirstText(flow, last.to);
|
|
428
|
-
if (first) lines.push(sayExactly(first));
|
|
429
|
-
return {
|
|
430
|
-
kind: "app",
|
|
431
|
-
name: "flow_transition",
|
|
432
|
-
title: lines.join("\n"),
|
|
433
|
-
data: { from: transitions[0].from, to: last.to, exitIds: transitions.map((transition) => transition.exitId) }
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
function transitionReport(flow, run, transitions) {
|
|
437
|
-
const from = stateById(flow, transitions[0].from)?.title ?? transitions[0].from;
|
|
438
|
-
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)];
|
|
439
|
-
const first = exactFirstText(flow, run.state);
|
|
440
|
-
if (first) lines.push("", sayExactly(first));
|
|
441
|
-
return lines.join("\n");
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
// src/runtime/note-tools.ts
|
|
445
339
|
var FLOW_NOTE_TOOL = "flow_note";
|
|
446
340
|
var CLIENT_CARD_TOOL = "client_card";
|
|
447
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.";
|
|
@@ -674,105 +568,6 @@ function createClientCard(store, now, adapter, logError = () => {
|
|
|
674
568
|
};
|
|
675
569
|
}
|
|
676
570
|
|
|
677
|
-
// src/runtime/first-message-retry.ts
|
|
678
|
-
var FIRST_MESSAGE_RETRY_MS = 36e5;
|
|
679
|
-
var FIRST_MESSAGE_MIN_RETRY_MS = 6e4;
|
|
680
|
-
|
|
681
|
-
// src/runtime/first-message-rules.ts
|
|
682
|
-
function chatWriteBlock(chat, at, timeZone, schedule) {
|
|
683
|
-
if (chat.windowClosed) return { retryAt: null };
|
|
684
|
-
const pausedUntil = chat.pausedUntil ? Date.parse(chat.pausedUntil) : 0;
|
|
685
|
-
if (pausedUntil > at) return { retryAt: pausedUntil };
|
|
686
|
-
if (schedule && dayClosedToday(chat.dayClosedOn, at, timeZone, schedule)) return { retryAt: nextDayStartAfter(at, timeZone, schedule) };
|
|
687
|
-
return null;
|
|
688
|
-
}
|
|
689
|
-
function scenarioRules(rules, schedule) {
|
|
690
|
-
if (!schedule) return rules;
|
|
691
|
-
return {
|
|
692
|
-
quietHours: { from: schedule.nightFrom ?? rules.quietHours.from, to: schedule.dayStart },
|
|
693
|
-
maxPerDay: schedule.firstMessages?.maxPerDay ?? rules.maxPerDay
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
function createFirstMessageRules(setting, store) {
|
|
697
|
-
const rules = resolveFirstMessages(setting);
|
|
698
|
-
if (!rules) return null;
|
|
699
|
-
return {
|
|
700
|
-
async check({ ownerId, chatId, at, timeZone, schedule, chat }) {
|
|
701
|
-
const blocked = chat ? chatWriteBlock(chat, at, timeZone, schedule) : null;
|
|
702
|
-
if (blocked) return { allowed: false, retryAt: blocked.retryAt };
|
|
703
|
-
const sentToday = await store.countFirstMessages(ownerId, localDay(at, timeZone));
|
|
704
|
-
const slot = firstMessageSlot(scenarioRules(rules, schedule), { at, timeZone, sentToday });
|
|
705
|
-
if (!slot.allowed) return slot;
|
|
706
|
-
const answer = rules.canWriteNow ? await rules.canWriteNow({ ownerId, chatId, at: new Date(at) }) : true;
|
|
707
|
-
if (answer === true) return { allowed: true };
|
|
708
|
-
return { allowed: false, retryAt: retryAtOf(answer, at) };
|
|
709
|
-
},
|
|
710
|
-
async record({ ownerId, at, timeZone }) {
|
|
711
|
-
await store.addFirstMessage(ownerId, localDay(at, timeZone));
|
|
712
|
-
}
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
function retryAtOf(answer, at) {
|
|
716
|
-
if (answer === "never") return null;
|
|
717
|
-
const retryAt = typeof answer === "object" && answer !== null && answer.retryAt instanceof Date ? answer.retryAt.getTime() : Number.NaN;
|
|
718
|
-
if (!Number.isFinite(retryAt)) return at + FIRST_MESSAGE_RETRY_MS;
|
|
719
|
-
return Math.max(retryAt, at + FIRST_MESSAGE_MIN_RETRY_MS);
|
|
720
|
-
}
|
|
721
|
-
var UNKNOWN_TIME_ZONE = "";
|
|
722
|
-
var serverTimeZone = () => Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
723
|
-
function pageTimeZone(turnZone, appZone, sent) {
|
|
724
|
-
if (sent !== void 0) return sent;
|
|
725
|
-
return turnZone === (appZone ?? serverTimeZone()) ? null : turnZone;
|
|
726
|
-
}
|
|
727
|
-
function createChatTimeZones(input) {
|
|
728
|
-
const warned = /* @__PURE__ */ new Set();
|
|
729
|
-
return {
|
|
730
|
-
of(chatId, stored) {
|
|
731
|
-
if (stored) return stored;
|
|
732
|
-
if (input.defaultTimeZone) return input.defaultTimeZone;
|
|
733
|
-
const app = input.appTimeZone();
|
|
734
|
-
if (app) return app;
|
|
735
|
-
const server = serverTimeZone();
|
|
736
|
-
if (!warned.has(chatId)) {
|
|
737
|
-
warned.add(chatId);
|
|
738
|
-
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}`);
|
|
739
|
-
}
|
|
740
|
-
return server;
|
|
741
|
-
}
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
function ianaTimeZone(value) {
|
|
745
|
-
if (!value.trim()) return null;
|
|
746
|
-
try {
|
|
747
|
-
return new Intl.DateTimeFormat("en-US", { timeZone: value.trim() }).resolvedOptions().timeZone;
|
|
748
|
-
} catch {
|
|
749
|
-
return null;
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
function createFlowTimeZones(input) {
|
|
753
|
-
const { store } = input;
|
|
754
|
-
const iso = () => new Date(input.now()).toISOString();
|
|
755
|
-
const readPerson = (ownerId) => input.people ? input.people.readTimeZone(ownerId) : store.readTimeZone(ownerId);
|
|
756
|
-
const writePerson = (ownerId, timeZone) => input.people ? input.people.writeTimeZone(ownerId, timeZone) : store.writeTimeZone(ownerId, timeZone, iso());
|
|
757
|
-
async function known(ownerId, chatId) {
|
|
758
|
-
const person = await readPerson(ownerId);
|
|
759
|
-
if (person) return person;
|
|
760
|
-
return (await store.getChat(chatId))?.timeZone || null;
|
|
761
|
-
}
|
|
762
|
-
return {
|
|
763
|
-
known,
|
|
764
|
-
fallback: input.fallback,
|
|
765
|
-
async of(ownerId, chatId) {
|
|
766
|
-
return await known(ownerId, chatId) ?? input.fallback(chatId);
|
|
767
|
-
},
|
|
768
|
-
async write(ownerId, chatId, timeZone) {
|
|
769
|
-
if (await readPerson(ownerId) !== timeZone) await writePerson(ownerId, timeZone);
|
|
770
|
-
const record = await store.getChat(chatId);
|
|
771
|
-
if (record && record.timeZone !== timeZone) await store.updateChat(chatId, { timeZone }, iso());
|
|
772
|
-
}
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
|
|
776
571
|
// src/runtime/decisions-client.ts
|
|
777
572
|
function decisionsUrl(baseUrl) {
|
|
778
573
|
const base = baseUrl.replace(/\/+$/, "");
|
|
@@ -1009,375 +804,11 @@ function errorText2(error) {
|
|
|
1009
804
|
import { randomUUID } from "crypto";
|
|
1010
805
|
import { ChatNotFoundError as ChatNotFoundError2, SpendingLimitReachedError } from "@ykurilov/agent-chat-core";
|
|
1011
806
|
|
|
1012
|
-
// src/runtime/test-clock.ts
|
|
1013
|
-
var FLOW_TEST_CHAT_KEY = "flowTestChat";
|
|
1014
|
-
function flowTestChatData() {
|
|
1015
|
-
return { [FLOW_TEST_CHAT_KEY]: { timeOffset: 0 } };
|
|
1016
|
-
}
|
|
1017
|
-
function testChatOffset(data) {
|
|
1018
|
-
const mark = data?.[FLOW_TEST_CHAT_KEY];
|
|
1019
|
-
if (!mark || typeof mark !== "object") return null;
|
|
1020
|
-
const offset = mark.timeOffset;
|
|
1021
|
-
return typeof offset === "number" && Number.isFinite(offset) ? offset : 0;
|
|
1022
|
-
}
|
|
1023
|
-
var chatTime = (run, serverNow) => serverNow + (run?.timeOffset ?? 0);
|
|
1024
|
-
var FlowTestClockError = class extends Error {
|
|
1025
|
-
constructor(code, message) {
|
|
1026
|
-
super(message);
|
|
1027
|
-
this.code = code;
|
|
1028
|
-
this.name = "FlowTestClockError";
|
|
1029
|
-
}
|
|
1030
|
-
code;
|
|
1031
|
-
};
|
|
1032
|
-
var NEXT_FIRST_MESSAGE_STEPS = 50;
|
|
1033
|
-
var TURN_WAIT_LIMIT = 5;
|
|
1034
|
-
async function placeRun(runtime, port, chat, flow, now) {
|
|
1035
|
-
const { values } = await port.getChatState(chat.ownerId, chat.chatId);
|
|
1036
|
-
const stored = storedRun(values[FLOW_STATE_KEY]);
|
|
1037
|
-
if (stored) return stored;
|
|
1038
|
-
const record = await runtime.store.getChat(chat.chatId);
|
|
1039
|
-
return record ? runOutsideTurn(runtime, chat, flow, values, now, record) : null;
|
|
1040
|
-
}
|
|
1041
|
-
async function msToNextDay(runtime, port, ownerId, chatId, offset) {
|
|
1042
|
-
const pinned = await runtime.chatFlow(ownerId, chatId);
|
|
1043
|
-
const schedule = pinned?.flow.schedule;
|
|
1044
|
-
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)");
|
|
1045
|
-
const chatNow = runtime.now() + offset;
|
|
1046
|
-
const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, chatNow);
|
|
1047
|
-
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");
|
|
1048
|
-
const timeZone = runtime.timeZones ? await runtime.timeZones.of(ownerId, chatId) : "UTC";
|
|
1049
|
-
return flowDay(run, chatNow, timeZone, schedule).nextDayStartAt - chatNow;
|
|
1050
|
-
}
|
|
1051
|
-
async function turnFinished(port, ownerId, chatId) {
|
|
1052
|
-
for (let turn = 0; turn < TURN_WAIT_LIMIT; turn += 1) {
|
|
1053
|
-
const stream = await port.subscribe?.(ownerId, chatId);
|
|
1054
|
-
if (!stream) return;
|
|
1055
|
-
for await (const _chunk of stream) ;
|
|
1056
|
-
}
|
|
1057
|
-
}
|
|
1058
|
-
async function advanceTestClock(input) {
|
|
1059
|
-
const { runtime, port, timers, ownerId, chatId, clock } = input;
|
|
1060
|
-
const offset = testChatOffset(await port.getChatData(ownerId, chatId));
|
|
1061
|
-
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`);
|
|
1062
|
-
if (clock.to === "next_first_message") return advanceToNextFirstMessage({ runtime, port, timers, ownerId, chatId, offset });
|
|
1063
|
-
const ms = clock.to === "next_day_start" ? await msToNextDay(runtime, port, ownerId, chatId, offset) : clock.ms ?? Number.NaN;
|
|
1064
|
-
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");
|
|
1065
|
-
const timeOffset = offset + ms;
|
|
1066
|
-
await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });
|
|
1067
|
-
const pinned = timers && await runtime.chatFlow(ownerId, chatId);
|
|
1068
|
-
if (!timers || !pinned) return { timeOffset };
|
|
1069
|
-
const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);
|
|
1070
|
-
if (!run) return { timeOffset };
|
|
1071
|
-
await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });
|
|
1072
|
-
await timers.tick();
|
|
1073
|
-
await turnFinished(port, ownerId, chatId);
|
|
1074
|
-
return { timeOffset };
|
|
1075
|
-
}
|
|
1076
|
-
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`);
|
|
1077
|
-
async function advanceToNextFirstMessage(input) {
|
|
1078
|
-
const { runtime, port, timers, ownerId, chatId } = input;
|
|
1079
|
-
const pinned = timers && await runtime.chatFlow(ownerId, chatId);
|
|
1080
|
-
if (!timers?.watch || !pinned) throw nothingScheduled(chatId);
|
|
1081
|
-
let timeOffset = input.offset;
|
|
1082
|
-
for (let step = 0; step < NEXT_FIRST_MESSAGE_STEPS; step += 1) {
|
|
1083
|
-
const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);
|
|
1084
|
-
if (!run) throw nothingScheduled(chatId);
|
|
1085
|
-
if (step === 0) await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });
|
|
1086
|
-
const record = await runtime.store.getChat(chatId);
|
|
1087
|
-
if (!record || record.endedAt !== null || record.dueAt === null) throw nothingScheduled(chatId);
|
|
1088
|
-
const serverNow = runtime.now();
|
|
1089
|
-
const shift = Math.max(0, Date.parse(record.dueAt) - serverNow);
|
|
1090
|
-
if (shift > 0) {
|
|
1091
|
-
timeOffset += shift;
|
|
1092
|
-
await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });
|
|
1093
|
-
await runtime.store.setDue(chatId, { at: new Date(serverNow).toISOString(), exitId: record.dueExitId ?? FLOW_FIRST_EXIT, branchHead: record.dueBranchHead });
|
|
1094
|
-
}
|
|
1095
|
-
let reason = null;
|
|
1096
|
-
const unwatch = timers.watch(chatId, (fired) => reason ??= fired);
|
|
1097
|
-
try {
|
|
1098
|
-
await timers.tick();
|
|
1099
|
-
await turnFinished(port, ownerId, chatId);
|
|
1100
|
-
} finally {
|
|
1101
|
-
unwatch();
|
|
1102
|
-
}
|
|
1103
|
-
if (reason) return { timeOffset, reason };
|
|
1104
|
-
}
|
|
1105
|
-
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`);
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
// src/runtime/flow-finish.ts
|
|
1109
|
-
var FLOW_DEADLINE_NOTICE = "flow_deadline";
|
|
1110
|
-
var DEADLINE_REASON = { kind: "timer", text: "\u0441\u0440\u043E\u043A \u0432\u044B\u0448\u0435\u043B" };
|
|
1111
|
-
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.";
|
|
1112
|
-
function afterEndOf(flow) {
|
|
1113
|
-
return flow.afterEnd ?? "reply";
|
|
1114
|
-
}
|
|
1115
|
-
var finalEndsChat = (flow) => flow.schedule !== void 0 || flow.afterEnd !== void 0;
|
|
1116
|
-
function deadlineTransition(flow, run, at) {
|
|
1117
|
-
const to = flow.schedule?.deadlineState;
|
|
1118
|
-
const state = to === void 0 ? void 0 : stateById(flow, to);
|
|
1119
|
-
if (!state || run.state === state.id) return null;
|
|
1120
|
-
return { from: run.state, to: state.id, exitId: FLOW_DEADLINE_EXIT, decidedBy: "timer", tags: state.tags, at, reason: DEADLINE_REASON };
|
|
1121
|
-
}
|
|
1122
|
-
function endedRun(flow, run, ended) {
|
|
1123
|
-
if (run.endedAt !== void 0) return run;
|
|
1124
|
-
const transition = ended.endReason === "deadline" ? deadlineTransition(flow, run, ended.endedAt) : null;
|
|
1125
|
-
const moved = transition ? applyTransition(flow, run, transition) : run;
|
|
1126
|
-
return { ...moved, endedAt: ended.endedAt, endReason: ended.endReason };
|
|
1127
|
-
}
|
|
1128
|
-
function createFlowFinish(input) {
|
|
1129
|
-
const { runtime } = input;
|
|
1130
|
-
async function report(event) {
|
|
1131
|
-
const { onFinish } = input;
|
|
1132
|
-
if (!onFinish) return;
|
|
1133
|
-
const { schedule } = event.pinned.flow;
|
|
1134
|
-
let day = null;
|
|
1135
|
-
if (schedule) {
|
|
1136
|
-
const timeZone = await runtime.timeZones?.of(event.ownerId, event.chatId) ?? "UTC";
|
|
1137
|
-
day = flowDay(event.run, event.at, timeZone, schedule).day;
|
|
1138
|
-
if (schedule.lengthDays !== void 0) day = Math.min(day, schedule.lengthDays);
|
|
1139
|
-
}
|
|
1140
|
-
const { answerId: _answerId, firstReasons: _firstReasons, ...run } = event.run;
|
|
1141
|
-
const finish = {
|
|
1142
|
-
ownerId: event.ownerId,
|
|
1143
|
-
chatId: event.chatId,
|
|
1144
|
-
flowId: event.pinned.flowId,
|
|
1145
|
-
versionId: event.pinned.versionId,
|
|
1146
|
-
reason: event.reason,
|
|
1147
|
-
...event.outcome !== void 0 && { outcome: event.outcome },
|
|
1148
|
-
day,
|
|
1149
|
-
state: event.run.state,
|
|
1150
|
-
tags: stateById(event.pinned.flow, event.run.state)?.tags ?? [],
|
|
1151
|
-
run,
|
|
1152
|
-
at: new Date(event.at)
|
|
1153
|
-
};
|
|
1154
|
-
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)}`));
|
|
1155
|
-
if (runtime.track) runtime.track(work);
|
|
1156
|
-
else await work;
|
|
1157
|
-
}
|
|
1158
|
-
async function close(chatId, reason, at) {
|
|
1159
|
-
const before = await runtime.store.getChat(chatId);
|
|
1160
|
-
if (!before || before.endedAt !== null) return false;
|
|
1161
|
-
const after = await runtime.store.endChat(chatId, { reason, at: new Date(at).toISOString() });
|
|
1162
|
-
return after !== null && after.endReason === reason && Date.parse(after.endedAt ?? "") === at;
|
|
1163
|
-
}
|
|
1164
|
-
return {
|
|
1165
|
-
async afterTransitions({ ownerId, chatId, pinned, run, transitions }) {
|
|
1166
|
-
const final = transitions.find((transition) => {
|
|
1167
|
-
const state = stateById(pinned.flow, transition.to);
|
|
1168
|
-
return state !== void 0 && isFinal(state);
|
|
1169
|
-
});
|
|
1170
|
-
if (!final) return;
|
|
1171
|
-
if (finalEndsChat(pinned.flow) && !await close(chatId, "final", final.at)) return;
|
|
1172
|
-
await report({ ownerId, chatId, pinned, run, reason: "final", at: final.at });
|
|
1173
|
-
},
|
|
1174
|
-
async deadline({ ownerId, chatId, pinned, run, at }) {
|
|
1175
|
-
const ended = await close(chatId, "deadline", at);
|
|
1176
|
-
const transition = deadlineTransition(pinned.flow, run, at);
|
|
1177
|
-
let next = run;
|
|
1178
|
-
if (ended && transition) {
|
|
1179
|
-
next = applyTransition(pinned.flow, run, transition);
|
|
1180
|
-
await runtime.afterTransitions({ ownerId, chatId, pinned, run: next, transitions: [transition] });
|
|
1181
|
-
}
|
|
1182
|
-
if (ended) await report({ ownerId, chatId, pinned, run: next, reason: "deadline", at });
|
|
1183
|
-
return { run: endedRun(pinned.flow, next, { endedAt: at, endReason: "deadline" }), ended };
|
|
1184
|
-
},
|
|
1185
|
-
async farewell({ ownerId, chatId, pinned, at, timeZone, port, firstMessages, delivery }) {
|
|
1186
|
-
const schedule = pinned.flow.schedule;
|
|
1187
|
-
if (!schedule?.farewellOnDeadline) return;
|
|
1188
|
-
const record = await runtime.store.getChat(chatId);
|
|
1189
|
-
const chat = record ? { dayClosedOn: record.dayClosedOn, pausedUntil: record.pausedUntil, windowClosed: record.windowClosed } : void 0;
|
|
1190
|
-
const allowed = firstMessages ? await firstMessages.check({ ownerId, chatId, at, timeZone, schedule, ...chat && { chat } }) : { allowed: true };
|
|
1191
|
-
if (!allowed.allowed) return;
|
|
1192
|
-
const text = schedule.farewellText?.trim();
|
|
1193
|
-
const result = await port.deliverNotices({
|
|
1194
|
-
ownerId,
|
|
1195
|
-
chatId,
|
|
1196
|
-
notices: [{ kind: "app", name: FLOW_DEADLINE_NOTICE, title: FAREWELL_NOTICE, data: { reasons: [{ kind: "farewell" }] } }],
|
|
1197
|
-
wakeAgent: true,
|
|
1198
|
-
deliveryKey: `flow:${chatId}:deadline`,
|
|
1199
|
-
usageSource: "first_message",
|
|
1200
|
-
maxServerTurnsInRow: false,
|
|
1201
|
-
timeZone,
|
|
1202
|
-
...delivery?.own() && { awaitAnswer: true },
|
|
1203
|
-
...text && { reply: { text } }
|
|
1204
|
-
});
|
|
1205
|
-
if (result.status !== "delivered" || !result.turnStarted) return;
|
|
1206
|
-
if (delivery) await delivery.afterFirstTurn({ ownerId, chatId, answer: result.answer, reasons: [{ kind: "farewell" }], at, timeZone });
|
|
1207
|
-
else await firstMessages?.record({ ownerId, at, timeZone });
|
|
1208
|
-
},
|
|
1209
|
-
async end({ ownerId, chatId, pinned, run, reason, outcome, at }) {
|
|
1210
|
-
const ended = await close(chatId, reason, at);
|
|
1211
|
-
if (ended) await report({ ownerId, chatId, pinned, run, reason, ...outcome !== void 0 && { outcome }, at });
|
|
1212
|
-
return { ended };
|
|
1213
|
-
}
|
|
1214
|
-
};
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
// src/runtime/prepare-turn.ts
|
|
1218
|
-
var FLOW_TIMER_NOTICE = "flow_timer";
|
|
1219
|
-
var FLOW_EVENT_NOTICE = "flow_event";
|
|
1220
|
-
var HUMAN_ACTIONS = /* @__PURE__ */ new Set(["send", "edit", "regenerate", "retry"]);
|
|
1221
|
-
var transitionKey = (transition) => `${transition.from}\0${transition.exitId}`;
|
|
1222
|
-
var errorText3 = (error) => error instanceof Error ? error.message : String(error);
|
|
1223
|
-
var dataOf = (notice) => notice.data && typeof notice.data === "object" ? notice.data : {};
|
|
1224
|
-
var firstReasonsOf = (notices) => notices.flatMap((notice) => {
|
|
1225
|
-
if (notice.kind !== "app" || notice.name !== FLOW_TIMER_NOTICE && notice.name !== FLOW_DEADLINE_NOTICE) return [];
|
|
1226
|
-
const { reasons } = dataOf(notice);
|
|
1227
|
-
return Array.isArray(reasons) ? reasons : [];
|
|
1228
|
-
});
|
|
1229
|
-
function flowPrepareTurn(runtime, appTimeZone, finisher) {
|
|
1230
|
-
return async (ctx) => {
|
|
1231
|
-
if (ctx.audience === "group") return;
|
|
1232
|
-
let pinned = await runtime.chatFlow(ctx.ownerId, ctx.chatId, ctx.chat);
|
|
1233
|
-
if (!pinned) return;
|
|
1234
|
-
const timeOffset = testChatOffset(ctx.chat.data);
|
|
1235
|
-
const now = runtime.now() + (timeOffset ?? 0);
|
|
1236
|
-
const transitions = [];
|
|
1237
|
-
let card;
|
|
1238
|
-
const applied = [];
|
|
1239
|
-
const step = async (run2, event, out = transitions) => advance(pinned.flow, run2, event, {
|
|
1240
|
-
now,
|
|
1241
|
-
card: card ??= await runtime.cardValues(ctx.ownerId),
|
|
1242
|
-
out,
|
|
1243
|
-
onChainLimit: (stopped) => runtime.logError(chainLimitMessage(ctx.chatId, stopped)),
|
|
1244
|
-
timeZone
|
|
1245
|
-
});
|
|
1246
|
-
const record = await runtime.store.getChat(ctx.chatId);
|
|
1247
|
-
const page = pageTimeZone(ctx.timeZone, appTimeZone(), ctx.pageTimeZone);
|
|
1248
|
-
const known = page ?? (runtime.timeZones ? await runtime.timeZones.known(ctx.ownerId, ctx.chatId) : record?.timeZone || null);
|
|
1249
|
-
const timeZone = known ?? runtime.defaultTimeZone ?? ctx.timeZone;
|
|
1250
|
-
let run = currentRun(pinned.flow, ctx.state, now);
|
|
1251
|
-
const previousAnswer = run.answerId;
|
|
1252
|
-
const pending = await runtime.store.takePending(ctx.chatId);
|
|
1253
|
-
const appliedInTime2 = new Set(
|
|
1254
|
-
pending.flatMap((change) => change.kind === "transition" && change.appliedAt !== void 0 && change.branchHead === previousAnswer ? [transitionKey(change.transition)] : [])
|
|
1255
|
-
);
|
|
1256
|
-
let restored = null;
|
|
1257
|
-
const restore = () => restored ??= Promise.all(pending.map((change) => runtime.store.addPending(ctx.chatId, change)));
|
|
1258
|
-
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)}`));
|
|
1259
|
-
if (pending.length > 0) ctx.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1260
|
-
try {
|
|
1261
|
-
return await applyTurn();
|
|
1262
|
-
} catch (error) {
|
|
1263
|
-
await restore();
|
|
1264
|
-
throw error;
|
|
1265
|
-
} finally {
|
|
1266
|
-
ctx.abortSignal.removeEventListener("abort", onAbort);
|
|
1267
|
-
}
|
|
1268
|
-
async function applyTurn() {
|
|
1269
|
-
const schedule = pinned.flow.schedule;
|
|
1270
|
-
if (schedule) run = withDayAnchor(run, timeZone, schedule);
|
|
1271
|
-
if (record?.endedAt) {
|
|
1272
|
-
run = endedRun(pinned.flow, run, { endedAt: Date.parse(record.endedAt), endReason: record.endReason ?? "final" });
|
|
1273
|
-
} else if (schedule && finisher) {
|
|
1274
|
-
const deadline = deadlineAt(run, timeZone, schedule);
|
|
1275
|
-
if (deadline !== null && deadline <= now) {
|
|
1276
|
-
({ run } = await finisher().deadline({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned, run, at: deadline, timeZone }));
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
const ended = run.endedAt !== void 0;
|
|
1280
|
-
for (const change of ended ? [] : pending) {
|
|
1281
|
-
if (change.kind === "migrate") {
|
|
1282
|
-
const next = await runtime.chatFlow(ctx.ownerId, ctx.chatId);
|
|
1283
|
-
if (next) {
|
|
1284
|
-
run = { ...migrateRun(run, next.flow, change.map, now), answerId: previousAnswer };
|
|
1285
|
-
pinned = next;
|
|
1286
|
-
}
|
|
1287
|
-
} else if (change.kind === "transition") {
|
|
1288
|
-
if (change.branchHead !== previousAnswer || run.state !== change.transition.from) continue;
|
|
1289
|
-
const out = [];
|
|
1290
|
-
run = await step(run, pendingEvent(change.transition, timeZone), out);
|
|
1291
|
-
for (const transition of out) (appliedInTime2.has(transitionKey(transition)) ? applied : transitions).push(transition);
|
|
1292
|
-
} else {
|
|
1293
|
-
run = await step(run, { type: "event", event: "app", name: change.name });
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
let touchBase = null;
|
|
1297
|
-
for (const notice of ended ? [] : ctx.notices) {
|
|
1298
|
-
if (notice.kind !== "app") continue;
|
|
1299
|
-
const data = dataOf(notice);
|
|
1300
|
-
if (notice.name === FLOW_TIMER_NOTICE) {
|
|
1301
|
-
if (typeof data.undelivered === "string") run = withoutUndeliveredTouch(run, /* @__PURE__ */ new Set([data.undelivered])).run;
|
|
1302
|
-
if (typeof data.exitId === "string") run = await step(run, { type: "timer", exitId: data.exitId, timeZone });
|
|
1303
|
-
touchBase = { lastAgentAt: run.lastAgentAt, nudgeStep: run.nudgeStep ?? 0, ...run.nudgeStepOwn && { nudgeStepOwn: true }, agentAnswers: run.agentAnswers };
|
|
1304
|
-
if (typeof data.nudgeStep === "number") run = withNudgeStep(run, data.nudgeStep, data.nudgeStepOwn === true);
|
|
1305
|
-
} else if (notice.name === FLOW_EVENT_NOTICE && typeof data.name === "string") {
|
|
1306
|
-
run = await step(run, { type: "event", event: "app", name: data.name });
|
|
1307
|
-
}
|
|
1308
|
-
}
|
|
1309
|
-
if (!ctx.resumed && HUMAN_ACTIONS.has(ctx.action)) {
|
|
1310
|
-
if (record?.windowClosed) await runtime.store.setWindowClosed(ctx.chatId, false);
|
|
1311
|
-
run = noteHumanMessage(run, now, schedule ? dayInProgress(now, timeZone, schedule) : void 0, ctx.burstMessageIds?.length ?? 1);
|
|
1312
|
-
if (!ended) run = await step(run, { type: "event", event: "message" });
|
|
1313
|
-
}
|
|
1314
|
-
if (ctx.action === "notice" && !ctx.resumed) run = { ...run, serverTurns: (run.serverTurns ?? 0) + 1 };
|
|
1315
|
-
if (ctx.action !== "approval" && ctx.action !== "widget") run = noteAgentAnswer(run, now);
|
|
1316
|
-
const { touch: earlierTouch, firstReasons: earlierReasons, ...withoutTouch } = run;
|
|
1317
|
-
const continued = ctx.action === "approval" || ctx.action === "widget";
|
|
1318
|
-
const touch = touchBase ? { answerId: ctx.messageId, at: now, ...touchBase } : continued ? earlierTouch : void 0;
|
|
1319
|
-
const reasons = firstReasonsOf(ctx.notices);
|
|
1320
|
-
const firstReasons = reasons.length > 0 ? reasons : continued ? earlierReasons : void 0;
|
|
1321
|
-
run = { ...withoutTouch, answerId: ctx.messageId, ...timeOffset !== null && { timeOffset }, ...touch && { touch }, ...firstReasons && { firstReasons } };
|
|
1322
|
-
if (ctx.abortSignal.aborted) {
|
|
1323
|
-
await restore();
|
|
1324
|
-
return void 0;
|
|
1325
|
-
}
|
|
1326
|
-
if (known && runtime.timeZones) await runtime.timeZones.write(ctx.ownerId, ctx.chatId, known);
|
|
1327
|
-
else if (record && page && record.timeZone !== page) await runtime.store.updateChat(ctx.chatId, { timeZone: page }, new Date(runtime.now()).toISOString());
|
|
1328
|
-
await runtime.afterTransitions({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned, run, transitions });
|
|
1329
|
-
return {
|
|
1330
|
-
set: { [FLOW_STATE_KEY]: run },
|
|
1331
|
-
...applied.length + transitions.length > 0 && { notices: [transitionNotice(pinned.flow, [...applied, ...transitions])] }
|
|
1332
|
-
};
|
|
1333
|
-
}
|
|
1334
|
-
};
|
|
1335
|
-
}
|
|
1336
|
-
|
|
1337
|
-
// src/runtime/first-reasons-notice.ts
|
|
1338
|
-
var REASON_LINES = {
|
|
1339
|
-
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.",
|
|
1340
|
-
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}).`,
|
|
1341
|
-
daily: (at) => `\u041A\u0430\u0441\u0430\u043D\u0438\u0435 \u043F\u043E \u0440\u0430\u0441\u043F\u0438\u0441\u0430\u043D\u0438\u044E, ${at}.`,
|
|
1342
|
-
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.`
|
|
1343
|
-
};
|
|
1344
|
-
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.";
|
|
1345
|
-
var transitionLine = (flow, exitId) => {
|
|
1346
|
-
const exit = exitById(flow, exitId);
|
|
1347
|
-
return `\u0421\u0440\u0430\u0431\u043E\u0442\u0430\u043B\u043E: ${exit ? describeCondition(flow, exit.when) : exitId}`;
|
|
1348
|
-
};
|
|
1349
|
-
function reasonLine(reason) {
|
|
1350
|
-
switch (reason.kind) {
|
|
1351
|
-
case "morning":
|
|
1352
|
-
return REASON_LINES.morning;
|
|
1353
|
-
case "silence":
|
|
1354
|
-
return REASON_LINES.silence(reason.step);
|
|
1355
|
-
case "daily":
|
|
1356
|
-
return REASON_LINES.daily(reason.at);
|
|
1357
|
-
case "night_messages":
|
|
1358
|
-
return REASON_LINES.nightMessages(reason.count);
|
|
1359
|
-
default:
|
|
1360
|
-
return null;
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
function firstReasonsNotice(flow, reasons, exitId, ownNudge = false) {
|
|
1364
|
-
const silence = reasons.find((reason) => reason.kind === "silence");
|
|
1365
|
-
const moved = exitId !== null && reasons.some((reason) => reason.kind === "transition");
|
|
1366
|
-
const morning = reasons.some((reason) => reason.kind === "morning");
|
|
1367
|
-
const nudgeStep = moved || morning ? 0 : silence?.kind === "silence" ? silence.step : void 0;
|
|
1368
|
-
const own = ownNudge && silence !== void 0 && !moved && !morning;
|
|
1369
|
-
const data = { ...moved && { exitId }, reasons: [...reasons], ...nudgeStep !== void 0 && { nudgeStep }, ...own && { nudgeStepOwn: true } };
|
|
1370
|
-
const others = reasons.flatMap((reason) => reasonLine(reason) ?? []);
|
|
1371
|
-
if (others.length === 0 && moved) return { kind: "app", name: FLOW_TIMER_NOTICE, title: transitionLine(flow, exitId), data };
|
|
1372
|
-
const lines = [...moved ? [`${transitionLine(flow, exitId)}.`] : [], ...others, TASK];
|
|
1373
|
-
return { kind: "app", name: FLOW_TIMER_NOTICE, title: lines.join("\n"), data };
|
|
1374
|
-
}
|
|
1375
|
-
|
|
1376
807
|
// src/runtime/flow-delivery.ts
|
|
1377
808
|
import { answerText, ChatNotFoundError, deliverableParts } from "@ykurilov/agent-chat-core";
|
|
1378
809
|
var FLOW_DELIVERY_EXIT = "@delivery";
|
|
1379
810
|
var RETRY_DELAYS_MS = [6e4, 5 * 6e4, 30 * 6e4];
|
|
1380
|
-
var
|
|
811
|
+
var errorText3 = (error) => error instanceof Error ? error.message : String(error);
|
|
1381
812
|
var isoOf = (at) => new Date(at).toISOString();
|
|
1382
813
|
var hasContent = (answer) => answer.text.trim() !== "" || answer.parts.some((part) => part.type !== "text");
|
|
1383
814
|
function createFlowDelivery(input) {
|
|
@@ -1392,7 +823,7 @@ function createFlowDelivery(input) {
|
|
|
1392
823
|
await (await portOf({ ownerId, chatId })).markUndelivered({ ownerId, chatId, messageId });
|
|
1393
824
|
} catch (error) {
|
|
1394
825
|
if (error instanceof ChatNotFoundError) return;
|
|
1395
|
-
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: ${
|
|
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)}`);
|
|
1396
827
|
}
|
|
1397
828
|
}
|
|
1398
829
|
async function blocked(ownerId, chatId) {
|
|
@@ -1444,7 +875,7 @@ function createFlowDelivery(input) {
|
|
|
1444
875
|
try {
|
|
1445
876
|
return await deliver(message);
|
|
1446
877
|
} catch (error) {
|
|
1447
|
-
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: ${
|
|
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)}`);
|
|
1448
879
|
return { status: "retry" };
|
|
1449
880
|
}
|
|
1450
881
|
}
|
|
@@ -1520,7 +951,7 @@ var TIMER_BATCH = 50;
|
|
|
1520
951
|
var TIMER_PARALLEL = 5;
|
|
1521
952
|
var DAY_MS = 864e5;
|
|
1522
953
|
var DEFAULT_FLOW_SERVER_TURNS_IN_ROW = 50;
|
|
1523
|
-
var
|
|
954
|
+
var errorText4 = (error) => error instanceof Error ? error.message : String(error);
|
|
1524
955
|
var isoOf2 = (at) => new Date(at).toISOString();
|
|
1525
956
|
var timerKey = (chatId, exitId, run) => `flow:${chatId}:${exitId}:${run.enteredAt}`;
|
|
1526
957
|
async function historyFacts(port, ownerId, chatId, answerId) {
|
|
@@ -1727,7 +1158,7 @@ function createFlowTimers(input) {
|
|
|
1727
1158
|
await defer(flow.schedule ? nextDayStartAfter(at, timeZone, flow.schedule) : at + DAY_MS);
|
|
1728
1159
|
return;
|
|
1729
1160
|
}
|
|
1730
|
-
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));
|
|
1731
1162
|
const notice = undelivered ? { ...reasonsNotice, data: { ...reasonsNotice.data, undelivered } } : reasonsNotice;
|
|
1732
1163
|
const reply = moved ? exactFirstText(flow, next.state) : null;
|
|
1733
1164
|
const delivery = input.delivery?.();
|
|
@@ -1772,7 +1203,7 @@ function createFlowTimers(input) {
|
|
|
1772
1203
|
try {
|
|
1773
1204
|
if (!await renew(record.chatId)) return;
|
|
1774
1205
|
const heartbeat = setInterval(() => {
|
|
1775
|
-
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: ${
|
|
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)}`));
|
|
1776
1207
|
}, leaseMs / 3);
|
|
1777
1208
|
heartbeat.unref?.();
|
|
1778
1209
|
try {
|
|
@@ -1781,7 +1212,7 @@ function createFlowTimers(input) {
|
|
|
1781
1212
|
clearInterval(heartbeat);
|
|
1782
1213
|
}
|
|
1783
1214
|
} catch (error) {
|
|
1784
|
-
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: ${
|
|
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)}`);
|
|
1785
1216
|
}
|
|
1786
1217
|
}
|
|
1787
1218
|
async function tickOnce() {
|
|
@@ -1803,7 +1234,7 @@ function createFlowTimers(input) {
|
|
|
1803
1234
|
start() {
|
|
1804
1235
|
if (interval) return;
|
|
1805
1236
|
interval = setInterval(() => {
|
|
1806
|
-
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: ${
|
|
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)}`));
|
|
1807
1238
|
}, tickMs);
|
|
1808
1239
|
interval.unref?.();
|
|
1809
1240
|
},
|
|
@@ -2425,7 +1856,7 @@ var DELIVERY_RETRY_MS = [50, 250, 1e3, 3e3];
|
|
|
2425
1856
|
var KNOWN_FLOWS_LIMIT = 1e4;
|
|
2426
1857
|
var KNOWN_PORTS_LIMIT = 1e4;
|
|
2427
1858
|
var SERVER_UNKNOWN_CODES = /* @__PURE__ */ new Set(["unknown_event"]);
|
|
2428
|
-
var
|
|
1859
|
+
var errorText5 = (error) => error instanceof Error ? error.message : String(error);
|
|
2429
1860
|
var pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
2430
1861
|
function stableJson(value) {
|
|
2431
1862
|
if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
|
|
@@ -2500,7 +1931,7 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2500
1931
|
try {
|
|
2501
1932
|
return make();
|
|
2502
1933
|
} catch (error) {
|
|
2503
|
-
logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] ${name} \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${
|
|
1934
|
+
logError(`[\u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439] ${name} \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D: ${errorText5(error)}`);
|
|
2504
1935
|
return null;
|
|
2505
1936
|
}
|
|
2506
1937
|
});
|
|
@@ -2565,7 +1996,7 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2565
1996
|
);
|
|
2566
1997
|
const background = /* @__PURE__ */ new Set();
|
|
2567
1998
|
function track(work) {
|
|
2568
|
-
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: ${
|
|
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));
|
|
2569
2000
|
background.add(tracked);
|
|
2570
2001
|
}
|
|
2571
2002
|
const known = /* @__PURE__ */ new Map();
|
|
@@ -2737,7 +2168,7 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2737
2168
|
return Object.fromEntries(entries.map((entry) => [entry.fieldId, entry.value]));
|
|
2738
2169
|
} catch (error) {
|
|
2739
2170
|
if (!app) throw error;
|
|
2740
|
-
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: ${
|
|
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)}`);
|
|
2741
2172
|
return {};
|
|
2742
2173
|
}
|
|
2743
2174
|
},
|
|
@@ -2746,7 +2177,7 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2746
2177
|
try {
|
|
2747
2178
|
await store.updateChat(chatId, { stateId: run.state }, iso());
|
|
2748
2179
|
} catch (error) {
|
|
2749
|
-
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: ${
|
|
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)}`);
|
|
2750
2181
|
}
|
|
2751
2182
|
const { schedule } = pinned.flow;
|
|
2752
2183
|
const timeZone = schedule ? await timeZones.of(ownerId, chatId) : null;
|
|
@@ -2779,7 +2210,7 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2779
2210
|
try {
|
|
2780
2211
|
await optionalTimers()?.reschedule({ ownerId, chatId, run, branchHead, ...failedAt !== void 0 && { failedAt } });
|
|
2781
2212
|
} catch (error) {
|
|
2782
|
-
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: ${
|
|
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)}`);
|
|
2783
2214
|
}
|
|
2784
2215
|
}
|
|
2785
2216
|
async function rescheduleUndelivered({ ownerId, chatId, at }) {
|
|
@@ -2803,7 +2234,7 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2803
2234
|
if (!run) return;
|
|
2804
2235
|
await reschedule(record.ownerId, record.chatId, migrateRun(run, flow, map, chatTime(run, now())), run.answerId ?? null);
|
|
2805
2236
|
} catch (error) {
|
|
2806
|
-
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: ${
|
|
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)}`);
|
|
2807
2238
|
}
|
|
2808
2239
|
}
|
|
2809
2240
|
async function deliverFirstText(ownerId, chatId, messageId, condition, text) {
|
|
@@ -2925,7 +2356,7 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2925
2356
|
await store.updateChat(chatId, { placement: null }, iso());
|
|
2926
2357
|
} catch (error) {
|
|
2927
2358
|
placedNow.add(chatId);
|
|
2928
|
-
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: ${
|
|
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)}`);
|
|
2929
2360
|
}
|
|
2930
2361
|
}
|
|
2931
2362
|
async function readRun(ownerId, chatId) {
|
|
@@ -2959,6 +2390,8 @@ function buildAgentFlow(options, modules = {}) {
|
|
|
2959
2390
|
...chat,
|
|
2960
2391
|
// Место в сценарии — служебное: посетитель его не видит, редактор и тестовый чат читают `readRun`.
|
|
2961
2392
|
privateStateKeys: [.../* @__PURE__ */ new Set([...chat.privateStateKeys ?? [], FLOW_STATE_KEY])],
|
|
2393
|
+
// Строки «Агент написал первым» на экране (право `appNotices`): только проекция, сырые данные пометок не уходят.
|
|
2394
|
+
appNoticeViews: { ...chat.appNoticeViews, ...FLOW_APP_NOTICE_VIEWS },
|
|
2962
2395
|
beforeTurn: async (input) => {
|
|
2963
2396
|
const own = await chat.beforeTurn?.(input);
|
|
2964
2397
|
return own ?? gate(input);
|
|
@@ -3027,7 +2460,7 @@ ${rules}`;
|
|
|
3027
2460
|
try {
|
|
3028
2461
|
await store.updateChat(input.chatId, { stateId: run.state }, iso());
|
|
3029
2462
|
} catch (error) {
|
|
3030
|
-
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: ${
|
|
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)}`);
|
|
3031
2464
|
}
|
|
3032
2465
|
await reschedule(input.ownerId, input.chatId, run, input.atMessageId);
|
|
3033
2466
|
})();
|