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