@ykurilov/agent-chat-flows 0.63.2 → 0.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  sideTaskFieldKnown,
15
15
  sideTaskRecord,
16
16
  sideTaskStanding
17
- } from "./chunk-H3AN5YUN.js";
17
+ } from "./chunk-3K72EVAT.js";
18
18
  import {
19
19
  FACT_PREFIX,
20
20
  MAX_FACTS,
@@ -26,7 +26,7 @@ import {
26
26
  fieldValueText,
27
27
  isFactEntry,
28
28
  sameFact
29
- } from "./chunk-KGWXTWBZ.js";
29
+ } from "./chunk-22FWN7NU.js";
30
30
  import {
31
31
  AGENT_FLOW_FORMAT,
32
32
  AGENT_FLOW_FORMAT_VERSION,
@@ -92,6 +92,7 @@ import {
92
92
  noteSideQuestion,
93
93
  overdueOnEntry,
94
94
  parseBooleanAnswer,
95
+ parseDuration,
95
96
  parseFlow,
96
97
  placeOfPath,
97
98
  returnTarget,
@@ -105,7 +106,7 @@ import {
105
106
  withDayAnchor,
106
107
  withNudgeStep,
107
108
  withoutUndeliveredTouch
108
- } from "./chunk-LT4QELAZ.js";
109
+ } from "./chunk-5RDY7QBA.js";
109
110
  import {
110
111
  appliedInTime,
111
112
  byVersionAndState,
@@ -1020,6 +1021,15 @@ function testChatOffset(data) {
1020
1021
  return typeof offset === "number" && Number.isFinite(offset) ? offset : 0;
1021
1022
  }
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;
1023
1033
  var TURN_WAIT_LIMIT = 5;
1024
1034
  async function placeRun(runtime, port, chat, flow, now) {
1025
1035
  const { values } = await port.getChatState(chat.ownerId, chat.chatId);
@@ -1049,7 +1059,8 @@ async function advanceTestClock(input) {
1049
1059
  const { runtime, port, timers, ownerId, chatId, clock } = input;
1050
1060
  const offset = testChatOffset(await port.getChatData(ownerId, chatId));
1051
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`);
1052
- const ms = clock.to === "next_day_start" ? await msToNextDay(runtime, port, ownerId, chatId, offset) : clock.ms;
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;
1053
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");
1054
1065
  const timeOffset = offset + ms;
1055
1066
  await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });
@@ -1062,6 +1073,37 @@ async function advanceTestClock(input) {
1062
1073
  await turnFinished(port, ownerId, chatId);
1063
1074
  return { timeOffset };
1064
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
+ }
1065
1107
 
1066
1108
  // src/runtime/flow-finish.ts
1067
1109
  var FLOW_DEADLINE_NOTICE = "flow_deadline";
@@ -1501,6 +1543,10 @@ function createFlowTimers(input) {
1501
1543
  const owner = `flow-timers:${randomUUID()}`;
1502
1544
  let interval;
1503
1545
  let running = null;
1546
+ const watchers = /* @__PURE__ */ new Map();
1547
+ const fired = (chatId, reason) => {
1548
+ for (const onFired of watchers.get(chatId) ?? []) onFired(reason);
1549
+ };
1504
1550
  const dueInput = (due, branchHead, offset) => ({ at: isoOf2(due.at - offset), exitId: due.exitId, branchHead });
1505
1551
  async function placeOf2(record, chatPort, run, failedAt) {
1506
1552
  const pausedUntil = record.pausedUntil === null ? null : Date.parse(record.pausedUntil);
@@ -1625,11 +1671,14 @@ function createFlowTimers(input) {
1625
1671
  await store.setDue(chatId, null);
1626
1672
  const endAt = flow.schedule ? deadlineAt(run, timeZone, flow.schedule) ?? wake.at : wake.at;
1627
1673
  await input.onDeadline?.({ record, flow, run, at: endAt, timeZone });
1674
+ const deadlineState = flow.schedule?.deadlineState;
1675
+ fired(chatId, { kind: "deadline", stateId: deadlineState && stateById(flow, deadlineState) ? deadlineState : run.state });
1628
1676
  return;
1629
1677
  }
1630
1678
  if (due.giveUp) {
1631
1679
  await store.setDue(chatId, null);
1632
1680
  await input.onGiveUp?.({ record, flow, run, at });
1681
+ fired(chatId, { kind: "give_up", stateId: run.state });
1633
1682
  return;
1634
1683
  }
1635
1684
  const transitions = [];
@@ -1710,6 +1759,7 @@ function createFlowTimers(input) {
1710
1759
  const answered = nudgeStep === void 0 ? noteAgentAnswer(next, at) : withNudgeStep(noteAgentAnswer(next, at), nudgeStep, nudgeStepOwn === true);
1711
1760
  const following = nextWake(flow, answered, at, timeZone, { pausedUntil: context.pausedUntil ?? null });
1712
1761
  await store.setDue(chatId, following ? dueInput(following, result.messageId ?? null, offset) : null, { unlessDelivery: true });
1762
+ fired(chatId, { kind: reasons[0].kind, stateId: next.state, ...moved && { exitId: due.exitId }, reasons });
1713
1763
  if (!delivery) {
1714
1764
  await firstMessages?.record({ ownerId, at, timeZone });
1715
1765
  return;
@@ -1764,7 +1814,15 @@ function createFlowTimers(input) {
1764
1814
  });
1765
1815
  },
1766
1816
  tick,
1767
- reschedule
1817
+ reschedule,
1818
+ watch(chatId, onFired) {
1819
+ const listeners = watchers.get(chatId) ?? /* @__PURE__ */ new Set();
1820
+ watchers.set(chatId, listeners.add(onFired));
1821
+ return () => {
1822
+ listeners.delete(onFired);
1823
+ if (listeners.size === 0) watchers.delete(chatId);
1824
+ };
1825
+ }
1768
1826
  };
1769
1827
  }
1770
1828
 
@@ -3289,6 +3347,7 @@ export {
3289
3347
  FLOW_TIMER_SPANS,
3290
3348
  FLOW_TIMER_UNITS,
3291
3349
  FLOW_TIME_ZONE_TOOL,
3350
+ FlowTestClockError,
3292
3351
  RETURN_TARGET,
3293
3352
  activeExits,
3294
3353
  agentFlowSchema,
@@ -3333,6 +3392,7 @@ export {
3333
3392
  noteRejectedClaim,
3334
3393
  noteSideQuestion,
3335
3394
  openSideTasks,
3395
+ parseDuration,
3336
3396
  parseFlow,
3337
3397
  placeOfPath,
3338
3398
  resolveFirstMessages,