@ykurilov/agent-chat-flows 0.0.0-stage → 0.55.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.
Files changed (151) hide show
  1. package/CHANGELOG.md +252 -0
  2. package/LICENSE +21 -0
  3. package/README.md +124 -2
  4. package/agent-flow.schema.json +1020 -0
  5. package/dist/chunk-3PTJI2XY.js +100 -0
  6. package/dist/chunk-3PTJI2XY.js.map +1 -0
  7. package/dist/chunk-BNMQUNSI.js +9 -0
  8. package/dist/chunk-BNMQUNSI.js.map +1 -0
  9. package/dist/chunk-BOV6FJGU.js +1288 -0
  10. package/dist/chunk-BOV6FJGU.js.map +1 -0
  11. package/dist/chunk-CKY5KG7N.js +165 -0
  12. package/dist/chunk-CKY5KG7N.js.map +1 -0
  13. package/dist/chunk-DCTRMCGV.js +34 -0
  14. package/dist/chunk-DCTRMCGV.js.map +1 -0
  15. package/dist/format.js +147 -0
  16. package/dist/format.js.map +1 -0
  17. package/dist/index.js +3197 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/postgres.js +387 -0
  20. package/dist/postgres.js.map +1 -0
  21. package/dist/react.js +2650 -0
  22. package/dist/react.js.map +1 -0
  23. package/dist/sqlite.js +319 -0
  24. package/dist/sqlite.js.map +1 -0
  25. package/dist/types/check/app-checks.d.ts +16 -0
  26. package/dist/types/check/check-flow.d.ts +8 -0
  27. package/dist/types/check/index.d.ts +2 -0
  28. package/dist/types/engine/data-conditions.d.ts +10 -0
  29. package/dist/types/engine/day-frame.d.ts +17 -0
  30. package/dist/types/engine/first-messages.d.ts +21 -0
  31. package/dist/types/engine/flow-days.d.ts +39 -0
  32. package/dist/types/engine/flow-graph.d.ts +26 -0
  33. package/dist/types/engine/flow-run.d.ts +130 -0
  34. package/dist/types/engine/index.d.ts +14 -0
  35. package/dist/types/engine/migrate-run.d.ts +11 -0
  36. package/dist/types/engine/next-transition.d.ts +40 -0
  37. package/dist/types/engine/nudges.d.ts +78 -0
  38. package/dist/types/engine/run-updates.d.ts +37 -0
  39. package/dist/types/engine/side-questions.d.ts +26 -0
  40. package/dist/types/engine/side-tasks.d.ts +44 -0
  41. package/dist/types/engine/targets.d.ts +9 -0
  42. package/dist/types/engine/timers.d.ts +61 -0
  43. package/dist/types/engine/zoned-time.d.ts +27 -0
  44. package/dist/types/format/catalog-events.d.ts +5 -0
  45. package/dist/types/format/compact-flow.d.ts +8 -0
  46. package/dist/types/format/flow-format.d.ts +286 -0
  47. package/dist/types/format/flow-json-schema.d.ts +2 -0
  48. package/dist/types/format/flow-schema.d.ts +605 -0
  49. package/dist/types/format/index.d.ts +7 -0
  50. package/dist/types/format/migrate-flow.d.ts +4 -0
  51. package/dist/types/format/parse-flow.d.ts +6 -0
  52. package/dist/types/format.d.ts +3 -0
  53. package/dist/types/index.d.ts +4 -0
  54. package/dist/types/labels/client-card.d.ts +20 -0
  55. package/dist/types/labels/editor.d.ts +261 -0
  56. package/dist/types/labels/index.d.ts +14 -0
  57. package/dist/types/labels/migrate.d.ts +19 -0
  58. package/dist/types/labels/test-chat.d.ts +28 -0
  59. package/dist/types/not-yet.d.ts +2 -0
  60. package/dist/types/postgres.d.ts +10 -0
  61. package/dist/types/prompt/client-card-block.d.ts +16 -0
  62. package/dist/types/prompt/flow-rules.d.ts +9 -0
  63. package/dist/types/prompt/rules/after-end.d.ts +3 -0
  64. package/dist/types/prompt/rules/client-facts.d.ts +2 -0
  65. package/dist/types/prompt/rules/day-frame.d.ts +2 -0
  66. package/dist/types/prompt/rules/link-views.d.ts +4 -0
  67. package/dist/types/prompt/rules/rule-part.d.ts +3 -0
  68. package/dist/types/prompt/rules/side-tasks.d.ts +2 -0
  69. package/dist/types/prompt/rules/time-zone.d.ts +2 -0
  70. package/dist/types/prompt/state-card.d.ts +14 -0
  71. package/dist/types/react/client-card/client-card-panel.d.ts +8 -0
  72. package/dist/types/react/client-card/index.d.ts +2 -0
  73. package/dist/types/react/client-card/use-client-card.d.ts +19 -0
  74. package/dist/types/react/client-card-skin.d.ts +27 -0
  75. package/dist/types/react/editor/board-space.d.ts +51 -0
  76. package/dist/types/react/editor/board-styles.d.ts +1 -0
  77. package/dist/types/react/editor/editor-model.d.ts +60 -0
  78. package/dist/types/react/editor/exit-text.d.ts +11 -0
  79. package/dist/types/react/editor/flow-board.d.ts +11 -0
  80. package/dist/types/react/editor/flow-editor.d.ts +13 -0
  81. package/dist/types/react/editor/flow-simulation.d.ts +44 -0
  82. package/dist/types/react/editor/index.d.ts +6 -0
  83. package/dist/types/react/editor/schedule-model.d.ts +47 -0
  84. package/dist/types/react/editor/side-task-model.d.ts +18 -0
  85. package/dist/types/react/editor/use-flow-editor.d.ts +120 -0
  86. package/dist/types/react/flow-editor-skin.d.ts +453 -0
  87. package/dist/types/react/flow-labels.d.ts +8 -0
  88. package/dist/types/react/index.d.ts +6 -0
  89. package/dist/types/react/test-chat/flow-test-chat.d.ts +22 -0
  90. package/dist/types/react/test-chat/index.d.ts +2 -0
  91. package/dist/types/react/test-chat/use-flow-test-chat.d.ts +50 -0
  92. package/dist/types/react.d.ts +3 -0
  93. package/dist/types/runtime/admit.d.ts +15 -0
  94. package/dist/types/runtime/agent-flow.d.ts +20 -0
  95. package/dist/types/runtime/client-card.d.ts +9 -0
  96. package/dist/types/runtime/client-facts.d.ts +22 -0
  97. package/dist/types/runtime/close-day-tool.d.ts +3 -0
  98. package/dist/types/runtime/decisions-client.d.ts +33 -0
  99. package/dist/types/runtime/extra-tool.d.ts +16 -0
  100. package/dist/types/runtime/first-message-retry.d.ts +4 -0
  101. package/dist/types/runtime/first-message-rules.d.ts +55 -0
  102. package/dist/types/runtime/first-reasons-notice.d.ts +9 -0
  103. package/dist/types/runtime/flow-delivery.d.ts +53 -0
  104. package/dist/types/runtime/flow-events.d.ts +126 -0
  105. package/dist/types/runtime/flow-finish.d.ts +68 -0
  106. package/dist/types/runtime/flow-known-texts.d.ts +11 -0
  107. package/dist/types/runtime/flow-signals.d.ts +12 -0
  108. package/dist/types/runtime/flow-timers.d.ts +55 -0
  109. package/dist/types/runtime/flow-tools.d.ts +23 -0
  110. package/dist/types/runtime/flow-types.d.ts +547 -0
  111. package/dist/types/runtime/held-messages.d.ts +25 -0
  112. package/dist/types/runtime/index.d.ts +11 -0
  113. package/dist/types/runtime/judge-context.d.ts +36 -0
  114. package/dist/types/runtime/judge.d.ts +21 -0
  115. package/dist/types/runtime/migrate-chats.d.ts +20 -0
  116. package/dist/types/runtime/note-tools.d.ts +15 -0
  117. package/dist/types/runtime/pause.d.ts +21 -0
  118. package/dist/types/runtime/prepare-turn.d.ts +12 -0
  119. package/dist/types/runtime/side-task-tool.d.ts +5 -0
  120. package/dist/types/runtime/step-tail.d.ts +11 -0
  121. package/dist/types/runtime/tail/day-line.d.ts +2 -0
  122. package/dist/types/runtime/tail/frame-line.d.ts +2 -0
  123. package/dist/types/runtime/tail/side-tasks-line.d.ts +2 -0
  124. package/dist/types/runtime/tail/tail-part.d.ts +21 -0
  125. package/dist/types/runtime/test-clock.d.ts +26 -0
  126. package/dist/types/runtime/time-zone-tool.d.ts +3 -0
  127. package/dist/types/runtime/turn-gate.d.ts +8 -0
  128. package/dist/types/sqlite.d.ts +9 -0
  129. package/dist/types/store/flow-store-shared.d.ts +20 -0
  130. package/dist/types/store/flow-store.d.ts +240 -0
  131. package/dist/types/store/memory.d.ts +3 -0
  132. package/dist/types/store/migration-settings.d.ts +7 -0
  133. package/dist/types/store/postgres-schema.d.ts +775 -0
  134. package/dist/types/store/postgres.d.ts +15 -0
  135. package/dist/types/store/sqlite-schema.d.ts +824 -0
  136. package/dist/types/store/sqlite.d.ts +9 -0
  137. package/drizzle/postgres/0000_init.sql +45 -0
  138. package/drizzle/postgres/0001_days_pause_side_tasks.sql +21 -0
  139. package/drizzle/postgres/0002_chat_placement.sql +1 -0
  140. package/drizzle/postgres/meta/0000_snapshot.json +317 -0
  141. package/drizzle/postgres/meta/0001_snapshot.json +439 -0
  142. package/drizzle/postgres/meta/0002_snapshot.json +445 -0
  143. package/drizzle/postgres/meta/_journal.json +27 -0
  144. package/drizzle/sqlite/0000_init.sql +44 -0
  145. package/drizzle/sqlite/0001_days_pause_side_tasks.sql +21 -0
  146. package/drizzle/sqlite/0002_chat_placement.sql +1 -0
  147. package/drizzle/sqlite/meta/0000_snapshot.json +297 -0
  148. package/drizzle/sqlite/meta/0001_snapshot.json +427 -0
  149. package/drizzle/sqlite/meta/0002_snapshot.json +434 -0
  150. package/drizzle/sqlite/meta/_journal.json +27 -0
  151. package/package.json +98 -3
@@ -0,0 +1,547 @@
1
+ import type { AfterTurnHandler, AgentNotice, AgentSettings, AgentToolsModel, AskModel, BeforeTurnHandler, Chat, ChatInstructions, ChatService, ModelChoice, RecordUsage, ReplyChecksSetting, StateChangedHandler, StepContextInput, StoredMessage } from "@ykurilov/agent-chat-core";
2
+ import type { ToolSet } from "ai";
3
+ import type { AgentFlow, FlowAfterEnd, FlowSchedule } from "../format/flow-format.js";
4
+ import type { FirstMessagesSetting, FlowEndReason, FlowRun, Transition } from "../engine/flow-run.js";
5
+ import type { StateMap } from "../engine/migrate-run.js";
6
+ import type { FlowDayInfo } from "../engine/flow-days.js";
7
+ import type { FlowFirstReason } from "../engine/nudges.js";
8
+ import type { ChatsInState, ClientCardEntry, ClientFact, FlowChatRecord, FlowStore } from "../store/flow-store.js";
9
+ export type { FlowDayInfo } from "../engine/flow-days.js";
10
+ export type { FlowFirstReason } from "../engine/nudges.js";
11
+ /** Сценарий, по которому идёт чат: приложение хранит графы у себя и отдаёт нужную версию. */
12
+ export interface LoadedFlow {
13
+ flowId: string;
14
+ /** Номер версии приложения; чат закрепляется за ней до перевода. */
15
+ versionId: string;
16
+ /** JSON сценария (как сохранил редактор) или уже разобранный. */
17
+ flow: AgentFlow | unknown;
18
+ /** Закрепить чат сразу в этом состоянии (перенос из старой системы). Нет — `start` сценария. */
19
+ stateId?: string;
20
+ /** Когда начался разговор: от него дни и срок сценария. Нет — сейчас. */
21
+ startedAt?: Date;
22
+ /** Когда чат вошёл в `stateId`: от него таймеры ноды. Нет — `startedAt`. */
23
+ enteredAt?: Date;
24
+ /** Последнее сообщение человека: от него «не завершил» (`giveUpAfter`) и «человек ответил». */
25
+ lastHumanAt?: Date;
26
+ /** Последнее сообщение агента — ответ или касание: от него тишина, касания и утреннее. */
27
+ lastAgentAt?: Date;
28
+ /** Какой шаг ряда касаний `afterSilence` следующий, с 0: сколько касаний уже было после последней реплики человека. Нет — 0. */
29
+ nudgeStep?: number;
30
+ /** Счёт за сегодня по часам человека. */
31
+ today?: FlowTodayCounts;
32
+ /** Пояс человека (IANA, `Europe/Moscow`), если старая система его знает: по нему ночь, «сегодня» и потолок дня до первого хода. */
33
+ timeZone?: string;
34
+ }
35
+ /** Что уже было сегодня в старой системе: продолжает дневной потолок и рамки дня. */
36
+ export interface FlowTodayCounts {
37
+ /** Сколько сообщений агент сегодня написал первым: идут в потолок `maxPerDay`. */
38
+ firstMessages?: number;
39
+ /** Сколько сообщений человек написал за день сценария: идут в рамки дня (`dayFrame`). Только с «Днями и временем». */
40
+ humanMessages?: number;
41
+ /** День закрыт («на сегодня всё»): сегодня агент первым не пишет. Только с «Днями и временем». */
42
+ dayClosed?: boolean;
43
+ }
44
+ /**
45
+ * Что видит `agent.stepContext` приложения со сценарием: в ходе «пишет первым» — все его причины (касание, утро,
46
+ * переход по таймеру, прощание). Ход на сообщение человека — `firstReasons` нет. Хвост не кэшируется: постоянная
47
+ * инструкция от причины не зависит.
48
+ */
49
+ export type FlowStepContextInput = StepContextInput & {
50
+ firstReasons?: FlowFirstReason[];
51
+ };
52
+ /** Закончить разговор по решению приложения: человека перевели на другой продукт, отдали менеджеру и т. п. */
53
+ export interface FlowEndInput {
54
+ ownerId: string;
55
+ chatId: string;
56
+ /** Своя причина («switched»): придёт в `onFinish.reason`, `admit` и `readRun`. */
57
+ reason: string;
58
+ /** Итог словами приложения: придёт в `onFinish.outcome`. */
59
+ outcome?: string;
60
+ }
61
+ /** Перенести идущий чат сразу, без хода: сценарий и место — как у `loadFlow`. */
62
+ export type FlowAdoptInput = LoadedFlow & {
63
+ ownerId: string;
64
+ chatId: string;
65
+ };
66
+ /** Переход с метками: приложение по ним пишет письмо, зовёт оператора, считает аналитику. */
67
+ export interface FlowTransitionEvent extends Transition {
68
+ ownerId: string;
69
+ chatId: string;
70
+ flowId: string;
71
+ versionId: string;
72
+ /** День сценария в момент перехода; без «Дней и времени» — нет поля. */
73
+ day?: FlowDayInfo;
74
+ }
75
+ /** Место чата для редактора и приложения: плюс день сценария, если у сценария есть «Дни и время». */
76
+ export type FlowRunView = FlowRun & {
77
+ day?: FlowDayInfo;
78
+ };
79
+ /** Реплика разговора для судьи: только текст. */
80
+ export interface JudgeMessage {
81
+ role: "human" | "agent";
82
+ text: string;
83
+ }
84
+ export interface JudgeVerdict {
85
+ exitId: string;
86
+ /** Уверенность, 0…1. Запасная модель вероятностей не даёт: 1 или 0. */
87
+ probability: number;
88
+ /** Условие выполнено: уверенность не ниже порога `threshold`. */
89
+ confirmed: boolean;
90
+ /** Кто решил: основной судья или запасная модель. */
91
+ source: "decision" | "fallback";
92
+ }
93
+ export interface JudgeSettings {
94
+ /** С какой уверенности переходить. По умолчанию 0.7. */
95
+ threshold?: number;
96
+ /** Основной судья. По умолчанию `microsoft/microsoft-decision-1`. */
97
+ model?: string;
98
+ /** Запасная модель каталога приложения: когда основной судья не ответил. */
99
+ fallback: ModelChoice;
100
+ /** Ключ и адрес OpenRouter; не заданы — из окружения сервера. `headers` — свои заголовки запроса (прокси). */
101
+ openrouter?: {
102
+ apiKey?: string;
103
+ baseUrl?: string;
104
+ headers?: Record<string, string>;
105
+ };
106
+ /** Срок основного судьи, мс. По умолчанию 5000. */
107
+ timeoutMs?: number;
108
+ }
109
+ export interface FlowJudge {
110
+ /**
111
+ * Агент заявил переход — подтвердить или нет. Вероятность приходит и ниже порога (`confirmed: false`).
112
+ * `null` — ни один судья не ответил («не проверено»).
113
+ */
114
+ checkClaim(input: {
115
+ ownerId: string;
116
+ chatId: string;
117
+ exitId: string;
118
+ condition: string;
119
+ /** Цель состояния. */
120
+ goal?: string;
121
+ /** Слова агента при заявке. */
122
+ why?: string;
123
+ messages: JudgeMessage[];
124
+ signal?: AbortSignal;
125
+ }): Promise<JudgeVerdict | null>;
126
+ /** После хода: условия словами, которые выполнены, в порядке `conditions` (своё раньше общего). Пусто — ни одно или не проверено. */
127
+ checkAll(input: {
128
+ ownerId: string;
129
+ chatId: string;
130
+ /** Цель состояния. */
131
+ goal?: string;
132
+ conditions: Array<{
133
+ exitId: string;
134
+ text: string;
135
+ }>;
136
+ messages: JudgeMessage[];
137
+ signal?: AbortSignal;
138
+ }): Promise<JudgeVerdict[]>;
139
+ }
140
+ export interface FlowTimerSettings {
141
+ /** Как часто смотреть наступившие таймеры, мс. По умолчанию 15 000. */
142
+ tickMs?: number;
143
+ /** Аренда таймера копией сервера, мс. По умолчанию 60 000. */
144
+ leaseMs?: number;
145
+ /**
146
+ * Мягкий потолок ходов сервера подряд без сообщения человека (касания, переходы по таймерам), с 0.53. Дошли — строка в
147
+ * журнал, касания и переходы по таймерам ждут сообщения человека; «срок вышел» и «не завершил» — в свой срок.
148
+ * По умолчанию 50; `false` — без потолка.
149
+ */
150
+ maxServerTurnsInRow?: number | false;
151
+ }
152
+ export interface FlowTimers {
153
+ start(): void;
154
+ stop(): Promise<void>;
155
+ /** Один обход (тесты, ручной запуск). */
156
+ tick(): Promise<void>;
157
+ /** Пересчитать ближайший таймер чата после хода или перехода. `failedAt` — касание не дошло (часы сервера): срок не раньше. */
158
+ reschedule(input: {
159
+ ownerId: string;
160
+ chatId: string;
161
+ run: FlowRun;
162
+ branchHead: string | null;
163
+ failedAt?: number;
164
+ }): Promise<void>;
165
+ }
166
+ export interface FirstMessageRules {
167
+ /** Можно ли написать первым сейчас; нет — когда повторить (мс) или `null`. */
168
+ check(input: {
169
+ ownerId: string;
170
+ chatId: string;
171
+ at: number;
172
+ timeZone: string;
173
+ /** «Дни и время» сценария: его ночь и потолок главнее настроек приложения, закрытый день — до следующего. */
174
+ schedule?: FlowSchedule;
175
+ /** Запись чата: окно канала, пауза сотрудника, закрытый день — запрет до правил приложения. */
176
+ chat?: Pick<FlowChatRecord, "dayClosedOn" | "pausedUntil" | "windowClosed">;
177
+ }): Promise<{
178
+ allowed: true;
179
+ } | {
180
+ allowed: false;
181
+ retryAt: number | null;
182
+ }>;
183
+ /** Записать, что агент написал первым. */
184
+ record(input: {
185
+ ownerId: string;
186
+ at: number;
187
+ timeZone: string;
188
+ }): Promise<void>;
189
+ }
190
+ /** Ход чата для инструментов карточки: кто говорит, где состояние чата и по какому сценарию идёт разговор. */
191
+ export interface CardToolsTurn {
192
+ model: AgentToolsModel;
193
+ /** Сценарий чата; `null` — чат без сценария. */
194
+ flow(): Promise<{
195
+ flowId: string;
196
+ versionId: string;
197
+ flow: AgentFlow;
198
+ } | null>;
199
+ }
200
+ export interface ClientCardTools {
201
+ /** `flow_note` и `client_card`: агент записывает узнанное. Функция — инструменты собираются на ход чата. */
202
+ tools: ToolSet | ((turn: CardToolsTurn) => ToolSet);
203
+ /** Блок «что известно о человеке» для запроса. */
204
+ block(input: {
205
+ ownerId: string;
206
+ flow: AgentFlow;
207
+ run: FlowRun;
208
+ }): Promise<string>;
209
+ read(ownerId: string): Promise<ClientCardEntry[]>;
210
+ forget(ownerId: string, fieldId: string | "all"): Promise<void>;
211
+ }
212
+ /** Свободные факты о человеке в хранилище приложения. Нет — они лежат в таблице модуля вместе с карточкой. */
213
+ export interface ClientFactStore {
214
+ read(ownerId: string): Promise<ClientFact[]>;
215
+ /** Записать один факт; ошибка — инструмент ответит агенту «не записано». Тот же `fact.id` — тот же факт. */
216
+ add(input: {
217
+ ownerId: string;
218
+ fact: {
219
+ id: string;
220
+ text: string;
221
+ };
222
+ chatId: string;
223
+ flowId: string;
224
+ at: Date;
225
+ }): Promise<void>;
226
+ /** Стереть один факт по `id` или все факты человека. */
227
+ forget(ownerId: string, factId: string | "all"): Promise<void>;
228
+ }
229
+ /** Карточка клиента в хранилище приложения. Нет — таблица модуля (`store.readCard` и др.), как в 0.48. */
230
+ export interface ClientCardStore {
231
+ read(ownerId: string): Promise<ClientCardEntry[]>;
232
+ /** Записать одно поле; ошибка — инструмент ответит агенту «не записано». */
233
+ write(input: {
234
+ ownerId: string;
235
+ fieldId: string;
236
+ value: unknown;
237
+ chatId: string;
238
+ flowId: string;
239
+ at: Date;
240
+ }): Promise<void>;
241
+ forget(ownerId: string, fieldId: string | "all"): Promise<void>;
242
+ /**
243
+ * Свои таблицы фактов (`clientFacts: true`). Нет — факты лежат в таблице модуля. «Стереть всё» зовёт `forget("all")`
244
+ * и здесь, и в полях.
245
+ */
246
+ facts?: ClientFactStore;
247
+ }
248
+ /** Пояс человека в хранилище приложения. Нет — таблица модуля `flow_people`. */
249
+ export interface FlowPeople {
250
+ /** IANA-пояс; не знаем — `null`. */
251
+ readTimeZone(ownerId: string): Promise<string | null>;
252
+ writeTimeZone(ownerId: string, timeZone: string): Promise<void>;
253
+ }
254
+ /** Ответ агента, который он написал первым, для канала приложения. */
255
+ export interface FlowOutgoing {
256
+ ownerId: string;
257
+ chatId: string;
258
+ /** Ключ повтора: тот же текст при `retry` уходит с тем же `messageId`. */
259
+ messageId: string;
260
+ /** Готовый, проверенный текст: текстовые части ответа подряд. */
261
+ text: string;
262
+ /**
263
+ * Все части ответа, которые надо доставить человеку (с 0.53): текст, файлы, вызовы инструментов с результатом —
264
+ * карточка ролика (`tool-show_video`), картинка, виджет. Без рассуждений модели и служебных частей (`deliverableParts`).
265
+ * Карточки строит приложение, как для обычного ответа.
266
+ */
267
+ parts: StoredMessage["parts"];
268
+ reasons: FlowFirstReason[];
269
+ /** Номер попытки, с 1. */
270
+ attempt: number;
271
+ }
272
+ export type FlowDeliveryOutcome = {
273
+ status: "delivered";
274
+ }
275
+ /** Человек заблокировал канал: конец разговора `blocked`. */
276
+ | {
277
+ status: "blocked";
278
+ }
279
+ /** Канал не принимает сообщения первым (окно ВК): молчать, пока человек не напишет. */
280
+ | {
281
+ status: "window_closed";
282
+ }
283
+ /** Повторить тот же текст; нет `afterMs` — по ряду 1 мин · 5 мин · 30 мин. */
284
+ | {
285
+ status: "retry";
286
+ afterMs?: number;
287
+ }
288
+ /** Не доставлено окончательно: текст помечается недоставленным, в счётчик дня не идёт. */
289
+ | {
290
+ status: "failed";
291
+ };
292
+ /** Каналы приложения: служба чата на чат и, если нужно, своя доставка. */
293
+ export interface FlowAttachment {
294
+ /** Служба чата этого разговора: у разных продуктов — разные службы с разной инструкцией. Может отвечать позже (обещанием). */
295
+ portFor: (chat: {
296
+ ownerId: string;
297
+ chatId: string;
298
+ }) => FlowPort | Promise<FlowPort>;
299
+ /** Своя доставка ответа, написанного первым. Нет — доставляют каналы SDK, как в 0.48. */
300
+ deliver?: (message: FlowOutgoing) => Promise<FlowDeliveryOutcome>;
301
+ }
302
+ /** Исход доставки из каналов SDK (`createMessengers({ onAnswerDelivery })`). */
303
+ export interface FlowDeliveryReport {
304
+ chatId: string;
305
+ messageId: string;
306
+ outcome: FlowDeliveryOutcome;
307
+ }
308
+ /** Сообщение человека пришло: как с ним поступить. Каналы приложения зовут `admit` до хода. */
309
+ export type FlowAdmitDecision = {
310
+ kind: "answer";
311
+ }
312
+ /** Ответить готовой фразой, без модели (жёсткая рамка дня). */
313
+ | {
314
+ kind: "reply";
315
+ text: string;
316
+ }
317
+ /** Записать и промолчать до `until`: после жёсткой рамки дня, до начала следующего дня. Ночью агент отвечает. */
318
+ | {
319
+ kind: "hold";
320
+ until: Date;
321
+ }
322
+ /**
323
+ * Разговор закончен. Каналы SDK сами ответят по общей части сценария (→ В6); канал приложения решает сам: ответить
324
+ * (ход как обычно), промолчать или передать во «Входящие».
325
+ */
326
+ | {
327
+ kind: "ended";
328
+ reason: FlowEndReason;
329
+ afterEnd: FlowAfterEnd;
330
+ }
331
+ /** Отвечает живой сотрудник: агент молчит. */
332
+ | {
333
+ kind: "paused";
334
+ };
335
+ /** Перемотка тестового чата: на `ms` или до начала следующего дня сценария. */
336
+ export type FlowTestClockInput = {
337
+ ownerId: string;
338
+ chatId: string;
339
+ } & ({
340
+ ms: number;
341
+ to?: undefined;
342
+ } | {
343
+ to: "next_day_start";
344
+ ms?: undefined;
345
+ });
346
+ export interface FlowAdmitInput {
347
+ ownerId: string;
348
+ chatId: string;
349
+ text?: string;
350
+ /**
351
+ * Сколько сообщений человека решается разом (пачка, склеенная в один ход): все идут в счёт дня. Пачка доходит до
352
+ * `hardAt` — готовая фраза один раз на всю пачку; начинается уже за ним — молчание. По умолчанию 1.
353
+ */
354
+ messages?: number;
355
+ }
356
+ /** Пауза «отвечает живой сотрудник» (→ В4). */
357
+ export interface FlowPauseSettings {
358
+ /** Пауза снимается сама через столько часов молчания сотрудника; каждый ответ сотрудника продлевает. По умолчанию 2. */
359
+ staffSilenceHours?: number;
360
+ }
361
+ /** Сотрудник ответил человеку вручную (→ В4). */
362
+ export interface FlowStaffReply {
363
+ ownerId: string;
364
+ chatId: string;
365
+ /**
366
+ * Что он написал: ложится в историю сообщением сотрудника (`ChatService.appendStaffMessage`, с 0.53) — агент увидит
367
+ * его в своём контексте и продолжит, не противореча; словами человека оно не считается. Нет — только продление паузы.
368
+ */
369
+ text?: string;
370
+ /** Имя сотрудника (с 0.53): модель и экран назовут его. */
371
+ staffName?: string;
372
+ }
373
+ export interface FlowPauseInput {
374
+ ownerId: string;
375
+ chatId: string;
376
+ /** До какого момента; нет — `staffSilenceHours` от сейчас. */
377
+ until?: Date;
378
+ }
379
+ /** Разговор закончился: срок вышел, конечная нода, человек ушёл или заблокировал канал. */
380
+ export interface FlowFinish {
381
+ ownerId: string;
382
+ chatId: string;
383
+ flowId: string;
384
+ versionId: string;
385
+ reason: FlowEndReason;
386
+ /** Итог словами приложения: передан в `flow.end`. Других концов — нет. */
387
+ outcome?: string;
388
+ /** День сценария; без «Дней и времени» — `null`. */
389
+ day: number | null;
390
+ /** Нода, в которой закончился разговор. */
391
+ state: string;
392
+ /** Метки этой ноды: итог разговора («записался», «отказ»). */
393
+ tags: string[];
394
+ /**
395
+ * Место в сценарии на момент конца: путь, заметки чата (`notes`), счётчики. Вместе с карточкой человека
396
+ * (`readClientCard(ownerId)`) — всё, чтобы передать человека дальше.
397
+ */
398
+ run: FlowRun;
399
+ at: Date;
400
+ }
401
+ export interface MigrateChatsInput {
402
+ flowId: string;
403
+ toVersionId: string;
404
+ /** Куда перевести чаты из состояний, которых нет в новой версии: id старого → id нового. */
405
+ map: StateMap;
406
+ /**
407
+ * Сценарий новой версии (JSON или разобранный). Нужен, если по этой версии ещё не шёл ни один чат: SDK сохранит копию.
408
+ * Версия уже сохранена — должна совпасть.
409
+ */
410
+ flow?: AgentFlow | unknown;
411
+ }
412
+ export interface ChatMigrator {
413
+ /** Сколько чатов в каких состояниях; `exceptVersionId` — без чатов, которые уже на этой версии. */
414
+ chatsByState(flowId: string, options?: {
415
+ exceptVersionId?: string;
416
+ }): Promise<ChatsInState[]>;
417
+ /** Переводит пачками; чаты применят перевод в начале своего следующего хода. */
418
+ migrateChats(input: MigrateChatsInput): Promise<{
419
+ moved: number;
420
+ }>;
421
+ }
422
+ /** Что сценарию нужно от службы чата. Приложение передаёт её в `attach` после `createChatServer`. */
423
+ export type FlowPort = Pick<ChatService, "deliverNotices" | "getChatState" | "peekHistory" | "getChatData" | "updateChatData" | "assertWithinLimit" | "markUndelivered"> &
424
+ /** Поток идущего хода: перемотка часов тестового чата ждёт по нему конца хода. */
425
+ Partial<Pick<ChatService, "subscribe">> & {
426
+ askModel: AskModel;
427
+ recordUsage: RecordUsage;
428
+ };
429
+ /** Часть настроек службы чата, которую `withChat` дополняет; остальное проходит как есть. */
430
+ export interface FlowChatOptions {
431
+ agent: AgentSettings;
432
+ chatInstructions?: ChatInstructions;
433
+ privateStateKeys?: string[];
434
+ /** Свои ворота приложения идут первыми; пропустили — решение сценария (рамки дня, пауза). */
435
+ beforeTurn?: BeforeTurnHandler;
436
+ afterTurn?: AfterTurnHandler;
437
+ onStateChanged?: StateChangedHandler;
438
+ /**
439
+ * Проверки ответа службы (`createChatService({ replyChecks })`). С ними `withChat` добавляет тексты сценария чата
440
+ * (`flowKnownTexts`) к `knownTexts`: ссылка из первого сообщения или прощания не считается выдуманной. Нет — не трогается.
441
+ */
442
+ replyChecks?: ReplyChecksSetting;
443
+ }
444
+ export interface AgentFlowOptions {
445
+ /** Какой сценарий у чата; `null` — чат без сценария. Зовётся, пока чат не закреплён за версией. */
446
+ loadFlow: (input: {
447
+ ownerId: string;
448
+ chat: Chat;
449
+ }) => Promise<LoadedFlow | null>;
450
+ /** `memoryFlowStore()`, `sqliteFlowStore(path)`, `postgresFlowStore(pool)`. */
451
+ store: FlowStore;
452
+ judge: JudgeSettings;
453
+ /** Правила «писать первым». По умолчанию тихие часы 21:00–09:00 и не больше 3 в сутки; `false` — без ограничений; «не сейчас» — `canWriteNow`. */
454
+ firstMessages?: FirstMessagesSetting;
455
+ onTransition?: (event: FlowTransitionEvent) => void | Promise<void>;
456
+ /** Разговор закончился (`final`, `deadline`, `abandoned`, `blocked`). Ошибка приложения не ломает сценарий. */
457
+ onFinish?: (event: FlowFinish) => void | Promise<void>;
458
+ /** Карточка клиента в хранилище приложения. Нет — таблица модуля. */
459
+ clientCard?: ClientCardStore;
460
+ /**
461
+ * Свободные факты о человеке в карточке («ведёт блог»): агент пишет их тем же `flow_note { fact }`, блок `flow:client`
462
+ * показывает, «забудь» стирает. В инструкции появляется правило, как делить факты и долгую память SDK. По умолчанию нет.
463
+ */
464
+ clientFacts?: boolean;
465
+ /** Пояс человека в хранилище приложения. Нет — таблица модуля. */
466
+ people?: FlowPeople;
467
+ /**
468
+ * Пояс по умолчанию (IANA, «Europe/Moscow»), с 0.53: по нему дни, тихие часы и касания, пока пояс человека неизвестен
469
+ * (до первого хода, мессенджер без страницы). Порядок: человек → страница → `defaultTimeZone` → пояс службы
470
+ * (`agent.timeZone`) → сервер. Не пояс — `TypeError` при создании.
471
+ */
472
+ defaultTimeZone?: string;
473
+ pause?: FlowPauseSettings;
474
+ timers?: FlowTimerSettings;
475
+ logError?: (message: string) => void;
476
+ /** Часы; для тестов. */
477
+ now?: () => Date;
478
+ }
479
+ /** Событие приложения вне хода: кнопка страницы, оплата, событие сервера. */
480
+ export interface FlowSignalInput {
481
+ ownerId: string;
482
+ chatId: string;
483
+ name: string;
484
+ data?: unknown;
485
+ /** Разбудить агента: он ответит сразу. Нет — переход применится в начале следующего хода. */
486
+ wakeAgent?: boolean;
487
+ }
488
+ export interface AgentFlowHandle {
489
+ /** Склеивает сценарий с настройками службы чата: инструкция, инструменты, блоки, `prepareTurn`, `afterTurn`. */
490
+ withChat<T extends FlowChatOptions>(options: T): T;
491
+ /** Служба чата (`attach(service)`, как в 0.48) или служба на чат и своя доставка. */
492
+ attach(target: FlowPort | FlowAttachment): void;
493
+ signal(input: FlowSignalInput): Promise<void>;
494
+ /**
495
+ * Закрепить идущий чат за сценарием сразу, не дожидаясь хода: место, последние реплики, шаг касаний и счёт дня — из
496
+ * старой системы, таймер касаний ставится тут же. Чат уже закреплён — `adopted: false`, ничего не меняется. Сценарий с
497
+ * ошибками или нет состояния — исключение. Чат должен быть в службе чата (`createChat`).
498
+ */
499
+ adopt(input: FlowAdoptInput): Promise<{
500
+ adopted: boolean;
501
+ }>;
502
+ /**
503
+ * Закончить разговор сейчас со своей причиной: событие `onFinish`, как у остальных концов; сроки и касания сняты, место
504
+ * остаётся в своей ноде; дальше — как настроено `afterEnd`. Уже законченный или чат не по сценарию — `ended: false`.
505
+ */
506
+ end(input: FlowEndInput): Promise<{
507
+ ended: boolean;
508
+ }>;
509
+ /** Сообщение человека в канале приложения: отвечать, ответить готовой фразой, промолчать. Звать до хода. */
510
+ admit(input: FlowAdmitInput): Promise<FlowAdmitDecision>;
511
+ /** Отвечает живой сотрудник: агент молчит, таймеры и касания стоят. */
512
+ pause(input: FlowPauseInput): Promise<void>;
513
+ resume(input: {
514
+ ownerId: string;
515
+ chatId: string;
516
+ }): Promise<void>;
517
+ /** Сотрудник ответил человеку: пауза продлевается на `staffSilenceHours`, его реплика — в контекст агента. */
518
+ noteStaffReply(input: FlowStaffReply): Promise<void>;
519
+ /** Исход доставки ответа из каналов SDK (`onAnswerDelivery`). */
520
+ reportDelivery(input: FlowDeliveryReport): Promise<void>;
521
+ readRun(ownerId: string, chatId: string): Promise<FlowRunView | null>;
522
+ chatsByState: ChatMigrator["chatsByState"];
523
+ migrateChats: ChatMigrator["migrateChats"];
524
+ readClientCard: ClientCardTools["read"];
525
+ forgetClientCard: ClientCardTools["forget"];
526
+ /**
527
+ * Тестовый чат редактора (создан с `flowTestChatData()`): промотать его часы вперёд на `ms` или до начала следующего
528
+ * дня сценария (`to: "next_day_start"`, по часам человека). Наступивший таймер срабатывает сразу; ответ ждёт конца
529
+ * его хода. Рабочий чат — ошибка.
530
+ */
531
+ advanceTestClock(input: FlowTestClockInput): Promise<{
532
+ timeOffset: number;
533
+ }>;
534
+ start(): void;
535
+ stop(): Promise<void>;
536
+ /**
537
+ * Один обход таймеров сейчас: сработает всё, чей срок наступил. Для тестов и ручного обхода (свой планировщик вместо
538
+ * `start()`); в обычной работе обходы идут сами после `start()`.
539
+ */
540
+ tick(): Promise<void>;
541
+ /** Дождаться фоновой работы: судья после хода, доставка, `onTransition`, `onFinish`. Для тестов и остановки. */
542
+ settled(): Promise<void>;
543
+ }
544
+ /** Служебное сообщение сценария для модели: вид `app` ядра. */
545
+ export type FlowNotice = AgentNotice & {
546
+ kind: "app";
547
+ };
@@ -0,0 +1,25 @@
1
+ import type { FlowRun } from "../engine/flow-run.js";
2
+ import type { FlowChatRecord, FlowStore } from "../store/flow-store.js";
3
+ import type { FlowPort } from "./flow-types.js";
4
+ /** Ключ данных чата (`Chat.data`) со временем последнего сообщения человека, отложенного до утра (часы чата, мс). */
5
+ export declare const FLOW_HELD_HUMAN_KEY = "flowHeldHuman";
6
+ /** Время последнего отложенного сообщения человека из данных чата, мс; нет — `null`. */
7
+ export declare function heldHumanAt(data: Record<string, unknown> | undefined): number | null;
8
+ /**
9
+ * Место чата для сроков с отложенным сообщением человека: оно — его слова, как сообщение с ходом. От него считаются
10
+ * тишина человека («не завершил», переход «человек молчит») и правило «писал меньше 3 ч назад» (утреннее, касания утром).
11
+ */
12
+ export declare function withHeldHuman<R extends FlowRun>(run: R, data: Record<string, unknown> | undefined): R;
13
+ /**
14
+ * Сообщение человека отложено до утра: запомнить его время (часы чата) и поставить срок чата не позже `morningAt` (часы
15
+ * сервера) — в этот срок обход таймеров увидит сообщения без ответа в истории и ответит на них. Срок раньше не трогается:
16
+ * обход всё равно считает ближайший заново.
17
+ */
18
+ export declare function noteHeldUntilMorning(input: {
19
+ store: FlowStore;
20
+ port: FlowPort;
21
+ record: FlowChatRecord;
22
+ at: number;
23
+ morningAt: number;
24
+ logError: (message: string) => void;
25
+ }): Promise<void>;
@@ -0,0 +1,11 @@
1
+ export { createAgentFlow } from "./agent-flow.js";
2
+ export type * from "./flow-types.js";
3
+ export { DEFAULT_JUDGE_MODEL, DEFAULT_JUDGE_THRESHOLD, DEFAULT_JUDGE_TIMEOUT_MS } from "./judge.js";
4
+ export { DEFAULT_TIMER_LEASE_MS, DEFAULT_TIMER_TICK_MS } from "./flow-timers.js";
5
+ export { flowTestChatData, testChatOffset } from "./test-clock.js";
6
+ export { DEFAULT_PAUSE_STAFF_SILENCE_HOURS } from "./pause.js";
7
+ export { FLOW_EXIT_TOOL, FLOW_EXTRA_TOOLS } from "./flow-tools.js";
8
+ export { FLOW_TIME_ZONE_TOOL } from "./time-zone-tool.js";
9
+ export { FLOW_CLOSE_DAY_TOOL } from "./close-day-tool.js";
10
+ export { FLOW_SIDE_TASK_TOOL } from "./side-task-tool.js";
11
+ export { factId } from "./client-facts.js";
@@ -0,0 +1,36 @@
1
+ import type { JudgeMessage } from "./flow-types.js";
2
+ /** Сколько последних реплик видит судья. */
3
+ export declare const JUDGE_MESSAGES = 6;
4
+ /** Длина одной реплики, знаков. */
5
+ export declare const JUDGE_MESSAGE_CHARS = 600;
6
+ /** Весь вход судьи, знаков: контекст decision-1 — 32 тыс. токенов, а токен бывает и в один знак. */
7
+ export declare const JUDGE_INPUT_CHARS = 24000;
8
+ export interface JudgeContextInput {
9
+ goal?: string;
10
+ /** Слова агента при заявке: почему он считает условие выполненным. */
11
+ why?: string;
12
+ messages: JudgeMessage[];
13
+ conditions: Array<{
14
+ exitId: string;
15
+ text: string;
16
+ }>;
17
+ }
18
+ export interface JudgeContext {
19
+ /** Что оценивает судья: цель, последние реплики, слова агента. */
20
+ state: {
21
+ goal?: string;
22
+ conversation: JudgeMessage[];
23
+ agent_words?: string;
24
+ };
25
+ /** Условия с обрезанным текстом, в исходном порядке. */
26
+ conditions: Array<{
27
+ exitId: string;
28
+ text: string;
29
+ }>;
30
+ }
31
+ /**
32
+ * Вход судьи: последние реплики, каждая обрезана, и общий потолок под контекст модели.
33
+ * Не помещается — сначала уходят старые реплики (свежая остаётся), потом все поля сжимаются вдвое, пока не уложатся.
34
+ * Условия не пропадают: на каждое судья должен ответить.
35
+ */
36
+ export declare function buildJudgeContext(input: JudgeContextInput): JudgeContext;
@@ -0,0 +1,21 @@
1
+ import type { AskModel, ChatService, RecordUsage } from "@ykurilov/agent-chat-core";
2
+ import type { FlowJudge, JudgeSettings } from "./flow-types.js";
3
+ export declare const DEFAULT_JUDGE_THRESHOLD = 0.7;
4
+ export declare const DEFAULT_JUDGE_MODEL = "microsoft/microsoft-decision-1";
5
+ export declare const DEFAULT_JUDGE_TIMEOUT_MS = 5000;
6
+ /** Что подменяют тесты. Окружение — откуда берутся ключ и адрес OpenRouter, если в настройках судьи их нет. */
7
+ export interface JudgeDeps {
8
+ fetch?: typeof fetch;
9
+ env?: Record<string, string | undefined>;
10
+ logError?: (message: string) => void;
11
+ }
12
+ /** Что судье нужно от службы чата. */
13
+ export type JudgePort = Pick<ChatService, "assertWithinLimit"> & {
14
+ askModel: AskModel;
15
+ recordUsage: RecordUsage;
16
+ };
17
+ /** Судья: decision-1, при сбое — запасная модель через `askModel`; расход — в журнал с `source: "judge"`. Служба — та, что ведёт чат вопроса. */
18
+ export declare function createFlowJudge(settings: JudgeSettings, port: (chat: {
19
+ ownerId: string;
20
+ chatId: string;
21
+ }) => JudgePort | Promise<JudgePort>, deps?: JudgeDeps): FlowJudge;