@ykurilov/agent-chat-flows 0.80.1 → 0.82.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/prompt/state-card.ts","../src/runtime/flow-events.ts","../src/runtime/test-clock.ts","../src/runtime/client-facts.ts","../src/runtime/first-message-retry.ts","../src/runtime/first-message-rules.ts","../src/runtime/first-reasons-notice.ts","../src/runtime/flow-finish.ts","../src/runtime/prepare-turn.ts","../src/prompt/client-card-block.ts"],"sourcesContent":["import type { AgentFlow } from \"../format/flow-format\";\nimport type { FlowRun } from \"../engine/flow-run\";\nimport { activeExits, fieldById, isFinal, stateById } from \"../engine/flow-graph\";\n\n/** Условия словами состояния по порядку проверки (свои, потом общие): номер в карточке = место в этом списке + 1. */\nexport function judgeExits(flow: AgentFlow, stateId: string): Array<{ exitId: string; text: string }> {\n const state = stateById(flow, stateId);\n if (!state) return [];\n return activeExits(flow, state).flatMap(({ exit }) => (exit.when.type === \"judge\" ? [{ exitId: exit.id, text: exit.when.text }] : []));\n}\n\n/** Цель состояния: первая строка инструкции. */\nexport function stateGoal(instructions: string): string {\n return instructions.split(\"\\n\")[0]!.trim();\n}\n\n/**\n * Блок `flow:state`: текущее состояние, цель, условия выхода, что узнать. Зависит только от состояния и набора\n * инструментов модели хода — пока они те же, блок не меняется и в новые вопросы не дописывается (→ В9). Первого текста\n * здесь нет: его несёт переход. `present` — имена в наборе модели хода: инструментов сценария вне набора карточка не\n * называет. Нет — набор неизвестен, называются все.\n */\nexport function stateCard(flow: AgentFlow, run: FlowRun, present?: ReadonlySet<string>): string {\n const state = stateById(flow, run.state);\n if (!state) return `Состояние «${run.state}» в сценарии не найдено: веди разговор по общим правилам.`;\n const [goal = \"\", ...rest] = state.instructions.split(\"\\n\");\n const lines = [`Состояние: «${state.title}»`, `Цель: ${goal.trim()}`];\n const how = rest.join(\"\\n\").trim();\n if (how) lines.push(\"\", how);\n\n const fields = state.learn.map((id) => fieldById(flow, id)).flatMap((found) => (found ? [`- ${found.field.name}: ${found.field.about}`] : []));\n if (fields.length > 0) lines.push(\"\", \"Узнать здесь:\", ...fields);\n\n const conditions = judgeExits(flow, state.id);\n if (isFinal(state)) {\n lines.push(\"\", \"Это последнее состояние сценария: условий выхода нет.\");\n } else if (conditions.length > 0) {\n lines.push(\"\", \"Условия выхода (номер — для flow_exit):\", ...conditions.map((condition, index) => `${index + 1}. ${condition.text}`));\n }\n\n const inSet = (name: string) => !present || present.has(name);\n const tools = state.tools.filter(inSet);\n if (tools.length > 0) lines.push(\"\", `Инструменты этого состояния: ${tools.join(\", \")}.`);\n const unavailable = flow.tools.filter((name) => inSet(name) && !state.tools.includes(name));\n if (unavailable.length > 0) lines.push(`Сейчас недоступны: ${unavailable.join(\", \")}.`);\n return lines.join(\"\\n\");\n}\n","import type { Chat } from \"@ykurilov/agent-chat-core\";\nimport type { AgentFlow, FlowExit } from \"../format/flow-format\";\nimport { FLOW_CHAIN_LIMIT, FLOW_STATE_KEY, type FlowRun, type Transition } from \"../engine/flow-run\";\nimport { stateById } from \"../engine/flow-graph\";\nimport { describeCondition, nextTransition, type FlowEvent } from \"../engine/next-transition\";\nimport { applyTransition, reopenRun, startRun } from \"../engine/run-updates\";\nimport { dayInProgress } from \"../engine/flow-days\";\nimport { overdueOnEntry } from \"../engine/timers\";\nimport { stateCard } from \"../prompt/state-card\";\nimport type { FlowChatRecord, FlowStore, PendingFlowChange } from \"../store/flow-store\";\nimport type { FlowNotice, FlowTransitionEvent } from \"./flow-types\";\nimport type { FlowFirstReason } from \"../engine/nudges\";\nimport type { FlowTimeZones } from \"./first-message-rules\";\n\n/**\n * `FlowRun` в состоянии чата: плюс ответ агента, в начале которого он записан. По нему ожидающий переход судьи узнаёт\n * свою ветку: правка или «Переделать» начинают ветку с другим ответом — переход снимается.\n */\nexport type ChatFlowRun = FlowRun & {\n /** Почему агент пишет первым в ответе `answerId`; ход на сообщение человека — нет. Для хвоста приложения. */\n firstReasons?: FlowFirstReason[];\n};\n\n/** Чат закреплён за версией сценария. */\nexport interface PinnedFlow {\n flowId: string;\n versionId: string;\n flow: AgentFlow;\n /** Разговор закончен (`endChat`): инструкция «после конца» вместо правил сценария. */\n ended?: boolean;\n}\n\n/** Что общего у частей сценария в службе чата: хранилище, часы, сценарий чата и отчёт о переходах. */\nexport interface FlowRuntime {\n confirmations?: import(\"./confirmations\").FlowConfirmations;\n store: FlowStore;\n now(): number;\n logError(message: string): void;\n /** Сценарий чата; с `chat` закрепляет незакреплённый чат за версией (`loadFlow`). `null` — чат без сценария. */\n chatFlow(ownerId: string, chatId: string, chat?: Chat): Promise<PinnedFlow | null>;\n /** Фоновая работа, которую ждёт `settled`; нет — не отслеживается (подделки в тестах). */\n track?(work: Promise<unknown>): void;\n /** Значения карточки клиента по `id` полей: для условий по данным. */\n cardValues(ownerId: string): Promise<Record<string, unknown>>;\n /** Пояс человека; нет — пояс хода (подделки в тестах). */\n timeZones?: FlowTimeZones;\n /** `AgentFlowOptions.defaultTimeZone`: пояс хода без страницы и без известного пояса человека — он, а не пояс службы. */\n defaultTimeZone?: string;\n /** Переходы случились: копия состояния в хранилище, `onTransition`. Не бросает. */\n afterTransitions(input: { ownerId: string; chatId: string; pinned: PinnedFlow; run: FlowRun; transitions: Transition[] }): Promise<void>;\n}\n\n/** Сообщить приложению о переходе (`onTransition`); ошибка приложения не ломает ход. */\nexport async function reportTransition(\n handler: ((event: FlowTransitionEvent) => void | Promise<void>) | undefined,\n event: FlowTransitionEvent,\n logError: (message: string) => void,\n): Promise<void> {\n if (!handler) return;\n try {\n await handler(event);\n } catch (error) {\n logError(`[сценарий] чат=${event.chatId} onTransition ${event.from} → ${event.to} упал: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\n/** `FlowRun` из состояния чата; нет или не то — `null`. */\nexport function storedRun(value: unknown): ChatFlowRun | null {\n return value && typeof value === \"object\" && typeof (value as FlowRun).state === \"string\" ? (value as ChatFlowRun) : null;\n}\n\n/**\n * Разговор открыт заново (`flow.reopen`), а место в состоянии чата ещё «до»: оно догоняет отметку записи — конец снят,\n * ряд касаний с первого шага от открытия. Уже догнало, не открывали или снова закончен — как есть.\n */\nexport function withReopen<T extends FlowRun>(flow: AgentFlow, run: T, record: Partial<Pick<FlowChatRecord, \"reopenedAt\" | \"endedAt\">> | null | undefined): T {\n if (!record?.reopenedAt || record.endedAt) return run;\n const at = Date.parse(record.reopenedAt);\n return run.reopenedAt === at ? run : reopenRun(flow, run, at);\n}\n\n/** Где чат сейчас: из состояния ветки, а на пустой ветке — старт сценария. */\nexport function currentRun(flow: AgentFlow, values: Record<string, unknown> | undefined, now: number): ChatFlowRun {\n return storedRun(values?.[FLOW_STATE_KEY]) ?? startRun(flow, now);\n}\n\n/**\n * Куда перенесли чат при закреплении (`loadFlow`, `adopt`): состояние и моменты, от которых считаются сроки, а у идущего\n * чата — последние слова обеих сторон, шаг касаний и счёт сообщений человека за день. Нет поля — как у нового чата.\n */\nexport interface ChatPlacement {\n stateId?: string;\n startedAt?: number;\n enteredAt?: number;\n lastHumanAt?: number;\n lastAgentAt?: number;\n nudgeStep?: number;\n /** Сообщения человека за день сценария к моменту `at`. */\n humanToday?: { at: number; count: number };\n}\n\n/**\n * Место чата, перенесённого в заданное состояние: путь начинается с него, сроки таймеров — от `enteredAt` (нет — от\n * `startedAt`). Счёт дня ложится в день сценария по поясу `timeZone`; пояса нет — счёт не переносится.\n */\nexport function placedRun(flow: AgentFlow, placement: ChatPlacement, now: number, timeZone?: string): ChatFlowRun {\n const start = startRun(flow, now);\n const state = (placement.stateId !== undefined && stateById(flow, placement.stateId)) || stateById(flow, flow.start)!;\n const startedAt = placement.startedAt ?? now;\n const enteredAt = placement.enteredAt ?? placement.startedAt ?? now;\n return {\n ...start,\n state: state.id,\n startedAt,\n enteredAt,\n path: [{ state: state.id, at: enteredAt, tags: state.tags, from: null, exitId: null, decidedBy: \"start\" }],\n ...(placement.lastHumanAt !== undefined && { lastHumanAt: placement.lastHumanAt }),\n ...(placement.lastAgentAt !== undefined && { lastAgentAt: placement.lastAgentAt }),\n ...(placement.nudgeStep !== undefined && { nudgeStep: placement.nudgeStep }),\n ...(placement.humanToday && flow.schedule && timeZone && { dayCounts: { [dayInProgress(placement.humanToday.at, timeZone, flow.schedule)]: placement.humanToday.count } }),\n };\n}\n\nconst msOf = (value: string | undefined) => (value === undefined ? undefined : Date.parse(value));\n\n/** Перенос из записи чата; первый ход его уже записал в состояние (или переноса не было) — `null`. */\nexport function recordPlacement(record: Pick<FlowChatRecord, \"stateId\" | \"placement\">): ChatPlacement | null {\n const placement = record.placement;\n if (!placement) return null;\n const lastHumanAt = msOf(placement.lastHumanAt);\n const lastAgentAt = msOf(placement.lastAgentAt);\n return {\n stateId: record.stateId,\n startedAt: Date.parse(placement.startedAt),\n enteredAt: Date.parse(placement.enteredAt),\n ...(lastHumanAt !== undefined && { lastHumanAt }),\n ...(lastAgentAt !== undefined && { lastAgentAt }),\n ...(placement.nudgeStep !== undefined && { nudgeStep: placement.nudgeStep }),\n ...(placement.humanToday && { humanToday: { at: Date.parse(placement.humanToday.at), count: placement.humanToday.count } }),\n };\n}\n\n/**\n * Где чат сейчас вне хода: из состояния ветки; до первого хода перенесённого чата — из записи переноса; иначе старт.\n * Так таймеры, `admit` и `readRun` видят перенесённое место, не дожидаясь хода человека.\n */\nexport function chatRun(\n flow: AgentFlow,\n values: Record<string, unknown> | undefined,\n record: (Pick<FlowChatRecord, \"stateId\" | \"placement\"> & Partial<Pick<FlowChatRecord, \"reopenedAt\" | \"endedAt\">>) | null | undefined,\n now: number,\n timeZone?: string,\n): ChatFlowRun {\n const stored = storedRun(values?.[FLOW_STATE_KEY]);\n if (stored) return withReopen(flow, stored, record);\n const placement = record ? recordPlacement(record) : null;\n return placement ? placedRun(flow, placement, now, timeZone) : startRun(flow, now);\n}\n\n/**\n * Пояс человека для шага движка вне подготовки хода, как в `runOutsideTurn`: пояса модуля, без них — из записи чата;\n * неизвестен — `defaultTimeZone`, иначе сервера.\n */\nexport async function personTimeZone(runtime: Pick<FlowRuntime, \"store\" | \"timeZones\" | \"defaultTimeZone\">, chat: { ownerId: string; chatId: string }): Promise<string> {\n const known = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : (await runtime.store.getChat(chat.chatId))?.timeZone;\n return known || runtime.defaultTimeZone || Intl.DateTimeFormat().resolvedOptions().timeZone;\n}\n\n/** `chatRun` для частей сценария вне хода: запись чата (если не передана) и пояс человека читаются тут. */\nexport async function runOutsideTurn(\n runtime: Pick<FlowRuntime, \"store\" | \"timeZones\">,\n chat: { ownerId: string; chatId: string },\n flow: AgentFlow,\n values: Record<string, unknown> | undefined,\n now: number,\n record?: FlowChatRecord | null,\n): Promise<ChatFlowRun> {\n const found = record === undefined ? await runtime.store.getChat(chat.chatId) : record;\n const stored = storedRun(values?.[FLOW_STATE_KEY]);\n if (stored) return withReopen(flow, stored, found);\n if (!found?.placement) return startRun(flow, now);\n const timeZone = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : found.timeZone || undefined;\n return chatRun(flow, values, found, now, timeZone);\n}\n\n/**\n * Шаг движка с проверкой нового состояния: условия по данным и наступившие таймеры, чей срок не зависит от входа\n * (`overdueOnEntry`). Переходы складываются в `out`. `timeZone` — пояс человека: по нему считаются дни программы.\n */\nexport function advance(\n flow: AgentFlow,\n run: ChatFlowRun,\n event: FlowEvent,\n input: { now: number; card: Record<string, unknown>; out: Transition[]; onChainLimit: (run: FlowRun) => void; timeZone: string },\n): ChatFlowRun {\n let current = run;\n let step: FlowEvent | null = event;\n let entered = false;\n let stopped = false;\n while (step) {\n const transition = nextTransition(flow, current, step, input.now);\n if (!transition) {\n // Движок молчит из-за предела переходов подряд, а без предела переход был бы: сценарий зациклился. Одна запись.\n if (current.chain >= FLOW_CHAIN_LIMIT && nextTransition(flow, { ...current, chain: 0 }, step, input.now)) {\n if (!stopped) input.onChainLimit(current);\n stopped = true;\n }\n // Вошли в состояние, а его срок уже наступил («с дня 3», вошли в день 4): дальше сразу, человек получит один ответ.\n const overdue: string | null = entered && !stopped && step.type === \"data\" ? overdueOnEntry(flow, current, input.now, input.timeZone) : null;\n step = overdue ? { type: \"timer\", exitId: overdue, timeZone: input.timeZone } : null;\n continue;\n }\n current = applyTransition(flow, current, transition);\n input.out.push(transition);\n entered = true;\n // В новом состоянии могли уже выполниться условия по данным: поле узнали раньше.\n step = { type: \"data\", card: input.card };\n }\n return current;\n}\n\n/** Событие движка для ожидающего перехода: таймер проверяется заново по своему сроку, судья — как вердикт. */\nexport function pendingEvent(transition: Transition, timeZone: string): FlowEvent {\n if (transition.decidedBy === \"timer\") return { type: \"timer\", exitId: transition.exitId, timeZone };\n return { type: \"verdict\", exitId: transition.exitId, probability: transition.probability ?? 1, decidedBy: \"judge\" };\n}\n\n/**\n * Ожидающее вне хода поверх места ветки — каким место станет к ходу: переходы своей ветки из того же состояния и\n * события приложения. Само ожидание не трогается.\n */\nexport function withPending(flow: AgentFlow, run: ChatFlowRun, pending: readonly PendingFlowChange[], input: Parameters<typeof advance>[3]): ChatFlowRun {\n let current = run;\n for (const change of pending) {\n if (change.kind === \"transition\") {\n if (change.branchHead !== run.answerId || current.state !== change.transition.from) continue;\n current = advance(flow, current, pendingEvent(change.transition, input.timeZone), input);\n } else if (change.kind === \"event\") {\n current = advance(flow, current, { type: \"event\", event: \"app\", name: change.name }, input);\n }\n }\n return current;\n}\n\n/** Строка журнала про зацикливание. */\nexport const chainLimitMessage = (chatId: string, run: FlowRun) =>\n `[сценарий] чат=${chatId} ${FLOW_CHAIN_LIMIT} переходов подряд без ответа агента, движок остановлен в «${run.state}»`;\n\n/** Условие выхода по `id`: своё у состояния или общее. */\nexport function exitById(flow: AgentFlow, exitId: string): FlowExit | undefined {\n for (const state of flow.states) {\n const found = state.exits.find((exit) => exit.id === exitId);\n if (found) return found;\n }\n return flow.anyState.exits.find((exit) => exit.id === exitId);\n}\n\nexport { describeCondition };\n\n/** Точный первый текст состояния: агент пишет его слово в слово, без модели, когда это возможно. */\nexport function exactFirstText(flow: AgentFlow, stateId: string): string | null {\n return stateById(flow, stateId)?.firstMessage ?? null;\n}\n\nconst sayExactly = (text: string) => `Первое сообщение этого состояния — скажи ровно это: «${text}»`;\n\n/** Служебное сообщение модели о переходах вне её ответа: «Сработало: тишина 3 ч → «Напоминание»». */\nexport function transitionNotice(flow: AgentFlow, transitions: Transition[]): FlowNotice {\n const lines = transitions.map((transition) => {\n const exit = exitById(flow, transition.exitId);\n const title = stateById(flow, transition.to)?.title ?? transition.to;\n return `Сработало: ${exit ? describeCondition(flow, exit.when) : transition.exitId} → «${title}».`;\n });\n const last = transitions.at(-1)!;\n const first = exactFirstText(flow, last.to);\n if (first) lines.push(sayExactly(first));\n return {\n kind: \"app\",\n name: \"flow_transition\",\n title: lines.join(\"\\n\"),\n data: { from: transitions[0]!.from, to: last.to, exitIds: transitions.map((transition) => transition.exitId) },\n };\n}\n\n/** Результат инструмента, после которого сменилось состояние: карточка нового состояния и точный первый текст. */\nexport function transitionReport(flow: AgentFlow, run: FlowRun, transitions: Transition[]): string {\n const from = stateById(flow, transitions[0]!.from)?.title ?? transitions[0]!.from;\n const lines = [`Переход: «${from}» → «${stateById(flow, run.state)?.title ?? run.state}». Состояние сменилось, продолжай по новой карточке.`, \"\", stateCard(flow, run)];\n const first = exactFirstText(flow, run.state);\n if (first) lines.push(\"\", sayExactly(first));\n return lines.join(\"\\n\");\n}\n","import { FLOW_STATE_KEY, type FlowRun } from \"../engine/flow-run\";\nimport { flowDay } from \"../engine/flow-days\";\nimport { FLOW_FIRST_EXIT } from \"../engine/timers\";\nimport { runOutsideTurn, storedRun, type FlowRuntime } from \"./flow-events\";\nimport type { AgentFlow } from \"../format/flow-format\";\nimport type { FlowPort, FlowTestClockInput, FlowTestClockReason, FlowTestClockResult, FlowTimers } from \"./flow-types\";\n\n// Часы тестового чата редактора: автор перематывает их вперёд и видит напоминания без ожидания. Сдвиг хранится в данных\n// чата (`Chat.data`) — их можно писать вне хода, и их видят все копии сервера. Ход копирует сдвиг в место\n// (`FlowRun.timeOffset`), а часы чата везде — часы сервера плюс сдвиг. У рабочих чатов пометки нет: сдвига тоже нет.\n\n/** Ключ данных чата (`Chat.data`) с пометкой тестового чата и сдвигом его часов. */\nexport const FLOW_TEST_CHAT_KEY = \"flowTestChat\";\n\n/** Данные нового тестового чата: `service.createChat(userId, { data: flowTestChatData() })`. */\nexport function flowTestChatData(): Record<string, unknown> {\n return { [FLOW_TEST_CHAT_KEY]: { timeOffset: 0 } };\n}\n\n/** Сдвиг часов из данных чата, мс; `null` — чат не тестовый. */\nexport function testChatOffset(data: Record<string, unknown> | undefined): number | null {\n const mark = data?.[FLOW_TEST_CHAT_KEY];\n if (!mark || typeof mark !== \"object\") return null;\n const offset = (mark as { timeOffset?: unknown }).timeOffset;\n return typeof offset === \"number\" && Number.isFinite(offset) ? offset : 0;\n}\n\n/** Часы чата, мс: часы сервера плюс сдвиг тестового чата. */\nexport const chatTime = (run: Pick<FlowRun, \"timeOffset\"> | null | undefined, serverNow: number): number => serverNow + (run?.timeOffset ?? 0);\n\n/** Перемотка не удалась по понятной причине: `nothing_scheduled` — впереди сценарий сам ничего не напишет. */\nexport class FlowTestClockError extends Error {\n constructor(\n readonly code: \"nothing_scheduled\" | \"not_reached\",\n message: string,\n ) {\n super(message);\n this.name = \"FlowTestClockError\";\n }\n}\n\n/** Сколько раз подряд срок может отложиться (ночь, потолок дня, правила приложения), пока перемотка ищет отправку. */\nconst NEXT_FIRST_MESSAGE_STEPS = 50;\n\n/** Сколько ходов подряд перемотка ждёт (ход таймера и следующий за ним, например прощание). */\nconst TURN_WAIT_LIMIT = 5;\n\n/**\n * Место чата: из состояния после хода, а у чата, закреплённого без хода (`adopt`), — из записи переноса. Не закреплён и\n * хода не было — `null`.\n */\nasync function placeRun(runtime: FlowRuntime, port: FlowPort, chat: { ownerId: string; chatId: string }, flow: AgentFlow, now: number) {\n const { values } = await port.getChatState(chat.ownerId, chat.chatId);\n const stored = storedRun(values[FLOW_STATE_KEY]);\n if (stored) return stored;\n const record = await runtime.store.getChat(chat.chatId);\n return record ? runOutsideTurn(runtime, chat, flow, values, now, record) : null;\n}\n\n/** Сдвиг до начала следующего дня сценария по часам чата (этап 4, «Дни и время»). */\nasync function msToNextDay(runtime: FlowRuntime, port: FlowPort, ownerId: string, chatId: string, offset: number): Promise<number> {\n const pinned = await runtime.chatFlow(ownerId, chatId);\n const schedule = pinned?.flow.schedule;\n if (!pinned || !schedule) throw new Error(\"Перемотка до утра: у сценария не настроены «Дни и время» (schedule)\");\n const chatNow = runtime.now() + offset;\n const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, chatNow);\n if (!run) throw new Error(\"Перемотка до утра: в чате ещё не было хода, дни сценария не начались\");\n const timeZone = runtime.timeZones ? await runtime.timeZones.of(ownerId, chatId) : \"UTC\";\n return flowDay(run, chatNow, timeZone, schedule).nextDayStartAt - chatNow;\n}\n\n/** Дождаться конца хода, который начала перемотка: тестовый чат сразу показывает ответ. Служба без `subscribe` — не ждать. */\nasync function turnFinished(port: FlowPort, ownerId: string, chatId: string): Promise<void> {\n for (let turn = 0; turn < TURN_WAIT_LIMIT; turn += 1) {\n const stream = await port.subscribe?.(ownerId, chatId);\n if (!stream) return;\n for await (const _chunk of stream);\n }\n}\n\n/**\n * Промотать часы тестового чата вперёд: на `ms` или до начала следующего дня сценария (`to: \"next_day_start\"`). Сдвиг\n * в данные чата, ближайший таймер пересчитан по новым часам и, если наступил, срабатывает сразу (агент пишет первым по\n * обычным правилам); возврат — после конца этого хода. Чат не тестовый — ошибка.\n */\nexport async function advanceTestClock(input: {\n runtime: FlowRuntime;\n port: FlowPort;\n timers: FlowTimers | null;\n ownerId: string;\n chatId: string;\n clock: FlowTestClockInput;\n}): Promise<FlowTestClockResult> {\n const { runtime, port, timers, ownerId, chatId, clock } = input;\n const offset = testChatOffset(await port.getChatData(ownerId, chatId));\n if (offset === null) throw new Error(`Чат ${chatId} не тестовый: часы перематываются только у тестовых чатов`);\n if (clock.to === \"next_first_message\") return advanceToNextFirstMessage({ runtime, port, timers, ownerId, chatId, offset });\n const ms = clock.to === \"next_day_start\" ? await msToNextDay(runtime, port, ownerId, chatId, offset) : (clock.ms ?? Number.NaN);\n if (!Number.isFinite(ms) || ms <= 0) throw new Error(\"Перемотка часов: нужен сдвиг вперёд, мс больше нуля\");\n const timeOffset = offset + ms;\n await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });\n\n const pinned = timers && (await runtime.chatFlow(ownerId, chatId));\n if (!timers || !pinned) return { timeOffset };\n const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);\n // Хода ещё не было и чат не закреплён: таймеров нет, сдвиг подхватит первый ход.\n if (!run) return { timeOffset };\n await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });\n await timers.tick();\n await turnFinished(port, ownerId, chatId);\n return { timeOffset };\n}\n\nconst nothingScheduled = (chatId: string) =>\n new FlowTestClockError(\"nothing_scheduled\", `Чат ${chatId}: впереди сценарий сам ничего не напишет — перематывать не к чему`);\n\n/**\n * Перемотка к моменту, когда сценарий напишет первым. Сдвиг — ровно до срока таймера; срабатывание проверяет те же\n * правила, что и без перемотки. Срок отложился (ночь, потолок дня, три часа после сообщения человека) — следующий шаг до\n * нового срока, пока не уйдёт сообщение, не выйдет срок разговора или «не завершил».\n */\nasync function advanceToNextFirstMessage(input: {\n runtime: FlowRuntime;\n port: FlowPort;\n timers: FlowTimers | null;\n ownerId: string;\n chatId: string;\n offset: number;\n}): Promise<FlowTestClockResult> {\n const { runtime, port, timers, ownerId, chatId } = input;\n const pinned = timers && (await runtime.chatFlow(ownerId, chatId));\n if (!timers?.watch || !pinned) throw nothingScheduled(chatId);\n let timeOffset = input.offset;\n for (let step = 0; step < NEXT_FIRST_MESSAGE_STEPS; step += 1) {\n const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);\n if (!run) throw nothingScheduled(chatId);\n // Первый шаг пересчитывает срок по текущему сдвигу; дальше срок в хранилище — уже отложенный таймером (ночь, правила).\n if (step === 0) await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });\n const record = await runtime.store.getChat(chatId);\n if (!record || record.endedAt !== null || record.dueAt === null) throw nothingScheduled(chatId);\n const serverNow = runtime.now();\n const shift = Math.max(0, Date.parse(record.dueAt) - serverNow);\n if (shift > 0) {\n timeOffset += shift;\n await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });\n // Срок в хранилище — по часам сервера: с новым сдвигом он наступает сейчас. Выход ноды и ветка остаются его.\n await runtime.store.setDue(chatId, { at: new Date(serverNow).toISOString(), exitId: record.dueExitId ?? FLOW_FIRST_EXIT, branchHead: record.dueBranchHead });\n }\n let reason: FlowTestClockReason | null = null;\n const unwatch = timers.watch(chatId, (fired) => (reason ??= fired));\n try {\n await timers.tick();\n await turnFinished(port, ownerId, chatId);\n } finally {\n unwatch();\n }\n if (reason) return { timeOffset, reason };\n }\n throw new FlowTestClockError(\"not_reached\", `Чат ${chatId}: срок сообщения откладывался ${NEXT_FIRST_MESSAGE_STEPS} раз подряд — перемотка остановлена`);\n}\n","import type { ClientFact, FlowStore } from \"../store/flow-store\";\nimport type { ClientCardStore, ClientFactStore } from \"./flow-types\";\n\n/** Факт в таблице карточки модуля — запись с `fieldId` = `fact:<id>`; приложение не должно называть так свои поля. */\nexport const FACT_PREFIX = \"fact:\";\nexport const MAX_FACTS = 30;\nexport const MAX_FACT_LENGTH = 200;\n\nconst normalized = (text: string) => text.trim().toLowerCase().replace(/\\s+/g, \" \");\n\n/** Тот же факт (с точностью до регистра и пробелов) — тот же id: `f` и восемь шестнадцатеричных цифр. */\nexport function factId(text: string): string {\n let hash = 0x811c9dc5;\n for (const char of normalized(text)) {\n hash ^= char.codePointAt(0)!;\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return `f${hash.toString(16).padStart(8, \"0\")}`;\n}\n\nexport const sameFact = (left: string, right: string) => normalized(left) === normalized(right);\n\nexport const isFactEntry = (fieldId: string) => fieldId.startsWith(FACT_PREFIX);\n\n/** Факты по времени записи, раньше — выше: начало блока для модели не меняется, пока не пишут новое. */\nexport const byWriteOrder = (left: ClientFact, right: ClientFact) => left.updatedAt.localeCompare(right.updatedAt) || left.id.localeCompare(right.id);\n\n/** Факты в таблице модуля: рядом с полями карточки, под префиксом. Своей таблицы и миграции не нужно. */\nfunction moduleFacts(store: FlowStore): ClientFactStore {\n const read = async (ownerId: string): Promise<ClientFact[]> => {\n const entries = await store.readCard(ownerId);\n return entries\n .filter((entry) => isFactEntry(entry.fieldId))\n .map((entry) => ({ id: entry.fieldId.slice(FACT_PREFIX.length), text: String(entry.value), updatedAt: entry.updatedAt, chatId: entry.chatId, flowId: entry.flowId }))\n .sort(byWriteOrder);\n };\n return {\n read,\n add: ({ ownerId, fact, chatId, flowId, at }) => store.writeCardField({ ownerId, fieldId: `${FACT_PREFIX}${fact.id}`, value: fact.text, chatId, flowId, at: at.toISOString() }),\n async forget(ownerId, id) {\n if (id !== \"all\") return store.forgetCard(ownerId, `${FACT_PREFIX}${id}`);\n for (const fact of await read(ownerId)) await store.forgetCard(ownerId, `${FACT_PREFIX}${fact.id}`);\n },\n };\n}\n\n/** Факты человека: у приложения (`clientCard.facts`) или в таблице модуля. */\nexport function clientFactStore(store: FlowStore, adapter?: ClientCardStore): ClientFactStore {\n const own = adapter?.facts;\n if (!own) return moduleFacts(store);\n return {\n read: async (ownerId) => [...(await own.read(ownerId))].sort(byWriteOrder),\n add: (input) => own.add(input),\n forget: (ownerId, id) => own.forget(ownerId, id),\n };\n}\n\n/** Факт как запись карточки: так его видят `readClientCard` и экран («Что агент знает о вас»). */\nexport const factEntry = (fact: ClientFact) => ({ fieldId: `${FACT_PREFIX}${fact.id}`, value: fact.text, updatedAt: fact.updatedAt, chatId: fact.chatId, flowId: fact.flowId });\n","/** Ход «пишет первым» не начался или приложение сказало «не сейчас» (`canWriteNow` → `false`): касание — через час. */\nexport const FIRST_MESSAGE_RETRY_MS = 3_600_000;\n/** `canWriteNow` → `{ retryAt }` в прошлом: повтор не чаще, чем раз в минуту. */\nexport const FIRST_MESSAGE_MIN_RETRY_MS = 60_000;\n","import type { FlowSchedule } from \"../format/flow-format\";\nimport type { CanWriteNow, FirstMessagesRules, FirstMessagesSetting } from \"../engine/flow-run\";\nimport { firstMessageSlot, localDay, resolveFirstMessages } from \"../engine/first-messages\";\nimport { nextDayStartAfter } from \"../engine/flow-days\";\nimport { dayClosedToday } from \"../engine/day-frame\";\nimport type { FlowChatRecord, FlowStore } from \"../store/flow-store\";\nimport type { FirstMessageRules, FlowPeople } from \"./flow-types\";\nimport { FIRST_MESSAGE_MIN_RETRY_MS, FIRST_MESSAGE_RETRY_MS } from \"./first-message-retry\";\n\n/** Что в записи чата запрещает писать первым. */\nexport type FlowChatWriteState = Pick<FlowChatRecord, \"dayClosedOn\" | \"pausedUntil\" | \"windowClosed\">;\n\n/**\n * Запрет из записи чата, до правил приложения: окно канала закрыто — молчать до сообщения человека (`retryAt: null`);\n * пауза сотрудника — до её конца; агент закрыл день — до начала следующего. Нет запрета — `null`.\n */\nexport function chatWriteBlock(chat: FlowChatWriteState, at: number, timeZone: string, schedule: FlowSchedule | undefined): { retryAt: number | null } | null {\n if (chat.windowClosed) return { retryAt: null };\n const pausedUntil = chat.pausedUntil ? Date.parse(chat.pausedUntil) : 0;\n if (pausedUntil > at) return { retryAt: pausedUntil };\n if (schedule && dayClosedToday(chat.dayClosedOn, at, timeZone, schedule)) return { retryAt: nextDayStartAfter(at, timeZone, schedule) };\n return null;\n}\n\n/** Итоговые правила: ночь и потолок сценария главнее настроек приложения, те — значений SDK. */\nfunction scenarioRules(rules: FirstMessagesRules, schedule: FlowSchedule | undefined, programDay: number | undefined): Omit<FirstMessagesRules, \"canWriteNow\"> {\n if (!schedule) return rules;\n return {\n quietHours: { from: schedule.nightFrom ?? rules.quietHours.from, to: schedule.dayStart },\n maxPerDay: capOfProgramDay(schedule.firstMessages?.maxPerDayByDay, programDay) ?? schedule.firstMessages?.maxPerDay ?? rules.maxPerDay,\n };\n}\n\n/** Потолок дня программы из `maxPerDayByDay`: вечер до первого дня — как день 1, после списка — его последнее значение. */\nexport function capOfProgramDay(byDay: number[] | undefined, programDay: number | undefined): number | undefined {\n if (!byDay?.length) return undefined;\n return byDay[Math.min(Math.max(programDay ?? 1, 1), byDay.length) - 1];\n}\n\n/** Правила «писать первым» на хранилище счётчиков; `false` в настройках — `null`, агент пишет первым без ограничений. */\nexport function createFirstMessageRules(setting: FirstMessagesSetting | undefined, store: FlowStore): FirstMessageRules | null {\n const rules = resolveFirstMessages(setting);\n if (!rules) return null;\n return {\n async check({ ownerId, chatId, at, timeZone, schedule, programDay, chat }) {\n const blocked = chat ? chatWriteBlock(chat, at, timeZone, schedule) : null;\n if (blocked) return { allowed: false, retryAt: blocked.retryAt };\n const sentToday = await store.countFirstMessages(ownerId, localDay(at, timeZone));\n const slot = firstMessageSlot(scenarioRules(rules, schedule, programDay), { at, timeZone, sentToday });\n if (!slot.allowed) return slot;\n // Приложение спрашивается только тогда, когда остальные правила уже разрешили.\n const answer = rules.canWriteNow ? await rules.canWriteNow({ ownerId, chatId, at: new Date(at) }) : true;\n if (answer === true) return { allowed: true };\n // Конец разговора от ответа не зависит: повтор только у касаний.\n return { allowed: false, retryAt: retryAtOf(answer, at) };\n },\n async record({ ownerId, at, timeZone }) {\n await store.addFirstMessage(ownerId, localDay(at, timeZone));\n },\n };\n}\n\n/**\n * Когда спросить снова после «нет» приложения: `\"never\"` — до сообщения человека (`null`); `false` и негодная дата — через\n * час; дата в прошлом — не раньше чем через минуту, а не каждый обход.\n */\nfunction retryAtOf(answer: Exclude<CanWriteNow, true>, at: number): number | null {\n if (answer === \"never\") return null;\n const retryAt = typeof answer === \"object\" && answer !== null && answer.retryAt instanceof Date ? answer.retryAt.getTime() : Number.NaN;\n if (!Number.isFinite(retryAt)) return at + FIRST_MESSAGE_RETRY_MS;\n return Math.max(retryAt, at + FIRST_MESSAGE_MIN_RETRY_MS);\n}\n\n// ---------- Пояс человека → В10: страница → пояс приложения → пояс сервера ----------\n\n/** Пояс чата ещё не известен: страница его не присылала. */\nexport const UNKNOWN_TIME_ZONE = \"\";\n\nexport const serverTimeZone = (): string => Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n/**\n * Пояс страницы из пояса хода. Ядро с 0.53 говорит его прямо (`pageTimeZone`). Без этого поля: ядро без пояса страницы\n * подставляет запасной (приложения или сервера), поэтому совпадение с запасным — «страница пояс не прислала»: такой пояс\n * не затирает известный пояс чата.\n */\nexport function pageTimeZone(turnZone: string, appZone: string | undefined, sent?: string | null): string | null {\n if (sent !== undefined) return sent;\n return turnZone === (appZone ?? serverTimeZone()) ? null : turnZone;\n}\n\n/**\n * Пояс чата для таймеров: известный (человека или со страницы), иначе `defaultTimeZone`, иначе приложения, иначе\n * сервера — с предупреждением один раз на чат.\n */\nexport function createChatTimeZones(input: { appTimeZone: () => string | undefined; defaultTimeZone?: string; warn: (message: string) => void }) {\n const warned = new Set<string>();\n return {\n of(chatId: string, stored: string): string {\n if (stored) return stored;\n if (input.defaultTimeZone) return input.defaultTimeZone;\n const app = input.appTimeZone();\n if (app) return app;\n const server = serverTimeZone();\n if (!warned.has(chatId)) {\n warned.add(chatId);\n input.warn(`[сценарий] чат=${chatId}: пояс человека неизвестен (нет ни со страницы, ни defaultTimeZone, ни agent.timeZone) — тихие часы и «время суток» по поясу сервера ${server}`);\n }\n return server;\n },\n };\n}\n\n/** Пояс IANA в каноническом виде («asia/novosibirsk» → «Asia/Novosibirsk»); не пояс — `null`. */\nexport function ianaTimeZone(value: string): string | null {\n if (!value.trim()) return null;\n try {\n return new Intl.DateTimeFormat(\"en-US\", { timeZone: value.trim() }).resolvedOptions().timeZone;\n } catch {\n return null;\n }\n}\n\n/** Пояс человека: один на все его разговоры. Копия в записи чата — для таймеров, которые читают её без ожидания. */\nexport interface FlowTimeZones {\n /** Известный пояс: записанный человеку, иначе узнанный в этом чате. Не знаем — `null`. */\n known(ownerId: string, chatId: string): Promise<string | null>;\n /** Пояс для счёта: известный, иначе приложения или сервера. */\n of(ownerId: string, chatId: string): Promise<string>;\n /** Запасной пояс чата, пока пояс человека неизвестен: приложения или сервера. */\n fallback(chatId: string): string;\n /** Пояс узнан (страница, слова человека): человеку и копия в запись чата. */\n write(ownerId: string, chatId: string, timeZone: string): Promise<void>;\n}\n\n/** Пояса людей: адаптер приложения `people` или таблица модуля; запасной — `fallback` (приложения или сервера). */\nexport function createFlowTimeZones(input: { store: FlowStore; people?: FlowPeople; fallback: (chatId: string) => string; now: () => number }): FlowTimeZones {\n const { store } = input;\n const iso = () => new Date(input.now()).toISOString();\n const readPerson = (ownerId: string) => (input.people ? input.people.readTimeZone(ownerId) : store.readTimeZone(ownerId));\n const writePerson = (ownerId: string, timeZone: string) => (input.people ? input.people.writeTimeZone(ownerId, timeZone) : store.writeTimeZone(ownerId, timeZone, iso()));\n async function known(ownerId: string, chatId: string): Promise<string | null> {\n const person = await readPerson(ownerId);\n if (person) return person;\n return (await store.getChat(chatId))?.timeZone || null;\n }\n return {\n known,\n fallback: input.fallback,\n async of(ownerId, chatId) {\n return (await known(ownerId, chatId)) ?? input.fallback(chatId);\n },\n async write(ownerId, chatId, timeZone) {\n if ((await readPerson(ownerId)) !== timeZone) await writePerson(ownerId, timeZone);\n const record = await store.getChat(chatId);\n if (record && record.timeZone !== timeZone) await store.updateChat(chatId, { timeZone }, iso());\n },\n };\n}\n","import type { AgentFlow } from \"../format/flow-format\";\nimport type { FlowFirstReason } from \"../engine/nudges\";\nimport type { FlowRun } from \"../engine/flow-run\";\nimport { stateById } from \"../engine/flow-graph\";\nimport type { FlowNotice } from \"./flow-types\";\nimport { describeCondition, exitById } from \"./flow-events\";\nimport { FLOW_TIMER_NOTICE } from \"./prepare-turn\";\n\n// Один ход «пишет первым» на все причины одного срока (FR-304). Строки постоянные: служебное сообщение идёт в хвост\n// хода, постоянная инструкция не меняется (кэш модели).\n\nconst REASON_LINES = {\n morning: \"Начался новый день сценария: уместно доброе утро.\",\n silence: (step: number) => `Человек молчит после твоего сообщения (касание ${step}).`,\n daily: (at: string) => `Касание по расписанию, ${at}.`,\n nightMessages: (count: number) => `Человек накопил сообщения без ответа (${count}): ответь на них.`,\n} as const;\n\nconst TASK =\n \"Ты пишешь человеку первым. Напиши одно короткое сообщение: новое и полезное для человека по теме разговора, без повтора уже сказанного и без упрёков за молчание.\";\n\n/** Строка переходов: «Сработало: тишина 3 ч» — как в 0.48. */\nconst transitionLine = (flow: AgentFlow, exitId: string) => {\n const exit = exitById(flow, exitId);\n return `Сработало: ${exit ? describeCondition(flow, exit.when) : exitId}`;\n};\n\nfunction reasonLine(reason: FlowFirstReason): string | null {\n switch (reason.kind) {\n case \"morning\":\n return REASON_LINES.morning;\n case \"silence\":\n return REASON_LINES.silence(reason.step);\n case \"daily\":\n return REASON_LINES.daily(reason.at);\n case \"night_messages\":\n return REASON_LINES.nightMessages(reason.count);\n default:\n return null;\n }\n}\n\n/** Сколько человек молчал к ходу «пишет первым»: от его последнего сообщения по часам чата; не писал — `undefined`. */\nexport function silentMsOf(run: Pick<FlowRun, \"lastHumanAt\">, at: number): number | undefined {\n return run.lastHumanAt === null ? undefined : Math.max(0, at - run.lastHumanAt);\n}\n\n/**\n * Служебное сообщение хода «пишет первым». Только переход ноды — прежнее «Сработало: …» (как в 0.48); есть другие\n * причины — их список и задача «новое и полезное». `data.exitId` — переход, который применит ход; `data.nudgeStep` —\n * шаг ряда касаний после хода (после перехода и утреннего — 0, ряд заново); `data.nudgeStepOwn` — его насчитал свой ряд ноды на N дней (`ownNudge`).\n * Для строки на экране (право `appNotices`, `flowNoticeLines`): `data.silentMs` — сколько человек молчал, `data.exit` —\n * нода, куда ведёт переход, и условие срока. Модель их не видит: текст для неё — `title`.\n */\nexport function firstReasonsNotice(\n flow: AgentFlow,\n reasons: readonly FlowFirstReason[],\n exitId: string | null,\n ownNudge = false,\n silentMs?: number,\n): FlowNotice {\n const silence = reasons.find((reason) => reason.kind === \"silence\");\n const moved = exitId !== null && reasons.some((reason) => reason.kind === \"transition\");\n // Утреннее — новая точка отсчёта: ряд тишины после него с первого шага, каждый день заново.\n const morning = reasons.some((reason) => reason.kind === \"morning\");\n const nudgeStep = moved || morning ? 0 : silence?.kind === \"silence\" ? silence.step : undefined;\n const own = ownNudge && silence !== undefined && !moved && !morning;\n const exit = moved ? exitOnScreen(flow, exitId, reasons) : undefined;\n const data = {\n ...(moved && { exitId }),\n reasons: [...reasons],\n ...(nudgeStep !== undefined && { nudgeStep }),\n ...(own && { nudgeStepOwn: true }),\n ...(silentMs !== undefined && { silentMs }),\n ...(exit && { exit }),\n };\n const others = reasons.flatMap((reason) => reasonLine(reason) ?? []);\n if (others.length === 0 && moved) return { kind: \"app\", name: FLOW_TIMER_NOTICE, title: transitionLine(flow, exitId), data };\n const lines = [...(moved ? [`${transitionLine(flow, exitId)}.`] : []), ...others, TASK];\n return { kind: \"app\", name: FLOW_TIMER_NOTICE, title: lines.join(\"\\n\"), data };\n}\n\n/** Переход для строки на экране: название ноды, куда он ведёт, и условие срока. */\nfunction exitOnScreen(flow: AgentFlow, exitId: string, reasons: readonly FlowFirstReason[]) {\n const to = reasons.find((reason) => reason.kind === \"transition\");\n const state = to?.kind === \"transition\" ? stateById(flow, to.stateId) : undefined;\n const when = exitById(flow, exitId)?.when;\n if (!state && !when) return undefined;\n return { ...(state && { stateTitle: state.title }), ...(when && { when }) };\n}\n","import type { AgentFlow, FlowAfterEnd } from \"../format/flow-format\";\nimport type { FlowEndReason, FlowRun, Transition } from \"../engine/flow-run\";\nimport { isFinal, stateById } from \"../engine/flow-graph\";\nimport { applyTransition } from \"../engine/run-updates\";\nimport { flowDay } from \"../engine/flow-days\";\nimport { FLOW_DEADLINE_EXIT } from \"../engine/timers\";\nimport type { FirstMessageRules, FlowFinish, FlowPort } from \"./flow-types\";\nimport type { ChatFlowRun, FlowRuntime, PinnedFlow } from \"./flow-events\";\nimport type { FlowDelivery } from \"./flow-delivery\";\nimport { silentMsOf } from \"./first-reasons-notice\";\n\n// Конец разговора — запись `endedAt/endReason`, событие `onFinish`, прощание при сроке. По plan.md, «Конец разговора».\n// Конец — точка передачи, а не тупик: событие несёт всё, что нужно передать человека дальше (место с заметками чата,\n// карточка — по `ownerId`), а что делать с сообщением после конца, решает одно место — `afterEndOf`.\n\n/** Имя служебного сообщения `app` прощального хода при «срок вышел». */\nexport const FLOW_DEADLINE_NOTICE = \"flow_deadline\";\n\nconst DEADLINE_REASON = { kind: \"timer\", text: \"срок вышел\" } as const;\nconst FAREWELL_NOTICE = \"Срок программы вышел. Попрощайся с человеком одним сообщением: коротко подведи итог и скажи, что дальше.\";\n\n/**\n * Что делать с сообщением человека после конца (→ В6). Единственная точка решения: позже здесь встанет передача\n * следующему сценарию и признак «кто ведёт сейчас» (сценарий, рассылка, сотрудник).\n */\nexport function afterEndOf(flow: AgentFlow): FlowAfterEnd {\n return flow.afterEnd ?? \"reply\";\n}\n\n/**\n * Закрывает ли конечная нода разговор. Сценарий с «Днями и временем» или с заданным `afterEnd` — да; иначе, как в 0.48,\n * агент и дальше отвечает по карточке конечной ноды, а приложение узнаёт о ней только событием.\n */\nexport const finalEndsChat = (flow: AgentFlow) => flow.schedule !== undefined || flow.afterEnd !== undefined;\n\n/** Переход «срок вышел» в `deadlineState`; его нет, он не задан или чат уже там — `null`. */\nfunction deadlineTransition(flow: AgentFlow, run: FlowRun, at: number): Transition | null {\n const to = flow.schedule?.deadlineState;\n const state = to === undefined ? undefined : stateById(flow, to);\n if (!state || run.state === state.id) return null;\n return { from: run.state, to: state.id, exitId: FLOW_DEADLINE_EXIT, decidedBy: \"timer\", tags: state.tags, at, reason: DEADLINE_REASON };\n}\n\n/** Место чата, закрытого вне хода (таймер, перенос): переход в `deadlineState` и отметка конца. О переходе уже сообщено. */\nexport function endedRun<T extends FlowRun>(flow: AgentFlow, run: T, ended: { endedAt: number; endReason: FlowEndReason }): T {\n if (run.endedAt !== undefined) return run;\n const transition = ended.endReason === \"deadline\" ? deadlineTransition(flow, run, ended.endedAt) : null;\n const moved = transition ? (applyTransition(flow, run, transition) as T) : run;\n return { ...moved, endedAt: ended.endedAt, endReason: ended.endReason };\n}\n\nexport interface FlowDeadlineInput {\n ownerId: string;\n chatId: string;\n pinned: PinnedFlow;\n run: ChatFlowRun;\n /** Когда срок вышел, по часам чата. */\n at: number;\n timeZone: string;\n}\n\nexport interface FlowFinisher {\n /** Переходы хода: вход в конечную ноду — конец `final` (или только событие у сценария 0.48). */\n afterTransitions(input: { ownerId: string; chatId: string; pinned: PinnedFlow; run: FlowRun; transitions: readonly Transition[] }): Promise<void>;\n /** «Срок вышел»: конец `deadline`, переход в `deadlineState`, событие. Возвращает место после конца. */\n deadline(input: FlowDeadlineInput): Promise<{ run: ChatFlowRun; ended: boolean }>;\n /** Прощальный ход при «срок вышел» (→ В5): по правилам «писать первым»; готовый текст — без модели. */\n farewell(input: FlowDeadlineInput & { port: FlowPort; firstMessages: FirstMessageRules | null; delivery?: FlowDelivery }): Promise<void>;\n /** Конец вне хода: «не завершил» (3 дня тишины), «заблокировал» (исход доставки), причина приложения (`flow.end`). Один раз. */\n end(input: { ownerId: string; chatId: string; pinned: PinnedFlow; run: FlowRun; reason: FlowEndReason; outcome?: string; at: number }): Promise<{ ended: boolean }>;\n}\n\nexport function createFlowFinish(input: { runtime: FlowRuntime; onFinish?: (event: FlowFinish) => void | Promise<void> }): FlowFinisher {\n const { runtime } = input;\n\n async function report(event: { ownerId: string; chatId: string; pinned: PinnedFlow; run: FlowRun; reason: FlowEndReason; outcome?: string; at: number }): Promise<void> {\n const { onFinish } = input;\n if (!onFinish) return;\n const { schedule } = event.pinned.flow;\n let day: number | null = null;\n if (schedule) {\n const timeZone = (await runtime.timeZones?.of(event.ownerId, event.chatId)) ?? \"UTC\";\n day = flowDay(event.run, event.at, timeZone, schedule).day;\n if (schedule.lengthDays !== undefined) day = Math.min(day, schedule.lengthDays);\n }\n const { answerId: _answerId, firstReasons: _firstReasons, ...run } = event.run as ChatFlowRun;\n const finish: FlowFinish = {\n ownerId: event.ownerId,\n chatId: event.chatId,\n flowId: event.pinned.flowId,\n versionId: event.pinned.versionId,\n reason: event.reason,\n ...(event.outcome !== undefined && { outcome: event.outcome }),\n day,\n state: event.run.state,\n tags: stateById(event.pinned.flow, event.run.state)?.tags ?? [],\n run,\n at: new Date(event.at),\n };\n const work = Promise.resolve()\n .then(() => onFinish(finish))\n .catch((error) => runtime.logError(`[сценарий] чат=${event.chatId} onFinish упал: ${error instanceof Error ? error.message : String(error)}`));\n if (runtime.track) runtime.track(work);\n else await work;\n }\n\n /** Закрыть разговор в хранилище; `true` — закрыл этот вызов (уже закрытый не закрывается второй раз). */\n async function close(chatId: string, reason: FlowEndReason, at: number): Promise<boolean> {\n const before = await runtime.store.getChat(chatId);\n if (!before || before.endedAt !== null) return false;\n const after = await runtime.store.endChat(chatId, { reason, at: new Date(at).toISOString() });\n return after !== null && after.endReason === reason && Date.parse(after.endedAt ?? \"\") === at;\n }\n\n return {\n async afterTransitions({ ownerId, chatId, pinned, run, transitions }) {\n const final = transitions.find((transition) => {\n const state = stateById(pinned.flow, transition.to);\n return state !== undefined && isFinal(state);\n });\n if (!final) return;\n if (finalEndsChat(pinned.flow) && !(await close(chatId, \"final\", final.at))) return;\n await report({ ownerId, chatId, pinned, run, reason: \"final\", at: final.at });\n },\n\n async deadline({ ownerId, chatId, pinned, run, at }) {\n const ended = await close(chatId, \"deadline\", at);\n const transition = deadlineTransition(pinned.flow, run, at);\n let next: ChatFlowRun = run;\n if (ended && transition) {\n next = applyTransition(pinned.flow, run, transition) as ChatFlowRun;\n await runtime.afterTransitions({ ownerId, chatId, pinned, run: next, transitions: [transition] });\n }\n if (ended) await report({ ownerId, chatId, pinned, run: next, reason: \"deadline\", at });\n return { run: endedRun(pinned.flow, next, { endedAt: at, endReason: \"deadline\" }), ended };\n },\n\n async farewell({ ownerId, chatId, pinned, run, at, timeZone, port, firstMessages, delivery }) {\n const schedule = pinned.flow.schedule;\n if (!schedule?.farewellOnDeadline) return;\n const record = await runtime.store.getChat(chatId);\n const chat = record ? { dayClosedOn: record.dayClosedOn, pausedUntil: record.pausedUntil, windowClosed: record.windowClosed } : undefined;\n const allowed = firstMessages\n ? await firstMessages.check({ ownerId, chatId, at, timeZone, schedule, programDay: flowDay(run, at, timeZone, schedule).day, ...(chat && { chat }) })\n : { allowed: true as const };\n // Нельзя писать сейчас — прощания нет: разговор уже закрыт, таймеров после конца нет.\n if (!allowed.allowed) return;\n const text = schedule.farewellText?.trim();\n const silentMs = silentMsOf(run, at);\n const result = await port.deliverNotices({\n ownerId,\n chatId,\n notices: [{ kind: \"app\", name: FLOW_DEADLINE_NOTICE, title: FAREWELL_NOTICE, data: { reasons: [{ kind: \"farewell\" }], ...(silentMs !== undefined && { silentMs }) } }],\n wakeAgent: true,\n deliveryKey: `flow:${chatId}:deadline`,\n usageSource: \"first_message\",\n maxServerTurnsInRow: false,\n timeZone,\n ...(delivery?.own() && { awaitAnswer: true }),\n ...(text && { reply: { text } }),\n });\n if (result.status !== \"delivered\" || !result.turnStarted) return;\n if (delivery) await delivery.afterFirstTurn({ ownerId, chatId, answer: result.answer, reasons: [{ kind: \"farewell\" }], at, timeZone });\n else await firstMessages?.record({ ownerId, at, timeZone });\n },\n\n async end({ ownerId, chatId, pinned, run, reason, outcome, at }) {\n const ended = await close(chatId, reason, at);\n if (ended) await report({ ownerId, chatId, pinned, run, reason, ...(outcome !== undefined && { outcome }), at });\n return { ended };\n },\n };\n}\n","import type { AgentNotice, PrepareTurn, TurnAction } from \"@ykurilov/agent-chat-core\";\nimport { FLOW_STATE_KEY, type FlowTouch, type Transition } from \"../engine/flow-run\";\nimport { migrateRun } from \"../engine/migrate-run\";\nimport { noteAgentAnswer, noteHumanMessage, withNudgeStep, withoutUndeliveredTouch } from \"../engine/run-updates\";\nimport { advance, chainLimitMessage, currentRun, pendingEvent, transitionNotice, withReopen, type ChatFlowRun, type FlowRuntime } from \"./flow-events\";\nimport type { PendingFlowChange } from \"../store/flow-store\";\nimport { pageTimeZone } from \"./first-message-rules\";\nimport { testChatOffset } from \"./test-clock\";\nimport { dayInProgress, deadlineAt, withDayAnchor } from \"../engine/flow-days\";\nimport { endedRun, FLOW_DEADLINE_NOTICE, type FlowFinisher } from \"./flow-finish\";\nimport type { FlowFirstReason } from \"../engine/nudges\";\n\n/** Имена служебных сообщений `app`, которые сценарий применяет в начале хода. */\nexport const FLOW_TIMER_NOTICE = \"flow_timer\";\nexport const FLOW_EVENT_NOTICE = \"flow_event\";\n\n/** Ходы, которые отвечают на сообщение человека. */\nconst HUMAN_ACTIONS: ReadonlySet<TurnAction> = new Set<TurnAction>([\"send\", \"edit\", \"regenerate\", \"retry\"]);\n\n/** Переход по месту: из какой ноды и каким выходом. */\nconst transitionKey = (transition: Pick<Transition, \"from\" | \"exitId\">) => `${transition.from}\\u0000${transition.exitId}`;\n\nconst errorText = (error: unknown) => (error instanceof Error ? error.message : String(error));\n\nconst dataOf = (notice: AgentNotice): Record<string, unknown> =>\n notice.data && typeof notice.data === \"object\" ? (notice.data as Record<string, unknown>) : {};\n\n/** Причины «пишет первым» этого хода: служебные сообщения таймера и прощания. */\nconst firstReasonsOf = (notices: readonly AgentNotice[]): FlowFirstReason[] =>\n notices.flatMap((notice) => {\n if (notice.kind !== \"app\" || (notice.name !== FLOW_TIMER_NOTICE && notice.name !== FLOW_DEADLINE_NOTICE)) return [];\n const { reasons } = dataOf(notice);\n return Array.isArray(reasons) ? (reasons as FlowFirstReason[]) : [];\n });\n\n/**\n * `agent.prepareTurn` сценария: в начале хода, до застывших блоков, применяет всё, что случилось вне хода — ожидающие\n * переходы судьи (только на своей ветке), события приложения, перевод версии, наступившие таймеры, — и сообщение\n * человека («назад» после касаний). Модель получает служебное сообщение «Сработало: …» и уже новую карточку.\n */\nexport function flowPrepareTurn(runtime: FlowRuntime, appTimeZone: () => string | undefined, finisher?: () => FlowFinisher): PrepareTurn {\n return async (ctx) => {\n // В группах сценарий выключен: чат не закрепляется, состояние не пишется.\n if (ctx.audience === \"group\") return;\n let pinned = await runtime.chatFlow(ctx.ownerId, ctx.chatId, ctx.chat);\n if (!pinned) return;\n // Тестовый чат идёт по своим часам: сдвиг из данных чата ложится в место.\n const timeOffset = testChatOffset(ctx.chat.data);\n const now = runtime.now() + (timeOffset ?? 0);\n const transitions: Transition[] = [];\n let card: Record<string, unknown> | undefined;\n // Переходы, применённые в срок без хода: сообщены тогда, ход только переносит их в состояние и показывает модели.\n const applied: Transition[] = [];\n const step = async (run: ChatFlowRun, event: Parameters<typeof advance>[2], out = transitions) =>\n advance(pinned!.flow, run, event, {\n now,\n card: (card ??= await runtime.cardValues(ctx.ownerId)),\n out,\n onChainLimit: (stopped) => runtime.logError(chainLimitMessage(ctx.chatId, stopped)),\n timeZone,\n });\n\n // Пояс человека → В10: со страницы, иначе известный пояс человека; ход без пояса страницы (мессенджер) его не затирает.\n const record = await runtime.store.getChat(ctx.chatId);\n const page = pageTimeZone(ctx.timeZone, appTimeZone(), ctx.pageTimeZone);\n const known = page ?? (runtime.timeZones ? await runtime.timeZones.known(ctx.ownerId, ctx.chatId) : record?.timeZone || null);\n const timeZone = known ?? runtime.defaultTimeZone ?? ctx.timeZone;\n\n // Открыт заново вне хода (`flow.reopen`): место догоняет запись.\n let run = withReopen(pinned.flow, currentRun(pinned.flow, ctx.state, now), record);\n const previousAnswer = run.answerId;\n const pending: PendingFlowChange[] = await runtime.store.takePending(ctx.chatId);\n const appliedInTime = new Set(\n pending.flatMap((change) => (change.kind === \"transition\" && change.appliedAt !== undefined && change.branchHead === previousAnswer ? [transitionKey(change.transition)] : [])),\n );\n // Ход не записал место (сбой, срок, «Стоп») — взятое возвращается в очередь: ключи не дают двойных записей.\n let restored: Promise<unknown> | null = null;\n const restore = () => (restored ??= Promise.all(pending.map((change) => runtime.store.addPending(ctx.chatId, change))));\n const onAbort = () => void restore().catch((error) => runtime.logError(`[сценарий] чат=${ctx.chatId} ожидающее не возвращено: ${errorText(error)}`));\n if (pending.length > 0) ctx.abortSignal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n return await applyTurn();\n } catch (error) {\n await restore();\n throw error;\n } finally {\n ctx.abortSignal.removeEventListener(\"abort\", onAbort);\n }\n\n async function applyTurn() {\n // Первый день — по поясу человека: пояс узнали или сменили — якорь пересчитан (→ В1, FR-207).\n const schedule = pinned!.flow.schedule;\n if (schedule) run = withDayAnchor(run, timeZone, schedule) as ChatFlowRun;\n if (record?.endedAt) {\n // Разговор закрыли вне хода (таймер, перенос): место догоняет запись.\n run = endedRun(pinned!.flow, run, { endedAt: Date.parse(record.endedAt), endReason: record.endReason ?? \"final\" });\n } else if (schedule && finisher) {\n // Таймер срока не сработал (сервер стоял): срок закрывает разговор на первом же ходе.\n const deadline = deadlineAt(run, timeZone, schedule);\n if (deadline !== null && deadline <= now) {\n ({ run } = await finisher().deadline({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned: pinned!, run, at: deadline, timeZone }));\n }\n }\n // После конца переходов нет: ожидающее снимается, сообщение человека только считается.\n const ended = run.endedAt !== undefined;\n if (!ended && run.checkedTransition) {\n const decision = run.checkedTransition;\n const scope = { ownerId: ctx.ownerId, chatId: ctx.chatId, flowId: pinned!.flowId };\n const allowed = decision.versionId === pinned!.versionId && decision.answerId === previousAnswer && decision.transition.from === run.state && runtime.confirmations?.allows(scope, run, decision.transition.exitId);\n const { checkedTransition, ...withoutDecision } = run;\n run = withoutDecision;\n if (allowed) run = await step(run, pendingEvent(decision.transition, timeZone));\n }\n for (const change of ended ? [] : pending) {\n if (change.kind === \"migrate\") {\n // Версия в записи чата уже новая (`migrateBatch`): переводится только место в сценарии.\n const next = await runtime.chatFlow(ctx.ownerId, ctx.chatId);\n if (next) {\n run = { ...migrateRun(run, next.flow, change.map, now), answerId: previousAnswer } as ChatFlowRun;\n pinned = next;\n }\n } else if (change.kind === \"transition\") {\n // Переход судьи или таймера — только на той ветке, где он решён, и только из того же состояния.\n if (change.branchHead !== previousAnswer || run.state !== change.transition.from) continue;\n if (runtime.confirmations?.guardFor(pinned!.flowId, run.state, change.transition.exitId)) continue;\n const out: Transition[] = [];\n run = await step(run, pendingEvent(change.transition, timeZone), out);\n // Применённые в срок уже сообщены; дотянутое ходом поверх них — новое.\n for (const transition of out) (appliedInTime.has(transitionKey(transition)) ? applied : transitions).push(transition);\n } else {\n run = await step(run, { type: \"event\", event: \"app\", name: change.name });\n }\n }\n // Ход «пишет первым»: счёт до него — на случай, если его ответ не дойдёт (FR-309).\n let touchBase: Omit<FlowTouch, \"answerId\" | \"at\"> | null = null;\n for (const notice of ended ? [] : ctx.notices) {\n if (notice.kind !== \"app\") continue;\n const data = dataOf(notice);\n if (notice.name === FLOW_TIMER_NOTICE) {\n // Прошлое касание не доставлено — не засчитано: счёт как до него.\n if (typeof data.undelivered === \"string\") run = withoutUndeliveredTouch(run, new Set([data.undelivered])).run as ChatFlowRun;\n // Переход ноды (если есть) и шаг ряда касаний после него.\n if (typeof data.exitId === \"string\") run = await step(run, { type: \"timer\", exitId: data.exitId, timeZone });\n touchBase = { lastAgentAt: run.lastAgentAt, nudgeStep: run.nudgeStep ?? 0, ...(run.nudgeStepOwn && { nudgeStepOwn: true }), agentAnswers: run.agentAnswers };\n if (typeof data.nudgeStep === \"number\") run = withNudgeStep(run, data.nudgeStep, data.nudgeStepOwn === true);\n } else if (notice.name === FLOW_EVENT_NOTICE && typeof data.name === \"string\") {\n run = await step(run, { type: \"event\", event: \"app\", name: data.name });\n }\n }\n // Ответ на сообщение человека: новое, правка, «Перегенерировать» и «Повторить» — состояние до сообщения, оно\n // учитывается заново. Продолжение прежнего ответа его уже учло.\n if (!ctx.resumed && HUMAN_ACTIONS.has(ctx.action)) {\n // Человек написал — окно канала снова открыто: агент может писать первым.\n if (record?.windowClosed) await runtime.store.setWindowClosed(ctx.chatId, false);\n // Ход на пачку сообщений — в счёт каждое её сообщение.\n run = noteHumanMessage(run, now, schedule ? dayInProgress(now, timeZone, schedule) : undefined, ctx.burstMessageIds?.length ?? 1);\n if (!ended) run = await step(run, { type: \"event\", event: \"message\" });\n }\n // Ход по служебному сообщению — ход сервера: счёт подряд для мягкого потолка таймеров; человек его обнулил выше.\n if (ctx.action === \"notice\" && !ctx.resumed) run = { ...run, serverTurns: (run.serverTurns ?? 0) + 1 };\n // Продолжение после подтверждения или виджета — тот же ответ: агент ещё не ответил заново.\n if (ctx.action !== \"approval\" && ctx.action !== \"widget\") run = noteAgentAnswer(run, now);\n // Снимок касания живёт до следующего хода; продолжение того же ответа его сохраняет.\n // Причины «пишет первым» — тоже: для хвоста приложения в этом ответе.\n const { touch: earlierTouch, firstReasons: earlierReasons, ...withoutTouch } = run;\n const continued = ctx.action === \"approval\" || ctx.action === \"widget\";\n const touch = touchBase ? { answerId: ctx.messageId, at: now, ...touchBase } : continued ? earlierTouch : undefined;\n const reasons = firstReasonsOf(ctx.notices);\n const firstReasons = reasons.length > 0 ? reasons : continued ? earlierReasons : undefined;\n run = { ...withoutTouch, answerId: ctx.messageId, ...(timeOffset !== null && { timeOffset }), ...(touch && { touch }), ...(firstReasons && { firstReasons }) };\n\n if (ctx.abortSignal.aborted) {\n await restore();\n return undefined;\n }\n // Пояс узнан — человеку (общий для всех его разговоров) и копия в запись чата для таймеров.\n if (known && runtime.timeZones) await runtime.timeZones.write(ctx.ownerId, ctx.chatId, known);\n else if (record && page && record.timeZone !== page) await runtime.store.updateChat(ctx.chatId, { timeZone: page }, new Date(runtime.now()).toISOString());\n await runtime.afterTransitions({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned: pinned!, run, transitions });\n return {\n set: { [FLOW_STATE_KEY]: run },\n ...(applied.length + transitions.length > 0 && { notices: [transitionNotice(pinned!.flow, [...applied, ...transitions])] }),\n };\n }\n };\n}\n","import type { AgentFlow, FlowField } from \"../format/flow-format\";\nimport type { FlowRun } from \"../engine/flow-run\";\nimport { fieldValueFits } from \"../engine/data-conditions\";\nimport { fieldById } from \"../engine/flow-graph\";\nimport { isFieldStale, sideQuestion } from \"../engine/side-questions\";\nimport type { ClientCardEntry, ClientFact } from \"../store/flow-store\";\n\n/** Значение поля готовой строкой: для блока модели, ответа `client_card` и экрана. */\nexport function fieldValueText(value: unknown): string {\n if (typeof value === \"string\") return value.trim();\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"boolean\") return value ? \"да\" : \"нет\";\n return JSON.stringify(value);\n}\n\nfunction questionLine(flow: AgentFlow, run: FlowRun, card: Record<string, { value: unknown; updatedAt: number }>, now: number): string {\n const question = sideQuestion(flow, run, { card, now });\n const found = question && fieldById(flow, question.fieldId);\n if (!question || !found) return \"Сейчас не спрашивай ничего о человеке между делом: ответь на то, что он пишет.\";\n const { field } = found;\n const noted = `flow_note { asked: \"${field.id}\" }`;\n if (question.reason === \"learn\") return `Сначала узнай: «${field.name}» — ${field.about} Запиши ответ через flow_note.`;\n if (question.reason === \"stale\") {\n return `Давно не уточняли: «${field.name}» (сейчас записано: ${fieldValueText(card[field.id]!.value)}). Мягко сверь одним коротким вопросом к месту. Спросил — отметь ${noted}; если изменилось — запиши новое значение через flow_note.`;\n }\n return `Можно спросить между делом: «${field.name}» — ${field.about} Один короткий вопрос к месту, не в лоб. Спросил — отметь ${noted}.`;\n}\n\n/**\n * Блок `flow:client`: что известно о человеке, что устарело, о чём можно спросить между делом. Известное и заметки идут\n * по порядку полей сценария (порядок = что важнее). Значение не по типу поля считается неизвестным.\n */\nexport function clientCardBlock(input: { flow: AgentFlow; run: FlowRun; card: ClientCardEntry[]; facts?: ClientFact[]; now: number }): string {\n const { flow, run, now } = input;\n const facts = input.facts ?? [];\n if (flow.clientCard.length === 0 && flow.chatNotes.length === 0 && facts.length === 0) return \"\";\n\n const byId = new Map(input.card.map((entry) => [entry.fieldId, { value: entry.value, updatedAt: Date.parse(entry.updatedAt) }]));\n const known = (field: FlowField, value: unknown) => fieldValueFits(field, value);\n\n const cardLines = flow.clientCard.flatMap((field) => {\n const entry = byId.get(field.id);\n if (!entry || !known(field, entry.value)) return [];\n const stale = isFieldStale(field, entry.updatedAt, now) ? \" (записано давно, могло измениться)\" : \"\";\n return [`- ${field.name}: ${fieldValueText(entry.value)}${stale}`];\n });\n const noteLines = flow.chatNotes.flatMap((field) => (known(field, run.notes[field.id]) ? [`- ${field.name}: ${fieldValueText(run.notes[field.id])}`] : []));\n\n const lines = [\"Что известно о человеке (карточка клиента):\", ...(cardLines.length > 0 ? cardLines : facts.length > 0 ? [] : [\"- пока ничего не записано\"])];\n if (facts.length > 0) lines.push(\"\", \"Факты о человеке:\", ...facts.map((fact) => `- ${fact.text}`));\n if (noteLines.length > 0) lines.push(\"\", \"Заметки этого чата:\", ...noteLines);\n lines.push(\"\", questionLine(flow, run, Object.fromEntries(byId), now));\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["run"]}
1
+ {"version":3,"sources":["../src/prompt/state-card.ts","../src/runtime/flow-events.ts","../src/runtime/test-clock.ts","../src/runtime/client-facts.ts","../src/runtime/first-message-retry.ts","../src/runtime/first-message-rules.ts","../src/runtime/first-reasons-notice.ts","../src/runtime/flow-finish.ts","../src/runtime/prepare-turn.ts","../src/prompt/client-card-block.ts"],"sourcesContent":["import type { AgentFlow } from \"../format/flow-format\";\nimport type { FlowRun } from \"../engine/flow-run\";\nimport { activeExits, fieldById, isFinal, stateById } from \"../engine/flow-graph\";\n\n/** Условия словами состояния по порядку проверки (свои, потом общие): номер в карточке = место в этом списке + 1. */\nexport function judgeExits(flow: AgentFlow, stateId: string): Array<{ exitId: string; text: string }> {\n const state = stateById(flow, stateId);\n if (!state) return [];\n return activeExits(flow, state).flatMap(({ exit }) => (exit.when.type === \"judge\" ? [{ exitId: exit.id, text: exit.when.text }] : []));\n}\n\n/** Цель состояния: первая строка инструкции. */\nexport function stateGoal(instructions: string): string {\n return instructions.split(\"\\n\")[0]!.trim();\n}\n\n/**\n * Блок `flow:state`: текущее состояние, цель, условия выхода, что узнать. Зависит только от состояния и набора\n * инструментов модели хода — пока они те же, блок не меняется и в новые вопросы не дописывается (→ В9). Первого текста\n * здесь нет: его несёт переход. `present` — имена в наборе модели хода: инструментов сценария вне набора карточка не\n * называет. Нет — набор неизвестен, называются все.\n */\nexport function stateCard(flow: AgentFlow, run: FlowRun, present?: ReadonlySet<string>): string {\n const state = stateById(flow, run.state);\n if (!state) return `Состояние «${run.state}» в сценарии не найдено: веди разговор по общим правилам.`;\n const [goal = \"\", ...rest] = state.instructions.split(\"\\n\");\n const lines = [`Состояние: «${state.title}»`, `Цель: ${goal.trim()}`];\n const how = rest.join(\"\\n\").trim();\n if (how) lines.push(\"\", how);\n\n const fields = state.learn.map((id) => fieldById(flow, id)).flatMap((found) => (found ? [`- ${found.field.name}: ${found.field.about}`] : []));\n if (fields.length > 0) lines.push(\"\", \"Узнать здесь:\", ...fields);\n\n const conditions = judgeExits(flow, state.id);\n if (isFinal(state)) {\n lines.push(\"\", \"Это последнее состояние сценария: условий выхода нет.\");\n } else if (conditions.length > 0) {\n lines.push(\"\", \"Условия выхода (номер — для flow_exit):\", ...conditions.map((condition, index) => `${index + 1}. ${condition.text}`));\n }\n\n const inSet = (name: string) => !present || present.has(name);\n const tools = state.tools.filter(inSet);\n if (tools.length > 0) lines.push(\"\", `Инструменты этого состояния: ${tools.join(\", \")}.`);\n const unavailable = flow.tools.filter((name) => inSet(name) && !state.tools.includes(name));\n if (unavailable.length > 0) lines.push(`Сейчас недоступны: ${unavailable.join(\", \")}.`);\n return lines.join(\"\\n\");\n}\n","import type { Chat } from \"@ykurilov/agent-chat-core\";\nimport type { AgentFlow, FlowExit } from \"../format/flow-format\";\nimport { FLOW_CHAIN_LIMIT, FLOW_STATE_KEY, type FlowRun, type Transition } from \"../engine/flow-run\";\nimport { stateById } from \"../engine/flow-graph\";\nimport { describeCondition, nextTransition, type FlowEvent } from \"../engine/next-transition\";\nimport { applyTransition, reopenRun, startRun } from \"../engine/run-updates\";\nimport { dayInProgress } from \"../engine/flow-days\";\nimport { overdueOnEntry } from \"../engine/timers\";\nimport { stateCard } from \"../prompt/state-card\";\nimport type { FlowChatRecord, FlowStore, PendingFlowChange } from \"../store/flow-store\";\nimport type { FlowNotice, FlowTransitionEvent } from \"./flow-types\";\nimport type { FlowFirstReason } from \"../engine/nudges\";\nimport type { FlowTimeZones } from \"./first-message-rules\";\n\n/**\n * `FlowRun` в состоянии чата: плюс ответ агента, в начале которого он записан. По нему ожидающий переход судьи узнаёт\n * свою ветку: правка или «Переделать» начинают ветку с другим ответом — переход снимается.\n */\nexport type ChatFlowRun = FlowRun & {\n /** Почему агент пишет первым в ответе `answerId`; ход на сообщение человека — нет. Для хвоста приложения. */\n firstReasons?: FlowFirstReason[];\n};\n\n/** Чат закреплён за версией сценария. */\nexport interface PinnedFlow {\n flowId: string;\n versionId: string;\n flow: AgentFlow;\n /** Разговор закончен (`endChat`): инструкция «после конца» вместо правил сценария. */\n ended?: boolean;\n}\n\n/** Что общего у частей сценария в службе чата: хранилище, часы, сценарий чата и отчёт о переходах. */\nexport interface FlowRuntime {\n confirmations?: import(\"./confirmations\").FlowConfirmations;\n store: FlowStore;\n now(): number;\n logError(message: string): void;\n /** Сценарий чата; с `chat` закрепляет незакреплённый чат за версией (`loadFlow`). `null` — чат без сценария. */\n chatFlow(ownerId: string, chatId: string, chat?: Chat): Promise<PinnedFlow | null>;\n /** Фоновая работа, которую ждёт `settled`; нет — не отслеживается (подделки в тестах). */\n track?(work: Promise<unknown>): void;\n /** Значения карточки клиента по `id` полей: для условий по данным. */\n cardValues(ownerId: string): Promise<Record<string, unknown>>;\n /** Пояс человека; нет — пояс хода (подделки в тестах). */\n timeZones?: FlowTimeZones;\n /** `AgentFlowOptions.defaultTimeZone`: пояс хода без страницы и без известного пояса человека — он, а не пояс службы. */\n defaultTimeZone?: string;\n /** Переходы случились: копия состояния в хранилище, `onTransition`. Не бросает. */\n afterTransitions(input: { ownerId: string; chatId: string; pinned: PinnedFlow; run: FlowRun; transitions: Transition[] }): Promise<void>;\n}\n\n/** Сообщить приложению о переходе (`onTransition`); ошибка приложения не ломает ход. */\nexport async function reportTransition(\n handler: ((event: FlowTransitionEvent) => void | Promise<void>) | undefined,\n event: FlowTransitionEvent,\n logError: (message: string) => void,\n): Promise<void> {\n if (!handler) return;\n try {\n await handler(event);\n } catch (error) {\n logError(`[сценарий] чат=${event.chatId} onTransition ${event.from} → ${event.to} упал: ${error instanceof Error ? error.message : String(error)}`);\n }\n}\n\n/** `FlowRun` из состояния чата; нет или не то — `null`. */\nexport function storedRun(value: unknown): ChatFlowRun | null {\n return value && typeof value === \"object\" && typeof (value as FlowRun).state === \"string\" ? (value as ChatFlowRun) : null;\n}\n\n/**\n * Разговор открыт заново (`flow.reopen`), а место в состоянии чата ещё «до»: оно догоняет отметку записи — конец снят,\n * ряд касаний с первого шага от открытия. Уже догнало, не открывали или снова закончен — как есть.\n */\nexport function withReopen<T extends FlowRun>(flow: AgentFlow, run: T, record: Partial<Pick<FlowChatRecord, \"reopenedAt\" | \"endedAt\">> | null | undefined): T {\n if (!record?.reopenedAt || record.endedAt) return run;\n const at = Date.parse(record.reopenedAt);\n return run.reopenedAt === at ? run : reopenRun(flow, run, at);\n}\n\n/** Где чат сейчас: из состояния ветки, а на пустой ветке — старт сценария. */\nexport function currentRun(flow: AgentFlow, values: Record<string, unknown> | undefined, now: number): ChatFlowRun {\n return storedRun(values?.[FLOW_STATE_KEY]) ?? startRun(flow, now);\n}\n\n/**\n * Куда перенесли чат при закреплении (`loadFlow`, `adopt`): состояние и моменты, от которых считаются сроки, а у идущего\n * чата — последние слова обеих сторон, шаг касаний и счёт сообщений человека за день. Нет поля — как у нового чата.\n */\nexport interface ChatPlacement {\n stateId?: string;\n startedAt?: number;\n enteredAt?: number;\n lastHumanAt?: number;\n lastAgentAt?: number;\n nudgeStep?: number;\n /** Сообщения человека за день сценария к моменту `at`. */\n humanToday?: { at: number; count: number };\n}\n\n/**\n * Место чата, перенесённого в заданное состояние: путь начинается с него, сроки таймеров — от `enteredAt` (нет — от\n * `startedAt`). Счёт дня ложится в день сценария по поясу `timeZone`; пояса нет — счёт не переносится.\n */\nexport function placedRun(flow: AgentFlow, placement: ChatPlacement, now: number, timeZone?: string): ChatFlowRun {\n const start = startRun(flow, now);\n const state = (placement.stateId !== undefined && stateById(flow, placement.stateId)) || stateById(flow, flow.start)!;\n const startedAt = placement.startedAt ?? now;\n const enteredAt = placement.enteredAt ?? placement.startedAt ?? now;\n return {\n ...start,\n state: state.id,\n startedAt,\n enteredAt,\n path: [{ state: state.id, at: enteredAt, tags: state.tags, from: null, exitId: null, decidedBy: \"start\" }],\n ...(placement.lastHumanAt !== undefined && { lastHumanAt: placement.lastHumanAt }),\n ...(placement.lastAgentAt !== undefined && { lastAgentAt: placement.lastAgentAt }),\n ...(placement.nudgeStep !== undefined && { nudgeStep: placement.nudgeStep }),\n ...(placement.humanToday && flow.schedule && timeZone && { dayCounts: { [dayInProgress(placement.humanToday.at, timeZone, flow.schedule)]: placement.humanToday.count } }),\n };\n}\n\nconst msOf = (value: string | undefined) => (value === undefined ? undefined : Date.parse(value));\n\n/** Перенос из записи чата; первый ход его уже записал в состояние (или переноса не было) — `null`. */\nexport function recordPlacement(record: Pick<FlowChatRecord, \"stateId\" | \"placement\">): ChatPlacement | null {\n const placement = record.placement;\n if (!placement) return null;\n const lastHumanAt = msOf(placement.lastHumanAt);\n const lastAgentAt = msOf(placement.lastAgentAt);\n return {\n stateId: record.stateId,\n startedAt: Date.parse(placement.startedAt),\n enteredAt: Date.parse(placement.enteredAt),\n ...(lastHumanAt !== undefined && { lastHumanAt }),\n ...(lastAgentAt !== undefined && { lastAgentAt }),\n ...(placement.nudgeStep !== undefined && { nudgeStep: placement.nudgeStep }),\n ...(placement.humanToday && { humanToday: { at: Date.parse(placement.humanToday.at), count: placement.humanToday.count } }),\n };\n}\n\n/**\n * Где чат сейчас вне хода: из состояния ветки; до первого хода перенесённого чата — из записи переноса; иначе старт.\n * Так таймеры, `admit` и `readRun` видят перенесённое место, не дожидаясь хода человека.\n */\nexport function chatRun(\n flow: AgentFlow,\n values: Record<string, unknown> | undefined,\n record: (Pick<FlowChatRecord, \"stateId\" | \"placement\"> & Partial<Pick<FlowChatRecord, \"reopenedAt\" | \"endedAt\">>) | null | undefined,\n now: number,\n timeZone?: string,\n): ChatFlowRun {\n const stored = storedRun(values?.[FLOW_STATE_KEY]);\n if (stored) return withReopen(flow, stored, record);\n const placement = record ? recordPlacement(record) : null;\n return placement ? placedRun(flow, placement, now, timeZone) : startRun(flow, now);\n}\n\n/**\n * Пояс человека для шага движка вне подготовки хода, как в `runOutsideTurn`: пояса модуля, без них — из записи чата;\n * неизвестен — `defaultTimeZone`, иначе сервера.\n */\nexport async function personTimeZone(runtime: Pick<FlowRuntime, \"store\" | \"timeZones\" | \"defaultTimeZone\">, chat: { ownerId: string; chatId: string }): Promise<string> {\n const known = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : (await runtime.store.getChat(chat.chatId))?.timeZone;\n return known || runtime.defaultTimeZone || Intl.DateTimeFormat().resolvedOptions().timeZone;\n}\n\n/** `chatRun` для частей сценария вне хода: запись чата (если не передана) и пояс человека читаются тут. */\nexport async function runOutsideTurn(\n runtime: Pick<FlowRuntime, \"store\" | \"timeZones\">,\n chat: { ownerId: string; chatId: string },\n flow: AgentFlow,\n values: Record<string, unknown> | undefined,\n now: number,\n record?: FlowChatRecord | null,\n): Promise<ChatFlowRun> {\n const found = record === undefined ? await runtime.store.getChat(chat.chatId) : record;\n const stored = storedRun(values?.[FLOW_STATE_KEY]);\n if (stored) return withReopen(flow, stored, found);\n if (!found?.placement) return startRun(flow, now);\n const timeZone = runtime.timeZones ? await runtime.timeZones.of(chat.ownerId, chat.chatId) : found.timeZone || undefined;\n return chatRun(flow, values, found, now, timeZone);\n}\n\n/**\n * Шаг движка с проверкой нового состояния: условия по данным и наступившие таймеры, чей срок не зависит от входа\n * (`overdueOnEntry`). Переходы складываются в `out`. `timeZone` — пояс человека: по нему считаются дни программы.\n */\nexport function advance(\n flow: AgentFlow,\n run: ChatFlowRun,\n event: FlowEvent,\n input: { now: number; card: Record<string, unknown>; out: Transition[]; onChainLimit: (run: FlowRun) => void; timeZone: string },\n): ChatFlowRun {\n let current = run;\n let step: FlowEvent | null = event;\n let entered = false;\n let stopped = false;\n while (step) {\n const transition = nextTransition(flow, current, step, input.now);\n if (!transition) {\n // Движок молчит из-за предела переходов подряд, а без предела переход был бы: сценарий зациклился. Одна запись.\n if (current.chain >= FLOW_CHAIN_LIMIT && nextTransition(flow, { ...current, chain: 0 }, step, input.now)) {\n if (!stopped) input.onChainLimit(current);\n stopped = true;\n }\n // Вошли в состояние, а его срок уже наступил («с дня 3», вошли в день 4): дальше сразу, человек получит один ответ.\n const overdue: string | null = entered && !stopped && step.type === \"data\" ? overdueOnEntry(flow, current, input.now, input.timeZone) : null;\n step = overdue ? { type: \"timer\", exitId: overdue, timeZone: input.timeZone } : null;\n continue;\n }\n current = applyTransition(flow, current, transition);\n input.out.push(transition);\n entered = true;\n // В новом состоянии могли уже выполниться условия по данным: поле узнали раньше.\n step = { type: \"data\", card: input.card };\n }\n return current;\n}\n\n/** Событие движка для ожидающего перехода: таймер проверяется заново по своему сроку, судья — как вердикт. */\nexport function pendingEvent(transition: Transition, timeZone: string): FlowEvent {\n if (transition.decidedBy === \"timer\") return { type: \"timer\", exitId: transition.exitId, timeZone };\n return { type: \"verdict\", exitId: transition.exitId, probability: transition.probability ?? 1, decidedBy: \"judge\" };\n}\n\n/**\n * Ожидающее вне хода поверх места ветки — каким место станет к ходу: переходы своей ветки из того же состояния и\n * события приложения. Само ожидание не трогается.\n */\nexport function withPending(flow: AgentFlow, run: ChatFlowRun, pending: readonly PendingFlowChange[], input: Parameters<typeof advance>[3]): ChatFlowRun {\n let current = run;\n for (const change of pending) {\n if (change.kind === \"transition\") {\n if (change.branchHead !== run.answerId || current.state !== change.transition.from) continue;\n current = advance(flow, current, pendingEvent(change.transition, input.timeZone), input);\n } else if (change.kind === \"event\") {\n current = advance(flow, current, { type: \"event\", event: \"app\", name: change.name }, input);\n }\n }\n return current;\n}\n\n/** Строка журнала про зацикливание. */\nexport const chainLimitMessage = (chatId: string, run: FlowRun) =>\n `[сценарий] чат=${chatId} ${FLOW_CHAIN_LIMIT} переходов подряд без ответа агента, движок остановлен в «${run.state}»`;\n\n/** Условие выхода по `id`: своё у состояния или общее. */\nexport function exitById(flow: AgentFlow, exitId: string): FlowExit | undefined {\n for (const state of flow.states) {\n const found = state.exits.find((exit) => exit.id === exitId);\n if (found) return found;\n }\n return flow.anyState.exits.find((exit) => exit.id === exitId);\n}\n\nexport { describeCondition };\n\n/** Точный первый текст состояния: агент пишет его слово в слово, без модели, когда это возможно. */\nexport function exactFirstText(flow: AgentFlow, stateId: string): string | null {\n return stateById(flow, stateId)?.firstMessage ?? null;\n}\n\nconst sayExactly = (text: string) => `Первое сообщение этого состояния — скажи ровно это: «${text}»`;\n\n/** Служебное сообщение модели о переходах вне её ответа: «Сработало: тишина 3 ч → «Напоминание»». */\nexport function transitionNotice(flow: AgentFlow, transitions: Transition[]): FlowNotice {\n const lines = transitions.map((transition) => {\n const exit = exitById(flow, transition.exitId);\n const title = stateById(flow, transition.to)?.title ?? transition.to;\n return `Сработало: ${exit ? describeCondition(flow, exit.when) : transition.exitId} → «${title}».`;\n });\n const last = transitions.at(-1)!;\n const first = exactFirstText(flow, last.to);\n if (first) lines.push(sayExactly(first));\n return {\n kind: \"app\",\n name: \"flow_transition\",\n title: lines.join(\"\\n\"),\n data: { from: transitions[0]!.from, to: last.to, exitIds: transitions.map((transition) => transition.exitId) },\n };\n}\n\n/** Результат инструмента, после которого сменилось состояние: карточка нового состояния и точный первый текст. */\nexport function transitionReport(flow: AgentFlow, run: FlowRun, transitions: Transition[]): string {\n const from = stateById(flow, transitions[0]!.from)?.title ?? transitions[0]!.from;\n const lines = [`Переход: «${from}» → «${stateById(flow, run.state)?.title ?? run.state}». Состояние сменилось, продолжай по новой карточке.`, \"\", stateCard(flow, run)];\n const first = exactFirstText(flow, run.state);\n if (first) lines.push(\"\", sayExactly(first));\n return lines.join(\"\\n\");\n}\n","import { FLOW_STATE_KEY, type FlowRun } from \"../engine/flow-run\";\nimport { flowDay } from \"../engine/flow-days\";\nimport { FLOW_FIRST_EXIT } from \"../engine/timers\";\nimport { runOutsideTurn, storedRun, type FlowRuntime } from \"./flow-events\";\nimport type { AgentFlow } from \"../format/flow-format\";\nimport type { FlowPort, FlowTestClockInput, FlowTestClockReason, FlowTestClockResult, FlowTimers } from \"./flow-types\";\n\n// Часы тестового чата редактора: автор перематывает их вперёд и видит напоминания без ожидания. Сдвиг хранится в данных\n// чата (`Chat.data`) — их можно писать вне хода, и их видят все копии сервера. Ход копирует сдвиг в место\n// (`FlowRun.timeOffset`), а часы чата везде — часы сервера плюс сдвиг. У рабочих чатов пометки нет: сдвига тоже нет.\n\n/** Ключ данных чата (`Chat.data`) с пометкой тестового чата и сдвигом его часов. */\nexport const FLOW_TEST_CHAT_KEY = \"flowTestChat\";\n\n/** Данные нового тестового чата: `service.createChat(userId, { data: flowTestChatData() })`. */\nexport function flowTestChatData(): Record<string, unknown> {\n return { [FLOW_TEST_CHAT_KEY]: { timeOffset: 0 } };\n}\n\n/** Сдвиг часов из данных чата, мс; `null` — чат не тестовый. */\nexport function testChatOffset(data: Record<string, unknown> | undefined): number | null {\n const mark = data?.[FLOW_TEST_CHAT_KEY];\n if (!mark || typeof mark !== \"object\") return null;\n const offset = (mark as { timeOffset?: unknown }).timeOffset;\n return typeof offset === \"number\" && Number.isFinite(offset) ? offset : 0;\n}\n\n/** Часы чата, мс: часы сервера плюс сдвиг тестового чата. */\nexport const chatTime = (run: Pick<FlowRun, \"timeOffset\"> | null | undefined, serverNow: number): number => serverNow + (run?.timeOffset ?? 0);\n\n/** Перемотка не удалась по понятной причине: `nothing_scheduled` — впереди сценарий сам ничего не напишет. */\nexport class FlowTestClockError extends Error {\n constructor(\n readonly code: \"nothing_scheduled\" | \"not_reached\",\n message: string,\n ) {\n super(message);\n this.name = \"FlowTestClockError\";\n }\n}\n\n/** Сколько раз подряд срок может отложиться (ночь, потолок дня, правила приложения), пока перемотка ищет отправку. */\nconst NEXT_FIRST_MESSAGE_STEPS = 50;\n\n/** Сколько ходов подряд перемотка ждёт (ход таймера и следующий за ним, например прощание). */\nconst TURN_WAIT_LIMIT = 5;\n\n/**\n * Место чата: из состояния после хода, а у чата, закреплённого без хода (`adopt`), — из записи переноса. Не закреплён и\n * хода не было — `null`.\n */\nasync function placeRun(runtime: FlowRuntime, port: FlowPort, chat: { ownerId: string; chatId: string }, flow: AgentFlow, now: number) {\n const { values } = await port.getChatState(chat.ownerId, chat.chatId);\n const stored = storedRun(values[FLOW_STATE_KEY]);\n if (stored) return stored;\n const record = await runtime.store.getChat(chat.chatId);\n return record ? runOutsideTurn(runtime, chat, flow, values, now, record) : null;\n}\n\n/** Сдвиг до начала следующего дня сценария по часам чата (этап 4, «Дни и время»). */\nasync function msToNextDay(runtime: FlowRuntime, port: FlowPort, ownerId: string, chatId: string, offset: number): Promise<number> {\n const pinned = await runtime.chatFlow(ownerId, chatId);\n const schedule = pinned?.flow.schedule;\n if (!pinned || !schedule) throw new Error(\"Перемотка до утра: у сценария не настроены «Дни и время» (schedule)\");\n const chatNow = runtime.now() + offset;\n const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, chatNow);\n if (!run) throw new Error(\"Перемотка до утра: в чате ещё не было хода, дни сценария не начались\");\n const timeZone = runtime.timeZones ? await runtime.timeZones.of(ownerId, chatId) : \"UTC\";\n return flowDay(run, chatNow, timeZone, schedule).nextDayStartAt - chatNow;\n}\n\n/** Дождаться конца хода, который начала перемотка: тестовый чат сразу показывает ответ. Служба без `subscribe` — не ждать. */\nasync function turnFinished(port: FlowPort, ownerId: string, chatId: string): Promise<void> {\n for (let turn = 0; turn < TURN_WAIT_LIMIT; turn += 1) {\n const stream = await port.subscribe?.(ownerId, chatId);\n if (!stream) return;\n for await (const _chunk of stream);\n }\n}\n\n/**\n * Промотать часы тестового чата вперёд: на `ms` или до начала следующего дня сценария (`to: \"next_day_start\"`). Сдвиг\n * в данные чата, ближайший таймер пересчитан по новым часам и, если наступил, срабатывает сразу (агент пишет первым по\n * обычным правилам); возврат — после конца этого хода. Чат не тестовый — ошибка.\n */\nexport async function advanceTestClock(input: {\n runtime: FlowRuntime;\n port: FlowPort;\n timers: FlowTimers | null;\n ownerId: string;\n chatId: string;\n clock: FlowTestClockInput;\n}): Promise<FlowTestClockResult> {\n const { runtime, port, timers, ownerId, chatId, clock } = input;\n const offset = testChatOffset(await port.getChatData(ownerId, chatId));\n if (offset === null) throw new Error(`Чат ${chatId} не тестовый: часы перематываются только у тестовых чатов`);\n if (clock.to === \"next_first_message\") return advanceToNextFirstMessage({ runtime, port, timers, ownerId, chatId, offset });\n const ms = clock.to === \"next_day_start\" ? await msToNextDay(runtime, port, ownerId, chatId, offset) : (clock.ms ?? Number.NaN);\n if (!Number.isFinite(ms) || ms <= 0) throw new Error(\"Перемотка часов: нужен сдвиг вперёд, мс больше нуля\");\n const timeOffset = offset + ms;\n await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });\n\n const pinned = timers && (await runtime.chatFlow(ownerId, chatId));\n if (!timers || !pinned) return { timeOffset };\n const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);\n // Хода ещё не было и чат не закреплён: таймеров нет, сдвиг подхватит первый ход.\n if (!run) return { timeOffset };\n await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });\n await timers.tick();\n await turnFinished(port, ownerId, chatId);\n return { timeOffset };\n}\n\nconst nothingScheduled = (chatId: string) =>\n new FlowTestClockError(\"nothing_scheduled\", `Чат ${chatId}: впереди сценарий сам ничего не напишет — перематывать не к чему`);\n\n/**\n * Перемотка к моменту, когда сценарий напишет первым. Сдвиг — ровно до срока таймера; срабатывание проверяет те же\n * правила, что и без перемотки. Срок отложился (ночь, потолок дня, три часа после сообщения человека) — следующий шаг до\n * нового срока, пока не уйдёт сообщение, не выйдет срок разговора или «не завершил».\n */\nasync function advanceToNextFirstMessage(input: {\n runtime: FlowRuntime;\n port: FlowPort;\n timers: FlowTimers | null;\n ownerId: string;\n chatId: string;\n offset: number;\n}): Promise<FlowTestClockResult> {\n const { runtime, port, timers, ownerId, chatId } = input;\n const pinned = timers && (await runtime.chatFlow(ownerId, chatId));\n if (!timers?.watch || !pinned) throw nothingScheduled(chatId);\n let timeOffset = input.offset;\n for (let step = 0; step < NEXT_FIRST_MESSAGE_STEPS; step += 1) {\n const run = await placeRun(runtime, port, { ownerId, chatId }, pinned.flow, runtime.now() + timeOffset);\n if (!run) throw nothingScheduled(chatId);\n // Первый шаг пересчитывает срок по текущему сдвигу; дальше срок в хранилище — уже отложенный таймером (ночь, правила).\n if (step === 0) await timers.reschedule({ ownerId, chatId, run: { ...run, timeOffset }, branchHead: run.answerId ?? null });\n const record = await runtime.store.getChat(chatId);\n if (!record || record.endedAt !== null || record.dueAt === null) throw nothingScheduled(chatId);\n const serverNow = runtime.now();\n const shift = Math.max(0, Date.parse(record.dueAt) - serverNow);\n if (shift > 0) {\n timeOffset += shift;\n await port.updateChatData(ownerId, chatId, { [FLOW_TEST_CHAT_KEY]: { timeOffset } });\n // Срок в хранилище — по часам сервера: с новым сдвигом он наступает сейчас. Выход ноды и ветка остаются его.\n await runtime.store.setDue(chatId, { at: new Date(serverNow).toISOString(), exitId: record.dueExitId ?? FLOW_FIRST_EXIT, branchHead: record.dueBranchHead });\n }\n let reason: FlowTestClockReason | null = null;\n const unwatch = timers.watch(chatId, (fired) => (reason ??= fired));\n try {\n await timers.tick();\n await turnFinished(port, ownerId, chatId);\n } finally {\n unwatch();\n }\n if (reason) return { timeOffset, reason };\n }\n throw new FlowTestClockError(\"not_reached\", `Чат ${chatId}: срок сообщения откладывался ${NEXT_FIRST_MESSAGE_STEPS} раз подряд — перемотка остановлена`);\n}\n","import type { ClientFact, FlowStore } from \"../store/flow-store\";\nimport type { ClientCardStore, ClientFactStore } from \"./flow-types\";\n\n/** Факт в таблице карточки модуля — запись с `fieldId` = `fact:<id>`; приложение не должно называть так свои поля. */\nexport const FACT_PREFIX = \"fact:\";\nexport const MAX_FACTS = 30;\nexport const MAX_FACT_LENGTH = 200;\n\nconst normalized = (text: string) => text.trim().toLowerCase().replace(/\\s+/g, \" \");\n\n/** Тот же факт (с точностью до регистра и пробелов) — тот же id: `f` и восемь шестнадцатеричных цифр. */\nexport function factId(text: string): string {\n let hash = 0x811c9dc5;\n for (const char of normalized(text)) {\n hash ^= char.codePointAt(0)!;\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n return `f${hash.toString(16).padStart(8, \"0\")}`;\n}\n\nexport const sameFact = (left: string, right: string) => normalized(left) === normalized(right);\n\nexport const isFactEntry = (fieldId: string) => fieldId.startsWith(FACT_PREFIX);\n\n/** Факты по времени записи, раньше — выше: начало блока для модели не меняется, пока не пишут новое. */\nexport const byWriteOrder = (left: ClientFact, right: ClientFact) => left.updatedAt.localeCompare(right.updatedAt) || left.id.localeCompare(right.id);\n\n/** Факты в таблице модуля: рядом с полями карточки, под префиксом. Своей таблицы и миграции не нужно. */\nfunction moduleFacts(store: FlowStore): ClientFactStore {\n const read = async (ownerId: string): Promise<ClientFact[]> => {\n const entries = await store.readCard(ownerId);\n return entries\n .filter((entry) => isFactEntry(entry.fieldId))\n .map((entry) => ({ id: entry.fieldId.slice(FACT_PREFIX.length), text: String(entry.value), updatedAt: entry.updatedAt, chatId: entry.chatId, flowId: entry.flowId }))\n .sort(byWriteOrder);\n };\n return {\n read,\n add: ({ ownerId, fact, chatId, flowId, at }) => store.writeCardField({ ownerId, fieldId: `${FACT_PREFIX}${fact.id}`, value: fact.text, chatId, flowId, at: at.toISOString() }),\n async forget(ownerId, id) {\n if (id !== \"all\") return store.forgetCard(ownerId, `${FACT_PREFIX}${id}`);\n for (const fact of await read(ownerId)) await store.forgetCard(ownerId, `${FACT_PREFIX}${fact.id}`);\n },\n };\n}\n\n/** Факты человека: у приложения (`clientCard.facts`) или в таблице модуля. */\nexport function clientFactStore(store: FlowStore, adapter?: ClientCardStore): ClientFactStore {\n const own = adapter?.facts;\n if (!own) return moduleFacts(store);\n return {\n read: async (ownerId) => [...(await own.read(ownerId))].sort(byWriteOrder),\n add: (input) => own.add(input),\n forget: (ownerId, id) => own.forget(ownerId, id),\n };\n}\n\n/** Факт как запись карточки: так его видят `readClientCard` и экран («Что агент знает о вас»). */\nexport const factEntry = (fact: ClientFact) => ({ fieldId: `${FACT_PREFIX}${fact.id}`, value: fact.text, updatedAt: fact.updatedAt, chatId: fact.chatId, flowId: fact.flowId });\n","/** Ход «пишет первым» не начался или приложение сказало «не сейчас» (`canWriteNow` → `false`): касание — через час. */\nexport const FIRST_MESSAGE_RETRY_MS = 3_600_000;\n/** `canWriteNow` → `{ retryAt }` в прошлом: повтор не чаще, чем раз в минуту. */\nexport const FIRST_MESSAGE_MIN_RETRY_MS = 60_000;\n","import type { FlowSchedule } from \"../format/flow-format\";\nimport type { CanWriteNow, FirstMessagesRules, FirstMessagesSetting } from \"../engine/flow-run\";\nimport { firstMessageSlot, localDay, resolveFirstMessages } from \"../engine/first-messages\";\nimport { nextDayStartAfter } from \"../engine/flow-days\";\nimport { dayClosedToday } from \"../engine/day-frame\";\nimport type { FlowChatRecord, FlowStore } from \"../store/flow-store\";\nimport type { FirstMessageRules, FlowPeople } from \"./flow-types\";\nimport { FIRST_MESSAGE_MIN_RETRY_MS, FIRST_MESSAGE_RETRY_MS } from \"./first-message-retry\";\n\n/** Что в записи чата запрещает писать первым. */\nexport type FlowChatWriteState = Pick<FlowChatRecord, \"dayClosedOn\" | \"pausedUntil\" | \"windowClosed\">;\n\n/**\n * Запрет из записи чата, до правил приложения: окно канала закрыто — молчать до сообщения человека (`retryAt: null`);\n * пауза сотрудника — до её конца; агент закрыл день — до начала следующего. Нет запрета — `null`.\n */\nexport function chatWriteBlock(chat: FlowChatWriteState, at: number, timeZone: string, schedule: FlowSchedule | undefined): { retryAt: number | null } | null {\n if (chat.windowClosed) return { retryAt: null };\n const pausedUntil = chat.pausedUntil ? Date.parse(chat.pausedUntil) : 0;\n if (pausedUntil > at) return { retryAt: pausedUntil };\n if (schedule && dayClosedToday(chat.dayClosedOn, at, timeZone, schedule)) return { retryAt: nextDayStartAfter(at, timeZone, schedule) };\n return null;\n}\n\n/** Итоговые правила: ночь и потолок сценария главнее настроек приложения, те — значений SDK. */\nfunction scenarioRules(rules: FirstMessagesRules, schedule: FlowSchedule | undefined, programDay: number | undefined): Omit<FirstMessagesRules, \"canWriteNow\"> {\n if (!schedule) return rules;\n return {\n quietHours: { from: schedule.nightFrom ?? rules.quietHours.from, to: schedule.dayStart },\n maxPerDay: capOfProgramDay(schedule.firstMessages?.maxPerDayByDay, programDay) ?? schedule.firstMessages?.maxPerDay ?? rules.maxPerDay,\n };\n}\n\n/** Потолок дня программы из `maxPerDayByDay`: вечер до первого дня — как день 1, после списка — его последнее значение. */\nexport function capOfProgramDay(byDay: number[] | undefined, programDay: number | undefined): number | undefined {\n if (!byDay?.length) return undefined;\n return byDay[Math.min(Math.max(programDay ?? 1, 1), byDay.length) - 1];\n}\n\n/** Правила «писать первым» на хранилище счётчиков; `false` в настройках — `null`, агент пишет первым без ограничений. */\nexport function createFirstMessageRules(setting: FirstMessagesSetting | undefined, store: FlowStore): FirstMessageRules | null {\n const rules = resolveFirstMessages(setting);\n if (!rules) return null;\n return {\n async check({ ownerId, chatId, at, timeZone, schedule, programDay, chat }) {\n const blocked = chat ? chatWriteBlock(chat, at, timeZone, schedule) : null;\n if (blocked) return { allowed: false, retryAt: blocked.retryAt };\n const sentToday = await store.countFirstMessages(ownerId, localDay(at, timeZone));\n const slot = firstMessageSlot(scenarioRules(rules, schedule, programDay), { at, timeZone, sentToday });\n if (!slot.allowed) return slot;\n // Приложение спрашивается только тогда, когда остальные правила уже разрешили.\n const answer = rules.canWriteNow ? await rules.canWriteNow({ ownerId, chatId, at: new Date(at) }) : true;\n if (answer === true) return { allowed: true };\n // Конец разговора от ответа не зависит: повтор только у касаний.\n return { allowed: false, retryAt: retryAtOf(answer, at) };\n },\n async record({ ownerId, at, timeZone }) {\n await store.addFirstMessage(ownerId, localDay(at, timeZone));\n },\n };\n}\n\n/**\n * Когда спросить снова после «нет» приложения: `\"never\"` — до сообщения человека (`null`); `false` и негодная дата — через\n * час; дата в прошлом — не раньше чем через минуту, а не каждый обход.\n */\nfunction retryAtOf(answer: Exclude<CanWriteNow, true>, at: number): number | null {\n if (answer === \"never\") return null;\n const retryAt = typeof answer === \"object\" && answer !== null && answer.retryAt instanceof Date ? answer.retryAt.getTime() : Number.NaN;\n if (!Number.isFinite(retryAt)) return at + FIRST_MESSAGE_RETRY_MS;\n return Math.max(retryAt, at + FIRST_MESSAGE_MIN_RETRY_MS);\n}\n\n// ---------- Пояс человека → В10: страница → пояс приложения → пояс сервера ----------\n\n/** Пояс чата ещё не известен: страница его не присылала. */\nexport const UNKNOWN_TIME_ZONE = \"\";\n\nexport const serverTimeZone = (): string => Intl.DateTimeFormat().resolvedOptions().timeZone;\n\n/**\n * Пояс страницы из пояса хода. Ядро с 0.53 говорит его прямо (`pageTimeZone`). Без этого поля: ядро без пояса страницы\n * подставляет запасной (приложения или сервера), поэтому совпадение с запасным — «страница пояс не прислала»: такой пояс\n * не затирает известный пояс чата.\n */\nexport function pageTimeZone(turnZone: string, appZone: string | undefined, sent?: string | null): string | null {\n if (sent !== undefined) return sent;\n return turnZone === (appZone ?? serverTimeZone()) ? null : turnZone;\n}\n\n/**\n * Пояс чата для таймеров: известный (человека или со страницы), иначе `defaultTimeZone`, иначе приложения, иначе\n * сервера — с предупреждением один раз на чат.\n */\nexport function createChatTimeZones(input: { appTimeZone: () => string | undefined; defaultTimeZone?: string; warn: (message: string) => void }) {\n const warned = new Set<string>();\n return {\n of(chatId: string, stored: string): string {\n if (stored) return stored;\n if (input.defaultTimeZone) return input.defaultTimeZone;\n const app = input.appTimeZone();\n if (app) return app;\n const server = serverTimeZone();\n if (!warned.has(chatId)) {\n warned.add(chatId);\n input.warn(`[сценарий] чат=${chatId}: пояс человека неизвестен (нет ни со страницы, ни defaultTimeZone, ни agent.timeZone) — тихие часы и «время суток» по поясу сервера ${server}`);\n }\n return server;\n },\n };\n}\n\n/** Пояс IANA в каноническом виде («asia/novosibirsk» → «Asia/Novosibirsk»); не пояс — `null`. */\nexport function ianaTimeZone(value: string): string | null {\n if (!value.trim()) return null;\n try {\n return new Intl.DateTimeFormat(\"en-US\", { timeZone: value.trim() }).resolvedOptions().timeZone;\n } catch {\n return null;\n }\n}\n\n/** Пояс человека: один на все его разговоры. Копия в записи чата — для таймеров, которые читают её без ожидания. */\nexport interface FlowTimeZones {\n /** Известный пояс: записанный человеку, иначе узнанный в этом чате. Не знаем — `null`. */\n known(ownerId: string, chatId: string): Promise<string | null>;\n /** Пояс для счёта: известный, иначе приложения или сервера. */\n of(ownerId: string, chatId: string): Promise<string>;\n /** Запасной пояс чата, пока пояс человека неизвестен: приложения или сервера. */\n fallback(chatId: string): string;\n /** Пояс узнан (страница, слова человека): человеку и копия в запись чата. */\n write(ownerId: string, chatId: string, timeZone: string): Promise<void>;\n}\n\n/** Пояса людей: адаптер приложения `people` или таблица модуля; запасной — `fallback` (приложения или сервера). */\nexport function createFlowTimeZones(input: { store: FlowStore; people?: FlowPeople; fallback: (chatId: string) => string; now: () => number }): FlowTimeZones {\n const { store } = input;\n const iso = () => new Date(input.now()).toISOString();\n const readPerson = (ownerId: string) => (input.people ? input.people.readTimeZone(ownerId) : store.readTimeZone(ownerId));\n const writePerson = (ownerId: string, timeZone: string) => (input.people ? input.people.writeTimeZone(ownerId, timeZone) : store.writeTimeZone(ownerId, timeZone, iso()));\n async function known(ownerId: string, chatId: string): Promise<string | null> {\n const person = await readPerson(ownerId);\n if (person) return person;\n return (await store.getChat(chatId))?.timeZone || null;\n }\n return {\n known,\n fallback: input.fallback,\n async of(ownerId, chatId) {\n return (await known(ownerId, chatId)) ?? input.fallback(chatId);\n },\n async write(ownerId, chatId, timeZone) {\n if ((await readPerson(ownerId)) !== timeZone) await writePerson(ownerId, timeZone);\n const record = await store.getChat(chatId);\n if (record && record.timeZone !== timeZone) await store.updateChat(chatId, { timeZone }, iso());\n },\n };\n}\n","import type { AgentFlow } from \"../format/flow-format\";\nimport type { FlowFirstReason } from \"../engine/nudges\";\nimport type { FlowRun } from \"../engine/flow-run\";\nimport { stateById } from \"../engine/flow-graph\";\nimport type { FlowNotice } from \"./flow-types\";\nimport { describeCondition, exitById } from \"./flow-events\";\nimport { FLOW_TIMER_NOTICE } from \"./prepare-turn\";\n\n// Один ход «пишет первым» на все причины одного срока (FR-304). Строки постоянные: служебное сообщение идёт в хвост\n// хода, постоянная инструкция не меняется (кэш модели).\n\nconst REASON_LINES = {\n morning: \"Начался новый день сценария: уместно доброе утро.\",\n silence: (step: number) => `Человек молчит после твоего сообщения (касание ${step}).`,\n daily: (at: string) => `Касание по расписанию, ${at}.`,\n nightMessages: (count: number) => `Человек накопил сообщения без ответа (${count}): ответь на них.`,\n} as const;\n\nconst TASK =\n \"Ты пишешь человеку первым. Напиши одно короткое сообщение: новое и полезное для человека по теме разговора, без повтора уже сказанного и без упрёков за молчание.\";\n\n/** Строка переходов: «Сработало: тишина 3 ч» — как в 0.48. */\nconst transitionLine = (flow: AgentFlow, exitId: string) => {\n const exit = exitById(flow, exitId);\n return `Сработало: ${exit ? describeCondition(flow, exit.when) : exitId}`;\n};\n\nfunction reasonLine(reason: FlowFirstReason): string | null {\n switch (reason.kind) {\n case \"morning\":\n return REASON_LINES.morning;\n case \"silence\":\n return REASON_LINES.silence(reason.step);\n case \"daily\":\n return REASON_LINES.daily(reason.at);\n case \"night_messages\":\n return REASON_LINES.nightMessages(reason.count);\n default:\n return null;\n }\n}\n\n/** Сколько человек молчал к ходу «пишет первым»: от его последнего сообщения по часам чата; не писал — `undefined`. */\nexport function silentMsOf(run: Pick<FlowRun, \"lastHumanAt\">, at: number): number | undefined {\n return run.lastHumanAt === null ? undefined : Math.max(0, at - run.lastHumanAt);\n}\n\n/**\n * Служебное сообщение хода «пишет первым». Только переход ноды — прежнее «Сработало: …» (как в 0.48); есть другие\n * причины — их список и задача «новое и полезное». `data.exitId` — переход, который применит ход; `data.nudgeStep` —\n * шаг ряда касаний после хода (после перехода и утреннего — 0, ряд заново); `data.nudgeStepOwn` — его насчитал свой ряд ноды на N дней (`ownNudge`).\n * Для строки на экране (право `appNotices`, `flowNoticeLines`): `data.silentMs` — сколько человек молчал, `data.exit` —\n * нода, куда ведёт переход, и условие срока. Модель их не видит: текст для неё — `title`.\n */\nexport function firstReasonsNotice(\n flow: AgentFlow,\n reasons: readonly FlowFirstReason[],\n exitId: string | null,\n ownNudge = false,\n silentMs?: number,\n): FlowNotice {\n const silence = reasons.find((reason) => reason.kind === \"silence\");\n const moved = exitId !== null && reasons.some((reason) => reason.kind === \"transition\");\n // Утреннее — новая точка отсчёта: ряд тишины после него с первого шага, каждый день заново.\n const morning = reasons.some((reason) => reason.kind === \"morning\");\n const nudgeStep = moved || morning ? 0 : silence?.kind === \"silence\" ? silence.step : undefined;\n const own = ownNudge && silence !== undefined && !moved && !morning;\n const exit = moved ? exitOnScreen(flow, exitId, reasons) : undefined;\n const data = {\n ...(moved && { exitId }),\n reasons: [...reasons],\n ...(nudgeStep !== undefined && { nudgeStep }),\n ...(own && { nudgeStepOwn: true }),\n ...(silentMs !== undefined && { silentMs }),\n ...(exit && { exit }),\n };\n const others = reasons.flatMap((reason) => reasonLine(reason) ?? []);\n if (others.length === 0 && moved) return { kind: \"app\", name: FLOW_TIMER_NOTICE, title: transitionLine(flow, exitId), data };\n const lines = [...(moved ? [`${transitionLine(flow, exitId)}.`] : []), ...others, TASK];\n return { kind: \"app\", name: FLOW_TIMER_NOTICE, title: lines.join(\"\\n\"), data };\n}\n\n/** Переход для строки на экране: название ноды, куда он ведёт, и условие срока. */\nfunction exitOnScreen(flow: AgentFlow, exitId: string, reasons: readonly FlowFirstReason[]) {\n const to = reasons.find((reason) => reason.kind === \"transition\");\n const state = to?.kind === \"transition\" ? stateById(flow, to.stateId) : undefined;\n const when = exitById(flow, exitId)?.when;\n if (!state && !when) return undefined;\n return { ...(state && { stateTitle: state.title }), ...(when && { when }) };\n}\n","import type { AgentFlow, FlowAfterEnd } from \"../format/flow-format\";\nimport type { FlowEndReason, FlowRun, Transition } from \"../engine/flow-run\";\nimport { isFinal, stateById } from \"../engine/flow-graph\";\nimport { applyTransition } from \"../engine/run-updates\";\nimport { flowDay } from \"../engine/flow-days\";\nimport { FLOW_DEADLINE_EXIT } from \"../engine/timers\";\nimport type { FirstMessageRules, FlowFinish, FlowPort } from \"./flow-types\";\nimport type { ChatFlowRun, FlowRuntime, PinnedFlow } from \"./flow-events\";\nimport type { FlowDelivery } from \"./flow-delivery\";\nimport { silentMsOf } from \"./first-reasons-notice\";\n\n// Конец разговора — запись `endedAt/endReason`, событие `onFinish`, прощание при сроке. По plan.md, «Конец разговора».\n// Конец — точка передачи, а не тупик: событие несёт всё, что нужно передать человека дальше (место с заметками чата,\n// карточка — по `ownerId`), а что делать с сообщением после конца, решает одно место — `afterEndOf`.\n\n/** Имя служебного сообщения `app` прощального хода при «срок вышел». */\nexport const FLOW_DEADLINE_NOTICE = \"flow_deadline\";\n\nconst DEADLINE_REASON = { kind: \"timer\", text: \"срок вышел\" } as const;\nconst FAREWELL_NOTICE = \"Срок программы вышел. Попрощайся с человеком одним сообщением: коротко подведи итог и скажи, что дальше.\";\n\n/**\n * Что делать с сообщением человека после конца (→ В6). Единственная точка решения: позже здесь встанет передача\n * следующему сценарию и признак «кто ведёт сейчас» (сценарий, рассылка, сотрудник).\n */\nexport function afterEndOf(flow: AgentFlow): FlowAfterEnd {\n return flow.afterEnd ?? \"reply\";\n}\n\n/**\n * Закрывает ли конечная нода разговор. Сценарий с «Днями и временем» или с заданным `afterEnd` — да; иначе, как в 0.48,\n * агент и дальше отвечает по карточке конечной ноды, а приложение узнаёт о ней только событием.\n */\nexport const finalEndsChat = (flow: AgentFlow) => flow.schedule !== undefined || flow.afterEnd !== undefined;\n\n/** Переход «срок вышел» в `deadlineState`; его нет, он не задан или чат уже там — `null`. */\nfunction deadlineTransition(flow: AgentFlow, run: FlowRun, at: number): Transition | null {\n const to = flow.schedule?.deadlineState;\n const state = to === undefined ? undefined : stateById(flow, to);\n if (!state || run.state === state.id) return null;\n return { from: run.state, to: state.id, exitId: FLOW_DEADLINE_EXIT, decidedBy: \"timer\", tags: state.tags, at, reason: DEADLINE_REASON };\n}\n\n/** Место чата, закрытого вне хода (таймер, перенос): переход в `deadlineState` и отметка конца. О переходе уже сообщено. */\nexport function endedRun<T extends FlowRun>(flow: AgentFlow, run: T, ended: { endedAt: number; endReason: FlowEndReason }): T {\n if (run.endedAt !== undefined) return run;\n const transition = ended.endReason === \"deadline\" ? deadlineTransition(flow, run, ended.endedAt) : null;\n const moved = transition ? (applyTransition(flow, run, transition) as T) : run;\n return { ...moved, endedAt: ended.endedAt, endReason: ended.endReason };\n}\n\nexport interface FlowDeadlineInput {\n ownerId: string;\n chatId: string;\n pinned: PinnedFlow;\n run: ChatFlowRun;\n /** Когда срок вышел, по часам чата. */\n at: number;\n timeZone: string;\n}\n\nexport interface FlowFinisher {\n /** Переходы хода: вход в конечную ноду — конец `final` (или только событие у сценария 0.48). */\n afterTransitions(input: { ownerId: string; chatId: string; pinned: PinnedFlow; run: FlowRun; transitions: readonly Transition[] }): Promise<void>;\n /** «Срок вышел»: конец `deadline`, переход в `deadlineState`, событие. Возвращает место после конца. */\n deadline(input: FlowDeadlineInput): Promise<{ run: ChatFlowRun; ended: boolean }>;\n /** Прощальный ход при «срок вышел» (→ В5): по правилам «писать первым»; готовый текст — без модели. */\n farewell(input: FlowDeadlineInput & { port: FlowPort; firstMessages: FirstMessageRules | null; delivery?: FlowDelivery }): Promise<void>;\n /** Конец вне хода: «не завершил» (3 дня тишины), «заблокировал» (исход доставки), причина приложения (`flow.end`). Один раз. */\n end(input: { ownerId: string; chatId: string; pinned: PinnedFlow; run: FlowRun; reason: FlowEndReason; outcome?: string; at: number }): Promise<{ ended: boolean }>;\n}\n\nexport function createFlowFinish(input: { runtime: FlowRuntime; onFinish?: (event: FlowFinish) => void | Promise<void> }): FlowFinisher {\n const { runtime } = input;\n\n async function report(event: { ownerId: string; chatId: string; pinned: PinnedFlow; run: FlowRun; reason: FlowEndReason; outcome?: string; at: number }): Promise<void> {\n const { onFinish } = input;\n if (!onFinish) return;\n const { schedule } = event.pinned.flow;\n let day: number | null = null;\n if (schedule) {\n const timeZone = (await runtime.timeZones?.of(event.ownerId, event.chatId)) ?? \"UTC\";\n day = flowDay(event.run, event.at, timeZone, schedule).day;\n if (schedule.lengthDays !== undefined) day = Math.min(day, schedule.lengthDays);\n }\n const { answerId: _answerId, firstReasons: _firstReasons, ...run } = event.run as ChatFlowRun;\n const finish: FlowFinish = {\n ownerId: event.ownerId,\n chatId: event.chatId,\n flowId: event.pinned.flowId,\n versionId: event.pinned.versionId,\n reason: event.reason,\n ...(event.outcome !== undefined && { outcome: event.outcome }),\n day,\n state: event.run.state,\n tags: stateById(event.pinned.flow, event.run.state)?.tags ?? [],\n run,\n at: new Date(event.at),\n };\n const work = Promise.resolve()\n .then(() => onFinish(finish))\n .catch((error) => runtime.logError(`[сценарий] чат=${event.chatId} onFinish упал: ${error instanceof Error ? error.message : String(error)}`));\n if (runtime.track) runtime.track(work);\n else await work;\n }\n\n /** Закрыть разговор в хранилище; `true` — закрыл этот вызов (уже закрытый не закрывается второй раз). */\n async function close(chatId: string, reason: FlowEndReason, at: number): Promise<boolean> {\n const before = await runtime.store.getChat(chatId);\n if (!before || before.endedAt !== null) return false;\n const after = await runtime.store.endChat(chatId, { reason, at: new Date(at).toISOString() });\n return after !== null && after.endReason === reason && Date.parse(after.endedAt ?? \"\") === at;\n }\n\n return {\n async afterTransitions({ ownerId, chatId, pinned, run, transitions }) {\n const final = transitions.find((transition) => {\n const state = stateById(pinned.flow, transition.to);\n return state !== undefined && isFinal(state);\n });\n if (!final) return;\n if (finalEndsChat(pinned.flow) && !(await close(chatId, \"final\", final.at))) return;\n await report({ ownerId, chatId, pinned, run, reason: \"final\", at: final.at });\n },\n\n async deadline({ ownerId, chatId, pinned, run, at }) {\n const ended = await close(chatId, \"deadline\", at);\n const transition = deadlineTransition(pinned.flow, run, at);\n let next: ChatFlowRun = run;\n if (ended && transition) {\n next = applyTransition(pinned.flow, run, transition) as ChatFlowRun;\n await runtime.afterTransitions({ ownerId, chatId, pinned, run: next, transitions: [transition] });\n }\n if (ended) await report({ ownerId, chatId, pinned, run: next, reason: \"deadline\", at });\n return { run: endedRun(pinned.flow, next, { endedAt: at, endReason: \"deadline\" }), ended };\n },\n\n async farewell({ ownerId, chatId, pinned, run, at, timeZone, port, firstMessages, delivery }) {\n const schedule = pinned.flow.schedule;\n if (!schedule?.farewellOnDeadline) return;\n const record = await runtime.store.getChat(chatId);\n const chat = record ? { dayClosedOn: record.dayClosedOn, pausedUntil: record.pausedUntil, windowClosed: record.windowClosed } : undefined;\n const allowed = firstMessages\n ? await firstMessages.check({ ownerId, chatId, at, timeZone, schedule, programDay: flowDay(run, at, timeZone, schedule).day, ...(chat && { chat }) })\n : { allowed: true as const };\n // Нельзя писать сейчас — прощания нет: разговор уже закрыт, таймеров после конца нет.\n if (!allowed.allowed) return;\n const text = schedule.farewellText?.trim();\n const silentMs = silentMsOf(run, at);\n const result = await port.deliverNotices({\n ownerId,\n chatId,\n notices: [{ kind: \"app\", name: FLOW_DEADLINE_NOTICE, title: FAREWELL_NOTICE, data: { reasons: [{ kind: \"farewell\" }], ...(silentMs !== undefined && { silentMs }) } }],\n wakeAgent: true,\n deliveryKey: `flow:${chatId}:deadline`,\n usageSource: \"first_message\",\n maxServerTurnsInRow: false,\n timeZone,\n ...(delivery?.own() && { awaitAnswer: true }),\n ...(text && { reply: { text } }),\n });\n if (result.status !== \"delivered\" || !result.turnStarted) return;\n if (delivery) await delivery.afterFirstTurn({ ownerId, chatId, answer: result.answer, reasons: [{ kind: \"farewell\" }], at, timeZone, endedAt: record?.endedAt ?? null });\n else await firstMessages?.record({ ownerId, at, timeZone });\n },\n\n async end({ ownerId, chatId, pinned, run, reason, outcome, at }) {\n const ended = await close(chatId, reason, at);\n if (ended) await report({ ownerId, chatId, pinned, run, reason, ...(outcome !== undefined && { outcome }), at });\n return { ended };\n },\n };\n}\n","import type { AgentNotice, PrepareTurn, TurnAction } from \"@ykurilov/agent-chat-core\";\nimport { FLOW_STATE_KEY, type FlowTouch, type Transition } from \"../engine/flow-run\";\nimport { migrateRun } from \"../engine/migrate-run\";\nimport { noteAgentAnswer, noteHumanMessage, withNudgeStep, withoutUndeliveredTouch } from \"../engine/run-updates\";\nimport { advance, chainLimitMessage, currentRun, pendingEvent, transitionNotice, withReopen, type ChatFlowRun, type FlowRuntime } from \"./flow-events\";\nimport type { PendingFlowChange } from \"../store/flow-store\";\nimport { pageTimeZone } from \"./first-message-rules\";\nimport { testChatOffset } from \"./test-clock\";\nimport { dayInProgress, deadlineAt, withDayAnchor } from \"../engine/flow-days\";\nimport { endedRun, FLOW_DEADLINE_NOTICE, type FlowFinisher } from \"./flow-finish\";\nimport type { FlowFirstReason } from \"../engine/nudges\";\n\n/** Имена служебных сообщений `app`, которые сценарий применяет в начале хода. */\nexport const FLOW_TIMER_NOTICE = \"flow_timer\";\nexport const FLOW_EVENT_NOTICE = \"flow_event\";\n\n/** Ходы, которые отвечают на сообщение человека. */\nconst HUMAN_ACTIONS: ReadonlySet<TurnAction> = new Set<TurnAction>([\"send\", \"edit\", \"regenerate\", \"retry\"]);\n\n/** Переход по месту: из какой ноды и каким выходом. */\nconst transitionKey = (transition: Pick<Transition, \"from\" | \"exitId\">) => `${transition.from}\\u0000${transition.exitId}`;\n\nconst errorText = (error: unknown) => (error instanceof Error ? error.message : String(error));\n\nconst dataOf = (notice: AgentNotice): Record<string, unknown> =>\n notice.data && typeof notice.data === \"object\" ? (notice.data as Record<string, unknown>) : {};\n\n/** Причины «пишет первым» этого хода: служебные сообщения таймера и прощания. */\nconst firstReasonsOf = (notices: readonly AgentNotice[]): FlowFirstReason[] =>\n notices.flatMap((notice) => {\n if (notice.kind !== \"app\" || (notice.name !== FLOW_TIMER_NOTICE && notice.name !== FLOW_DEADLINE_NOTICE)) return [];\n const { reasons } = dataOf(notice);\n return Array.isArray(reasons) ? (reasons as FlowFirstReason[]) : [];\n });\n\n/**\n * `agent.prepareTurn` сценария: в начале хода, до застывших блоков, применяет всё, что случилось вне хода — ожидающие\n * переходы судьи (только на своей ветке), события приложения, перевод версии, наступившие таймеры, — и сообщение\n * человека («назад» после касаний). Модель получает служебное сообщение «Сработало: …» и уже новую карточку.\n */\nexport function flowPrepareTurn(runtime: FlowRuntime, appTimeZone: () => string | undefined, finisher?: () => FlowFinisher): PrepareTurn {\n return async (ctx) => {\n // В группах сценарий выключен: чат не закрепляется, состояние не пишется.\n if (ctx.audience === \"group\") return;\n let pinned = await runtime.chatFlow(ctx.ownerId, ctx.chatId, ctx.chat);\n if (!pinned) return;\n // Тестовый чат идёт по своим часам: сдвиг из данных чата ложится в место.\n const timeOffset = testChatOffset(ctx.chat.data);\n const now = runtime.now() + (timeOffset ?? 0);\n const transitions: Transition[] = [];\n let card: Record<string, unknown> | undefined;\n // Переходы, применённые в срок без хода: сообщены тогда, ход только переносит их в состояние и показывает модели.\n const applied: Transition[] = [];\n const step = async (run: ChatFlowRun, event: Parameters<typeof advance>[2], out = transitions) =>\n advance(pinned!.flow, run, event, {\n now,\n card: (card ??= await runtime.cardValues(ctx.ownerId)),\n out,\n onChainLimit: (stopped) => runtime.logError(chainLimitMessage(ctx.chatId, stopped)),\n timeZone,\n });\n\n // Пояс человека → В10: со страницы, иначе известный пояс человека; ход без пояса страницы (мессенджер) его не затирает.\n const record = await runtime.store.getChat(ctx.chatId);\n const page = pageTimeZone(ctx.timeZone, appTimeZone(), ctx.pageTimeZone);\n const known = page ?? (runtime.timeZones ? await runtime.timeZones.known(ctx.ownerId, ctx.chatId) : record?.timeZone || null);\n const timeZone = known ?? runtime.defaultTimeZone ?? ctx.timeZone;\n\n // Открыт заново вне хода (`flow.reopen`): место догоняет запись.\n let run = withReopen(pinned.flow, currentRun(pinned.flow, ctx.state, now), record);\n const previousAnswer = run.answerId;\n const pending: PendingFlowChange[] = await runtime.store.takePending(ctx.chatId);\n const appliedInTime = new Set(\n pending.flatMap((change) => (change.kind === \"transition\" && change.appliedAt !== undefined && change.branchHead === previousAnswer ? [transitionKey(change.transition)] : [])),\n );\n // Ход не записал место (сбой, срок, «Стоп») — взятое возвращается в очередь: ключи не дают двойных записей.\n let restored: Promise<unknown> | null = null;\n const restore = () => (restored ??= Promise.all(pending.map((change) => runtime.store.addPending(ctx.chatId, change))));\n const onAbort = () => void restore().catch((error) => runtime.logError(`[сценарий] чат=${ctx.chatId} ожидающее не возвращено: ${errorText(error)}`));\n if (pending.length > 0) ctx.abortSignal.addEventListener(\"abort\", onAbort, { once: true });\n try {\n return await applyTurn();\n } catch (error) {\n await restore();\n throw error;\n } finally {\n ctx.abortSignal.removeEventListener(\"abort\", onAbort);\n }\n\n async function applyTurn() {\n // Первый день — по поясу человека: пояс узнали или сменили — якорь пересчитан (→ В1, FR-207).\n const schedule = pinned!.flow.schedule;\n if (schedule) run = withDayAnchor(run, timeZone, schedule) as ChatFlowRun;\n if (record?.endedAt) {\n // Разговор закрыли вне хода (таймер, перенос): место догоняет запись.\n run = endedRun(pinned!.flow, run, { endedAt: Date.parse(record.endedAt), endReason: record.endReason ?? \"final\" });\n } else if (schedule && finisher) {\n // Таймер срока не сработал (сервер стоял): срок закрывает разговор на первом же ходе.\n const deadline = deadlineAt(run, timeZone, schedule);\n if (deadline !== null && deadline <= now) {\n ({ run } = await finisher().deadline({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned: pinned!, run, at: deadline, timeZone }));\n }\n }\n // После конца переходов нет: ожидающее снимается, сообщение человека только считается.\n const ended = run.endedAt !== undefined;\n if (!ended && run.checkedTransition) {\n const decision = run.checkedTransition;\n const scope = { ownerId: ctx.ownerId, chatId: ctx.chatId, flowId: pinned!.flowId };\n const allowed = decision.versionId === pinned!.versionId && decision.answerId === previousAnswer && decision.transition.from === run.state && runtime.confirmations?.allows(scope, run, decision.transition.exitId);\n const { checkedTransition, ...withoutDecision } = run;\n run = withoutDecision;\n if (allowed) run = await step(run, pendingEvent(decision.transition, timeZone));\n }\n for (const change of ended ? [] : pending) {\n if (change.kind === \"migrate\") {\n // Версия в записи чата уже новая (`migrateBatch`): переводится только место в сценарии.\n const next = await runtime.chatFlow(ctx.ownerId, ctx.chatId);\n if (next) {\n run = { ...migrateRun(run, next.flow, change.map, now), answerId: previousAnswer } as ChatFlowRun;\n pinned = next;\n }\n } else if (change.kind === \"transition\") {\n // Переход судьи или таймера — только на той ветке, где он решён, и только из того же состояния.\n if (change.branchHead !== previousAnswer || run.state !== change.transition.from) continue;\n if (runtime.confirmations?.guardFor(pinned!.flowId, run.state, change.transition.exitId)) continue;\n const out: Transition[] = [];\n run = await step(run, pendingEvent(change.transition, timeZone), out);\n // Применённые в срок уже сообщены; дотянутое ходом поверх них — новое.\n for (const transition of out) (appliedInTime.has(transitionKey(transition)) ? applied : transitions).push(transition);\n } else {\n run = await step(run, { type: \"event\", event: \"app\", name: change.name });\n }\n }\n // Ход «пишет первым»: счёт до него — на случай, если его ответ не дойдёт (FR-309).\n let touchBase: Omit<FlowTouch, \"answerId\" | \"at\"> | null = null;\n for (const notice of ended ? [] : ctx.notices) {\n if (notice.kind !== \"app\") continue;\n const data = dataOf(notice);\n if (notice.name === FLOW_TIMER_NOTICE) {\n // Прошлое касание не доставлено — не засчитано: счёт как до него.\n if (typeof data.undelivered === \"string\") run = withoutUndeliveredTouch(run, new Set([data.undelivered])).run as ChatFlowRun;\n // Переход ноды (если есть) и шаг ряда касаний после него.\n if (typeof data.exitId === \"string\") run = await step(run, { type: \"timer\", exitId: data.exitId, timeZone });\n touchBase = { lastAgentAt: run.lastAgentAt, nudgeStep: run.nudgeStep ?? 0, ...(run.nudgeStepOwn && { nudgeStepOwn: true }), agentAnswers: run.agentAnswers };\n if (typeof data.nudgeStep === \"number\") run = withNudgeStep(run, data.nudgeStep, data.nudgeStepOwn === true);\n } else if (notice.name === FLOW_EVENT_NOTICE && typeof data.name === \"string\") {\n run = await step(run, { type: \"event\", event: \"app\", name: data.name });\n }\n }\n // Ответ на сообщение человека: новое, правка, «Перегенерировать» и «Повторить» — состояние до сообщения, оно\n // учитывается заново. Продолжение прежнего ответа его уже учло.\n if (!ctx.resumed && HUMAN_ACTIONS.has(ctx.action)) {\n // Человек написал — окно канала снова открыто: агент может писать первым.\n if (record?.windowClosed) await runtime.store.setWindowClosed(ctx.chatId, false);\n // Ход на пачку сообщений — в счёт каждое её сообщение.\n run = noteHumanMessage(run, now, schedule ? dayInProgress(now, timeZone, schedule) : undefined, ctx.burstMessageIds?.length ?? 1);\n if (!ended) run = await step(run, { type: \"event\", event: \"message\" });\n }\n // Ход по служебному сообщению — ход сервера: счёт подряд для мягкого потолка таймеров; человек его обнулил выше.\n if (ctx.action === \"notice\" && !ctx.resumed) run = { ...run, serverTurns: (run.serverTurns ?? 0) + 1 };\n // Продолжение после подтверждения или виджета — тот же ответ: агент ещё не ответил заново.\n if (ctx.action !== \"approval\" && ctx.action !== \"widget\") run = noteAgentAnswer(run, now);\n // Снимок касания живёт до следующего хода; продолжение того же ответа его сохраняет.\n // Причины «пишет первым» — тоже: для хвоста приложения в этом ответе.\n const { touch: earlierTouch, firstReasons: earlierReasons, ...withoutTouch } = run;\n const continued = ctx.action === \"approval\" || ctx.action === \"widget\";\n const touch = touchBase ? { answerId: ctx.messageId, at: now, ...touchBase } : continued ? earlierTouch : undefined;\n const reasons = firstReasonsOf(ctx.notices);\n const firstReasons = reasons.length > 0 ? reasons : continued ? earlierReasons : undefined;\n run = { ...withoutTouch, answerId: ctx.messageId, ...(timeOffset !== null && { timeOffset }), ...(touch && { touch }), ...(firstReasons && { firstReasons }) };\n\n if (ctx.abortSignal.aborted) {\n await restore();\n return undefined;\n }\n // Пояс узнан — человеку (общий для всех его разговоров) и копия в запись чата для таймеров.\n if (known && runtime.timeZones) await runtime.timeZones.write(ctx.ownerId, ctx.chatId, known);\n else if (record && page && record.timeZone !== page) await runtime.store.updateChat(ctx.chatId, { timeZone: page }, new Date(runtime.now()).toISOString());\n await runtime.afterTransitions({ ownerId: ctx.ownerId, chatId: ctx.chatId, pinned: pinned!, run, transitions });\n return {\n set: { [FLOW_STATE_KEY]: run },\n ...(applied.length + transitions.length > 0 && { notices: [transitionNotice(pinned!.flow, [...applied, ...transitions])] }),\n };\n }\n };\n}\n","import type { AgentFlow, FlowField } from \"../format/flow-format\";\nimport type { FlowRun } from \"../engine/flow-run\";\nimport { fieldValueFits } from \"../engine/data-conditions\";\nimport { fieldById } from \"../engine/flow-graph\";\nimport { isFieldStale, sideQuestion } from \"../engine/side-questions\";\nimport type { ClientCardEntry, ClientFact } from \"../store/flow-store\";\n\n/** Значение поля готовой строкой: для блока модели, ответа `client_card` и экрана. */\nexport function fieldValueText(value: unknown): string {\n if (typeof value === \"string\") return value.trim();\n if (typeof value === \"number\") return String(value);\n if (typeof value === \"boolean\") return value ? \"да\" : \"нет\";\n return JSON.stringify(value);\n}\n\nfunction questionLine(flow: AgentFlow, run: FlowRun, card: Record<string, { value: unknown; updatedAt: number }>, now: number): string {\n const question = sideQuestion(flow, run, { card, now });\n const found = question && fieldById(flow, question.fieldId);\n if (!question || !found) return \"Сейчас не спрашивай ничего о человеке между делом: ответь на то, что он пишет.\";\n const { field } = found;\n const noted = `flow_note { asked: \"${field.id}\" }`;\n if (question.reason === \"learn\") return `Сначала узнай: «${field.name}» — ${field.about} Запиши ответ через flow_note.`;\n if (question.reason === \"stale\") {\n return `Давно не уточняли: «${field.name}» (сейчас записано: ${fieldValueText(card[field.id]!.value)}). Мягко сверь одним коротким вопросом к месту. Спросил — отметь ${noted}; если изменилось — запиши новое значение через flow_note.`;\n }\n return `Можно спросить между делом: «${field.name}» — ${field.about} Один короткий вопрос к месту, не в лоб. Спросил — отметь ${noted}.`;\n}\n\n/**\n * Блок `flow:client`: что известно о человеке, что устарело, о чём можно спросить между делом. Известное и заметки идут\n * по порядку полей сценария (порядок = что важнее). Значение не по типу поля считается неизвестным.\n */\nexport function clientCardBlock(input: { flow: AgentFlow; run: FlowRun; card: ClientCardEntry[]; facts?: ClientFact[]; now: number }): string {\n const { flow, run, now } = input;\n const facts = input.facts ?? [];\n if (flow.clientCard.length === 0 && flow.chatNotes.length === 0 && facts.length === 0) return \"\";\n\n const byId = new Map(input.card.map((entry) => [entry.fieldId, { value: entry.value, updatedAt: Date.parse(entry.updatedAt) }]));\n const known = (field: FlowField, value: unknown) => fieldValueFits(field, value);\n\n const cardLines = flow.clientCard.flatMap((field) => {\n const entry = byId.get(field.id);\n if (!entry || !known(field, entry.value)) return [];\n const stale = isFieldStale(field, entry.updatedAt, now) ? \" (записано давно, могло измениться)\" : \"\";\n return [`- ${field.name}: ${fieldValueText(entry.value)}${stale}`];\n });\n const noteLines = flow.chatNotes.flatMap((field) => (known(field, run.notes[field.id]) ? [`- ${field.name}: ${fieldValueText(run.notes[field.id])}`] : []));\n\n const lines = [\"Что известно о человеке (карточка клиента):\", ...(cardLines.length > 0 ? cardLines : facts.length > 0 ? [] : [\"- пока ничего не записано\"])];\n if (facts.length > 0) lines.push(\"\", \"Факты о человеке:\", ...facts.map((fact) => `- ${fact.text}`));\n if (noteLines.length > 0) lines.push(\"\", \"Заметки этого чата:\", ...noteLines);\n lines.push(\"\", questionLine(flow, run, Object.fromEntries(byId), now));\n return lines.join(\"\\n\");\n}\n"],"mappings":"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","names":["run"]}
@@ -1,3 +1,6 @@
1
+ // src/store/flow-store.ts
2
+ var FLOW_DELIVERY_EXIT = "@delivery";
3
+
1
4
  // src/store/flow-store-shared.ts
2
5
  function migratePending(input) {
3
6
  return { kind: "migrate", key: `migrate:${input.toVersionId}:${input.at}`, versionId: input.toVersionId, map: input.map, at: input.at };
@@ -20,8 +23,21 @@ function withoutNul(value) {
20
23
  if (typeof value !== "object") return value;
21
24
  return JSON.parse(JSON.stringify(value, (_key, item) => typeof item === "string" ? item.replaceAll("\0", "") : item));
22
25
  }
26
+ function normalizedDelivery(delivery) {
27
+ return {
28
+ ...delivery,
29
+ retryAt: normalizeTime(delivery.retryAt),
30
+ ...delivery.pausedAt !== void 0 && { pausedAt: normalizeTime(delivery.pausedAt) },
31
+ ...delivery.resumedAt !== void 0 && { resumedAt: normalizeTime(delivery.resumedAt) }
32
+ };
33
+ }
34
+ function sameDelivery(stored, expected) {
35
+ if (stored === null || expected === null) return stored === expected;
36
+ return stored.messageId === expected.messageId && stored.attempt === expected.attempt && (stored.token ?? null) === (expected.token ?? null);
37
+ }
23
38
 
24
39
  export {
40
+ FLOW_DELIVERY_EXIT,
25
41
  migratePending,
26
42
  appliedInTime,
27
43
  pendingAdded,
@@ -29,6 +45,8 @@ export {
29
45
  byVersionAndState,
30
46
  compareText,
31
47
  normalizeTime,
32
- withoutNul
48
+ withoutNul,
49
+ normalizedDelivery,
50
+ sameDelivery
33
51
  };
34
- //# sourceMappingURL=chunk-DCTRMCGV.js.map
52
+ //# sourceMappingURL=chunk-DOKHNTPU.js.map