@ykurilov/agent-chat-core 0.76.0 → 0.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -128,6 +128,7 @@ import {
128
128
  endTurn,
129
129
  environmentSecrets,
130
130
  extendCatalog,
131
+ flowStateCommitter,
131
132
  foldAppState,
132
133
  hiddenStateKeyOf,
133
134
  isBurstMessage,
@@ -183,7 +184,7 @@ import {
183
184
  withoutFileParts,
184
185
  withoutGlueFragments,
185
186
  withoutResetSteps
186
- } from "./chunk-6KHSUGHE.js";
187
+ } from "./chunk-BQTUOC3T.js";
187
188
  import {
188
189
  APP_EVENT_DATA_MAX_BYTES,
189
190
  APP_EVENT_TYPE_MAX_LENGTH,
@@ -1012,6 +1013,7 @@ export {
1012
1013
  endTurn,
1013
1014
  environmentSecrets,
1014
1015
  extendCatalog,
1016
+ flowStateCommitter,
1015
1017
  foldAppState,
1016
1018
  hiddenStateKeyOf,
1017
1019
  isBurstMessage,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/memory/memory-optimization.ts","../src/storage/postgres-migrations.ts","../src/request-origin/request-origin.ts","../src/storage/in-memory-storage.ts"],"sourcesContent":["import type { MemoryError, MemoryNote, MemoryNoteInput } from \"./long-term-memory\";\n\n/**\n * Состояние запуска оптимизации дублей: идёт → слито / нечего сливать / не удалось; слитое можно вернуть (`reverted`).\n * Запуск `running` у человека не больше одного.\n */\nexport type MemoryOptimizationStatus = \"running\" | \"applied\" | \"no_changes\" | \"failed\" | \"reverted\";\n\n/** Одна склейка: записи `originalIds` скрыты и подчинены новой записи `resultId`. */\nexport interface MemoryMergeGroup {\n resultId: string;\n originalIds: string[];\n /** Версия результата на момент слияния: пока она та же, результат не менялся и слияние можно вернуть. */\n resultVersion: number;\n}\n\n/** Запуск оптимизации. Владелец (`ownerId`) — свойство хранилища и наружу не выдаётся. */\nexport interface MemoryOptimizationRun {\n id: string;\n /** Время в формате ISO 8601. Часы даёт вызывающий: от них считаются срок между запусками и пауза после сбоя. */\n startedAt: string;\n finishedAt: string | null;\n status: MemoryOptimizationStatus;\n /** Склейки запуска; у не слитых запусков пусто. */\n groups: MemoryMergeGroup[];\n /** Причина сбоя одним словом-кодом; у остальных `null`. */\n error: string | null;\n /** Запись расхода модели, если вызывающий её знает. */\n usageRecordId: string | null;\n}\n\n/** Правила запуска: их проверяет хранилище в той же операции, что и берёт запуск, чтобы два процесса не начали его вдвоём. */\nexport interface MemoryOptimizationRules {\n /** Сколько должно пройти после законченного запуска (слито, нечего сливать, отменено) до следующего. */\n minIntervalMs: number;\n /**\n * Сколько должно пройти после неудачного запуска до повтора. Число — одна пауза на все неудачи; список — паузы после\n * 1-й, 2-й, … неудачи подряд (последняя держится дальше). Успешный, пустой или отменённый запуск начинает счёт заново.\n */\n retryAfterFailureMs: number | readonly number[];\n /** Сколько запуск `running` считается живым; после этого срока он становится неудачным (`failed`, ошибка `lease_expired`). */\n leaseMs: number;\n}\n\n/** Чем закончился запуск без слияния. */\nexport interface MemoryOptimizationOutcome {\n status: \"no_changes\" | \"failed\";\n finishedAt: string;\n error?: string | null;\n usageRecordId?: string | null;\n}\n\n/** Что слить: новая запись-результат вместо оригиналов; версии оригиналов — те, что видел планировщик. */\nexport interface MemoryMergeInput {\n runId: string;\n finishedAt: string;\n usageRecordId?: string | null;\n merges: { result: MemoryNoteInput; originals: { id: string; version: number }[] }[];\n}\n\n/** Скрытая запись слияния: оригинал, подчинённый результату. Список, блок памяти и поиск таких записей не видят. */\nexport interface MergedMemoryNote extends MemoryNote {\n mergedInto: string;\n}\n\nexport type MemoryMergeApplyResult = MemoryOptimizationRun | \"stale_version\" | \"run_not_active\";\n/** `limit_reached` и `duplicate` — возврат вышел бы за пределы памяти или вернул бы повтор записи, сделанной за это время. */\nexport type MemoryMergeRevertResult = \"ok\" | \"not_found\" | \"conflict\" | \"limit_reached\" | \"duplicate\";\n\n/** Запуск в том виде, что нужен для решения «можно ли начать новый»; время — миллисекунды. */\nexport interface OptimizationRunTiming {\n status: MemoryOptimizationStatus;\n startedAtMs: number;\n /** Конец запуска; у идущего его нет. */\n endedAtMs: number | null;\n}\n\n/** Пауза после неудачи с номером `failuresInRow` (с 1) подряд. */\nexport function pauseAfterFailure(rules: Pick<MemoryOptimizationRules, \"retryAfterFailureMs\">, failuresInRow: number): number {\n const { retryAfterFailureMs } = rules;\n if (typeof retryAfterFailureMs === \"number\") return retryAfterFailureMs;\n return retryAfterFailureMs[Math.min(failuresInRow, retryAfterFailureMs.length) - 1] ?? 0;\n}\n\n/**\n * Мешает ли история запусков начать новый: идёт другой, или не вышла пауза после последнего. Пауза после неудачи\n * растёт с числом неудач подряд. Все три хранилища решают этим одним правилом.\n */\nexport function isOptimizationBlocked(runs: readonly OptimizationRunTiming[], rules: MemoryOptimizationRules, nowMs: number): boolean {\n const ordered = [...runs].sort((left, right) => left.startedAtMs - right.startedAtMs);\n let failuresInRow = 0;\n for (const run of ordered) {\n if (run.status === \"running\") return true;\n failuresInRow = run.status === \"failed\" ? failuresInRow + 1 : 0;\n const pause = run.status === \"failed\" ? pauseAfterFailure(rules, failuresInRow) : rules.minIntervalMs;\n if (nowMs - (run.endedAtMs ?? run.startedAtMs) < pause) return true;\n }\n return false;\n}\n\n/** Ответ отката слияния на ошибку проверки: повтор записи — `duplicate`, всё остальное (число, объём) — `limit_reached`. */\nexport function revertRefusal(error: MemoryError): \"limit_reached\" | \"duplicate\" {\n return error.kind === \"duplicate\" ? \"duplicate\" : \"limit_reached\";\n}\n","/** Соединение PostgreSQL, которое нужно помощнику миграций. Подходит `PoolClient` из `pg`: core от `pg` не зависит. */\nexport interface PostgresMigrationConnection {\n query(text: string, values?: unknown[]): Promise<{ rows: unknown[] }>;\n release(): void;\n}\n\nexport interface PostgresMigrationOptions<Connection extends PostgresMigrationConnection> {\n /** Новое соединение из пула, например `() => pool.connect()`; на каждую попытку — своё. */\n connect(): Promise<Connection>;\n /** Своя блокировка пакета: одновременно мигрирует только одна копия этого пакета. */\n lockKey: number;\n /** Миграции пакета на этом соединении, например `migrate(drizzle(connection), …)`. */\n migrate(connection: Connection): Promise<void>;\n /** Паузы перед повторами, если всё же столкнулись с соседом. */\n retryDelaysMs?: readonly number[];\n}\n\n/** Схема всех пакетов SDK. */\nexport const AGENT_CHAT_SCHEMA = \"agent_chat\";\n\n/** Общая блокировка на создание схемы: одна на все пакеты, держится только на `create schema`. */\nexport const AGENT_CHAT_SCHEMA_LOCK_KEY = 7_311_900_441;\n\nconst DEFAULT_RETRY_DELAYS_MS = [25, 50, 100, 200];\n\n/** Коды PostgreSQL «такое уже есть»: сосед создал то же самое одновременно с нами. */\nconst DUPLICATE_CODES = new Set([\"23505\", \"42P06\", \"42P07\", \"42710\"]);\n\n/**\n * Создаёт схему `agent_chat` и применяет миграции пакета так, чтобы пакеты могли стартовать одновременно на пустой базе.\n * Схема создаётся под общей блокировкой, затем она отпускается и берётся своя блокировка пакета: блокировки не\n * вложены, поэтому взаимной блокировки нет, а разные пакеты ждут друг друга только на создании схемы.\n * Столкнулись всё же («уже есть») — повтор с начала на новом соединении.\n */\nexport async function migratePostgresSchema<Connection extends PostgresMigrationConnection>(options: PostgresMigrationOptions<Connection>): Promise<void> {\n const delays = options.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;\n for (let attempt = 0; ; attempt++) {\n try {\n return await migrateOnce(options);\n } catch (error) {\n if (attempt >= delays.length || !isDuplicateError(error)) throw error;\n await new Promise((resolve) => setTimeout(resolve, delays[attempt]));\n }\n }\n}\n\nasync function migrateOnce<Connection extends PostgresMigrationConnection>(options: PostgresMigrationOptions<Connection>): Promise<void> {\n const connection = await options.connect();\n try {\n if (!(await schemaExists(connection))) {\n await underLock(connection, AGENT_CHAT_SCHEMA_LOCK_KEY, () => connection.query(`create schema if not exists \"${AGENT_CHAT_SCHEMA}\"`));\n }\n await underLock(connection, options.lockKey, () => options.migrate(connection));\n } finally {\n connection.release();\n }\n}\n\nasync function schemaExists(connection: PostgresMigrationConnection): Promise<boolean> {\n const result = await connection.query(\"select 1 from pg_namespace where nspname = $1\", [AGENT_CHAT_SCHEMA]);\n return result.rows.length > 0;\n}\n\nasync function underLock(connection: PostgresMigrationConnection, key: number, work: () => Promise<unknown>): Promise<void> {\n await connection.query(\"select pg_advisory_lock($1)\", [key]);\n try {\n await work();\n } finally {\n await connection.query(\"select pg_advisory_unlock($1)\", [key]);\n }\n}\n\n/** Ошибка «уже есть» — у самой ошибки или в цепочке причин (drizzle заворачивает ошибку `pg` в свою). */\nfunction isDuplicateError(error: unknown): boolean {\n for (let current = error, depth = 0; current && depth < 5; current = (current as { cause?: unknown }).cause, depth++) {\n const code = (current as { code?: unknown }).code;\n if (typeof code === \"string\" && DUPLICATE_CODES.has(code)) return true;\n }\n return false;\n}\n","/**\n * Защита от запросов с чужих страниц. Браузер сам прикладывает к запросу куки человека, даже если запрос\n * отправила чужая страница (обычная HTML-форма, WebSocket). Отличить свою страницу от чужой помогают заголовки,\n * которые ставит только браузер: `Sec-Fetch-Site` и `Origin`. Порядок проверки как в Go 1.25 (CrossOriginProtection).\n */\n\nexport interface RequestOriginCheckOptions {\n /**\n * Чужие страницы, которым можно слать запросы с куками человека, например `[\"https://school.getcourse.ru\"]`\n * (схема, хост, порт). Шаблон `https://*.example.com` пускает все поддомены `example.com`, но не сам домен\n * и не `evil-example.com`. Свой сайт в список вносить не нужно.\n */\n allowedOrigins?: readonly string[] | undefined;\n /** Отказ: вызывается один раз на каждый новый адрес страницы, чтобы журнал не забивался. */\n onRefused?: (refusal: OriginRefusal) => void;\n}\n\n/**\n * Почему запрос отклонён. `cross_site` — браузер сам сказал, что страница чужая. `host_mismatch` — браузер старый\n * или адрес не https, и Origin не совпал ни с одним именем сервера (`hosts`): так бывает за прокси, который подменил Host.\n */\nexport type OriginRefusal = { origin: string; reason: \"cross_site\" } | { origin: string; reason: \"host_mismatch\"; hosts: string[] };\n\n/** Подсказка для журнала: что поправить, если отклонена своя страница. */\nexport function originRefusalHint(refusal: OriginRefusal): string {\n if (refusal.reason === \"host_mismatch\") {\n return (\n `отклонён запрос со страницы ${refusal.origin}: Origin не совпал с Host (${refusal.hosts.join(\", \")}). ` +\n \"Если сервер за прокси, за прокси передайте Host/X-Forwarded-Host (nginx: proxy_set_header Host $host; \" +\n \"proxy_set_header X-Forwarded-Host $host;). Если это чужая для сервера, но ваша страница, добавьте её адрес в allowedOrigins.\"\n );\n }\n return `отклонён запрос с чужой страницы ${refusal.origin}. Если это ваша страница, добавьте её адрес в allowedOrigins.`;\n}\n\n/** `true` — запрос со своей страницы, со страницы из списка или не из браузера. Метод запроса не смотрит. */\nexport type RequestOriginCheck = (request: Request) => boolean;\n\n/** Чтения (GET, HEAD, OPTIONS) не меняют данных: их ответ браузер чужой странице всё равно не покажет. */\nexport function isSafeMethod(method: string): boolean {\n const upper = method.toUpperCase();\n return upper === \"GET\" || upper === \"HEAD\" || upper === \"OPTIONS\";\n}\n\n/** Сколько разных отклонённых адресов запоминается для журнала. */\nconst MAX_REPORTED_ORIGINS = 100;\nconst MAX_REPORTED_LENGTH = 200;\n\nexport function createRequestOriginCheck(options: RequestOriginCheckOptions = {}): RequestOriginCheck {\n const isAllowed = allowedOriginsMatcher(options.allowedOrigins ?? []);\n const reported = new Set<string>();\n const refuse = (refusal: OriginRefusal): false => {\n const shown = refusal.origin.slice(0, MAX_REPORTED_LENGTH);\n if (options.onRefused && !reported.has(shown) && reported.size < MAX_REPORTED_ORIGINS) {\n reported.add(shown);\n options.onRefused({ ...refusal, origin: shown });\n }\n return false;\n };\n return (request) => {\n const origin = request.headers.get(\"origin\");\n if (origin !== null && isAllowed(origin)) return true;\n const site = request.headers.get(\"sec-fetch-site\");\n if (site !== null) {\n if (site === \"same-origin\" || site === \"none\") return true;\n return refuse({ origin: origin ?? `(без Origin, Sec-Fetch-Site: ${site})`, reason: \"cross_site\" });\n }\n // Нет ни того ни другого — не браузер (сервер, curl): куки человека чужая страница так не приложит.\n if (origin === null) return true;\n // Старый браузер без Sec-Fetch-Site: Origin должен совпасть с адресом самого сервера.\n const host = originHost(origin);\n const hosts = hostsOf(request);\n if (host !== null && hosts.has(host)) return true;\n return refuse({ origin, reason: \"host_mismatch\", hosts: [...hosts] });\n };\n}\n\n/** Метка вместо `*`: адрес с ней разбирает URL, а в настоящем имени хоста она не встречается. */\nconst WILDCARD_LABEL = \"wildcard-label-0\";\n\n/** Точные адреса и шаблоны поддоменов из `allowedOrigins`; неверная запись — ошибка сразу. */\nfunction allowedOriginsMatcher(entries: readonly string[]): (origin: string) => boolean {\n const exact = new Set<string>();\n const subdomainsOf: Array<{ protocol: string; port: string; suffix: string }> = [];\n for (const entry of entries) {\n const wildcard = /^(https?:\\/\\/)\\*\\.([^*]+)$/.exec(entry);\n const url = parsePageAddress(wildcard ? `${wildcard[1]}${WILDCARD_LABEL}.${wildcard[2]}` : entry);\n if (url === null || (!wildcard && entry.includes(\"*\"))) throw wrongEntry(entry);\n if (!wildcard) {\n exact.add(url.origin);\n continue;\n }\n const suffix = url.hostname.slice(WILDCARD_LABEL.length);\n // `*.com` пустил бы весь интернет: после звёздочки нужен домен хотя бы из двух частей (или localhost).\n // Точка в конце (`*.com.`) — тот же `*.com`. Общие суффиксы вроде `*.co.uk` так не отличить: вписывают свой домен.\n const tooBroad = suffix.endsWith(\".\") || (!suffix.slice(1).includes(\".\") && suffix !== \".localhost\");\n if (!url.hostname.startsWith(`${WILDCARD_LABEL}.`) || tooBroad) throw wrongEntry(entry);\n subdomainsOf.push({ protocol: url.protocol, port: url.port, suffix });\n }\n return (origin) => {\n const url = parseOrigin(origin);\n if (url === null) return false;\n if (exact.has(url.origin)) return true;\n return subdomainsOf.some(\n (pattern) =>\n url.protocol === pattern.protocol && url.port === pattern.port && url.hostname.endsWith(pattern.suffix) && url.hostname.length > pattern.suffix.length,\n );\n };\n}\n\n/** Адрес страницы: http(s), без пути, запроса, якоря и имени пользователя. Иначе `null`. */\nfunction parsePageAddress(entry: string): URL | null {\n try {\n const url = new URL(entry);\n const bare = url.pathname === \"/\" && !url.search && !url.hash && !url.username;\n return (url.protocol === \"http:\" || url.protocol === \"https:\") && bare ? url : null;\n } catch {\n return null;\n }\n}\n\nfunction wrongEntry(entry: string): Error {\n return new Error(\n `allowedOrigins: «${entry}» не адрес страницы; нужен вид https://app.example, http://localhost:5173 или https://*.example.com ` +\n \"(в шаблоне — свой домен: общие суффиксы вроде *.co.uk проверка не распознаёт)\",\n );\n}\n\nfunction parseOrigin(origin: string): URL | null {\n try {\n return new URL(origin);\n } catch {\n return null;\n }\n}\n\n/** Хост с портом из Origin; `null` и не адрес — `null`. */\nfunction originHost(origin: string): string | null {\n try {\n const url = new URL(origin);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? url.host : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Под какими именами сервер знает себя: `Host` и `X-Forwarded-Host` от прокси. Адрес запроса — только без `Host`:\n * при подъёме WebSocket в Node (`@hono/node-ws`) он собран как `http://localhost/…` и порта в нём нет.\n */\nfunction hostsOf(request: Request): Set<string> {\n const host = request.headers.get(\"host\");\n const hosts = new Set<string>([host ? host.trim().toLowerCase() : new URL(request.url).host]);\n for (const forwarded of request.headers.get(\"x-forwarded-host\")?.split(\",\") ?? []) {\n if (forwarded.trim()) hosts.add(forwarded.trim().toLowerCase());\n }\n return hosts;\n}\n","import type { ModelChoice } from \"../models/model-choice\";\nimport type { MemoryNote, MemoryNoteInput } from \"../memory/long-term-memory\";\nimport { isOptimizationBlocked, revertRefusal } from \"../memory/memory-optimization\";\nimport type { MemoryMergeGroup, MemoryOptimizationRun, MergedMemoryNote } from \"../memory/memory-optimization\";\nimport type { Chat, StoredBranchSelection, StoredCompaction, StoredMessage, UsageRecord, UsageSum } from \"../conversation/types\";\nimport {\n aliveAtByLocalClock,\n assertKnownFormatVersion,\n ChatAlreadyExistsError,\n DuplicateChatSubjectError,\n DuplicateMessageError,\n MessageAlreadyFinishedError,\n MessageNotAwaitingError,\n sameMessageParts,\n silenceBoundaryByLocalClock,\n} from \"./chat-storage\";\nimport { assertChatDataFilter, chatDataFields, chatDataMatches, chatDataOfInput, mergeChatData } from \"./chat-data\";\nimport type { ChatListOptions, ChatStorage, ChatSubjectFilter, SpendingLimitStateRecord, StoredPendingBurst } from \"./chat-storage\";\n\n/** Хранилище в памяти процесса — для тестов. Копирует данные на входе и выходе, как база. */\ntype StoredNote = MemoryNote & { mergedInto?: string };\n\n/** Сумма записей расхода по ключу (`group` — поля ключа: пара «модель × провайдер», у суммы по чатам — ещё чат). */\nfunction sumRecords<K extends Pick<UsageSum, \"modelId\" | \"providerId\">>(records: readonly UsageRecord[], group: (record: UsageRecord) => K): Array<K & UsageSum> {\n const sums = new Map<string, K & UsageSum>();\n for (const record of records) {\n const fields = group(record);\n const key = Object.values(fields).join(\"\\u0000\");\n const sum = sums.get(key) ?? { ...fields, inputTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, outputTokens: 0, reasoningTokens: 0, costUsd: 0 };\n sum.inputTokens += record.inputTokens;\n sum.cachedInputTokens += record.cachedInputTokens;\n sum.cacheWriteTokens += record.cacheWriteTokens;\n sum.outputTokens += record.outputTokens;\n sum.reasoningTokens += record.reasoningTokens;\n sum.costUsd += record.costUsd;\n sums.set(key, sum);\n }\n return [...sums.values()];\n}\n\nexport function createInMemoryChatStorage(): ChatStorage {\n const chats = new Map<string, Chat>();\n const messages = new Map<string, StoredMessage>();\n /** Время последнего знака жизни идущих ответов по id: при чтении не отдаётся. */\n const aliveAt = new Map<string, string>();\n /** Аренды хода по чатам: кто держит и до какого времени (мс по часам этой машины). */\n const turnLeases = new Map<string, { holder: string; until: number }>();\n /** Ждущие пачки по чатам. */\n const pendingBursts = new Map<string, StoredPendingBurst>();\n const compactions: StoredCompaction[] = [];\n const branchSelections: StoredBranchSelection[] = [];\n const usageRecords: UsageRecord[] = [];\n const userModelChoices = new Map<string, ModelChoice>();\n const voiceChoices = new Map<string, string>();\n const spendingLimitStates = new Map<string, SpendingLimitStateRecord>();\n /** Долгая память: у каждого человека свои записи; у скрытых оригиналов слияния есть `mergedInto`. */\n const memoryNotes = new Map<string, Map<string, StoredNote>>();\n const optimizationRuns = new Map<string, MemoryOptimizationRun[]>();\n\n function streamingMessage(messageId: string): StoredMessage {\n const message = messages.get(messageId);\n if (!message) throw new Error(`Сообщения ${messageId} нет`);\n if (message.status !== \"streaming\") throw new MessageAlreadyFinishedError(messageId);\n return message;\n }\n\n /** Сообщение для чтения: копия, `voiceCut` — `null`, если не задан. */\n function readable(message: StoredMessage): StoredMessage {\n const copy = structuredClone(message);\n return { ...copy, voiceCut: copy.voiceCut ?? null };\n }\n\n function ownerNotes(ownerId: string): Map<string, StoredNote> {\n let notes = memoryNotes.get(ownerId);\n if (!notes) {\n notes = new Map();\n memoryNotes.set(ownerId, notes);\n }\n return notes;\n }\n\n function ownerRuns(ownerId: string): MemoryOptimizationRun[] {\n let runs = optimizationRuns.get(ownerId);\n if (!runs) {\n runs = [];\n optimizationRuns.set(ownerId, runs);\n }\n return runs;\n }\n\n /** Порядок списка по договору: `updatedAt`, затем `createdAt`, затем `id` — новее и больше первыми. */\n function newestFirst<T extends MemoryNote>(notes: T[]): T[] {\n return notes.sort(\n (left, right) =>\n right.updatedAt.localeCompare(left.updatedAt) ||\n right.createdAt.localeCompare(left.createdAt) ||\n (right.id > left.id ? 1 : right.id < left.id ? -1 : 0),\n );\n }\n\n /** Обычная запись без служебного признака скрытия. */\n function publicNote({ mergedInto: _hidden, ...note }: StoredNote): MemoryNote {\n return note;\n }\n\n /** Как база с NOT NULL: разделы — список строк, текст — строка. */\n function assertWellFormedNote(note: MemoryNoteInput): void {\n if (!Array.isArray(note?.categories) || !note.categories.every((category) => typeof category === \"string\")) {\n throw new TypeError(\"Разделы записи памяти — список строк\");\n }\n if (typeof note.content !== \"string\") throw new TypeError(\"Текст записи памяти — строка\");\n }\n\n /** Записи, которые видит человек: без скрытых оригиналов, копиями. */\n function visibleNotes(ownerId: string): MemoryNote[] {\n const visible = [...ownerNotes(ownerId).values()].filter((note) => !note.mergedInto);\n return structuredClone(newestFirst(visible).map(publicNote));\n }\n\n function insertNotes(ownerId: string, inputs: MemoryNoteInput[]): MemoryNote[] {\n // Сначала проверяем весь пакет: негодная запись не оставляет в памяти и остальных.\n inputs.forEach(assertWellFormedNote);\n const now = new Date().toISOString();\n const created = inputs.map(\n ({ categories, content }): MemoryNote => ({\n id: crypto.randomUUID(),\n categories: [...categories],\n content,\n version: 1,\n createdAt: now,\n updatedAt: now,\n }),\n );\n const notes = ownerNotes(ownerId);\n for (const note of created) notes.set(note.id, note);\n return structuredClone(created);\n }\n\n function updateNote(ownerId: string, id: string, expectedVersion: number, input: MemoryNoteInput): MemoryNote | \"not_found\" | \"stale_version\" {\n assertWellFormedNote(input);\n const notes = ownerNotes(ownerId);\n const note = notes.get(id);\n if (!note || note.mergedInto) return \"not_found\";\n if (note.version !== expectedVersion) return \"stale_version\";\n const updated: MemoryNote = {\n ...note,\n categories: [...input.categories],\n content: input.content,\n version: note.version + 1,\n updatedAt: new Date().toISOString(),\n };\n notes.set(id, updated);\n return structuredClone(updated);\n }\n\n /** Как уникальный индекс (владелец, ключ): чат без ключа не мешает никому. */\n function assertSubjectFree(chat: Pick<Chat, \"ownerId\" | \"subjectKey\">): void {\n if (chat.subjectKey === undefined) return;\n for (const existing of chats.values()) {\n if (existing.ownerId === chat.ownerId && existing.subjectKey === chat.subjectKey) {\n throw new DuplicateChatSubjectError(chat.ownerId, chat.subjectKey);\n }\n }\n }\n\n function sortedMessages(chatId: string): StoredMessage[] {\n return [...messages.values()]\n .filter((message) => message.chatId === chatId)\n .sort((left, right) => left.createdAt.localeCompare(right.createdAt));\n }\n\n return {\n async createChat({ createdAt, ...input }) {\n const at = createdAt ?? new Date().toISOString();\n const chat: Chat = withChatData(withoutUndefined({ ...structuredClone(input), createdAt: at, updatedAt: at }));\n assertSubjectFree(chat);\n chats.set(chat.id, chat);\n return structuredClone(chat);\n },\n\n async findChatBySubject(ownerId, subjectKey) {\n const chat = [...chats.values()].find((existing) => existing.ownerId === ownerId && existing.subjectKey === subjectKey);\n return chat ? structuredClone(chat) : null;\n },\n\n async importChat(input, imported) {\n // Сначала все проверки: при ошибке ничего не записано.\n if (chats.has(input.id)) throw new ChatAlreadyExistsError(input.id);\n const chat: Chat = withChatData(withoutUndefined(structuredClone(input)));\n assertSubjectFree(chat);\n const ids = new Set<string>();\n for (const message of imported) {\n if (messages.has(message.id) || ids.has(message.id)) throw new DuplicateMessageError(message.id);\n ids.add(message.id);\n }\n chats.set(chat.id, chat);\n for (const message of imported) {\n const { alive: _alive, ...stored } = message;\n messages.set(message.id, structuredClone(stored));\n }\n },\n\n async setAfterTurnState(messageId, state, options) {\n const message = messages.get(messageId);\n if (!message || (state !== \"pending\" && message.afterTurn !== \"pending\")) return false;\n const parts = options?.appendParts ? [...message.parts, ...structuredClone(options.appendParts)] : message.parts;\n messages.set(messageId, { ...message, parts, afterTurn: state });\n // Отметка ожидания хука — в той же колонке, что знак жизни: у законченного ответа она больше ни для чего.\n if (state === \"pending\") aliveAt.set(messageId, aliveAtByLocalClock(\"now\"));\n return true;\n },\n\n async listPendingAfterTurn(filter) {\n const boundary = filter?.pendingForMs === undefined ? null : silenceBoundaryByLocalClock(filter.pendingForMs);\n return [...messages.values()]\n .filter((message) => message.afterTurn === \"pending\")\n .filter((message) => boundary === null || (aliveAt.get(message.id) ?? message.createdAt) < boundary)\n .map((message) => message.id);\n },\n\n async getChat(chatId) {\n const chat = chats.get(chatId);\n return chat ? structuredClone(chat) : null;\n },\n\n async updateChatModel(chatId, model) {\n const chat = chats.get(chatId);\n if (!chat) return;\n const { reasoning: _dropped, ...rest } = chat;\n chats.set(chatId, { ...rest, ...model });\n },\n\n async listChats(ownerId, options?: ChatListOptions) {\n if (options?.data) assertChatDataFilter(options.data);\n const owned = [...chats.values()].filter(\n (chat) => chat.ownerId === ownerId && matchesSubjects(chat, options?.subjects) && (!options?.data || chatDataMatches(chat.data, options.data)),\n );\n owned.sort(\n (left, right) =>\n right.updatedAt.localeCompare(left.updatedAt) ||\n right.createdAt.localeCompare(left.createdAt) ||\n (right.id > left.id ? 1 : right.id < left.id ? -1 : 0),\n );\n return structuredClone(owned);\n },\n\n async updateChatData(chatId, patch, { maxBytes }) {\n const chat = chats.get(chatId);\n if (!chat) return null;\n // Слияние и проверка размера до записи: при ошибке данные прежние.\n const merged = mergeChatData(chat.data, patch, maxBytes);\n const { data: _data, subjectData: _subjectData, ...rest } = chat;\n const updated: Chat = { ...rest, ...chatDataFields(merged) };\n chats.set(chatId, updated);\n return structuredClone(updated);\n },\n\n async renameChat(chatId, title) {\n const chat = chats.get(chatId);\n if (!chat) return;\n const { title: _dropped, ...rest } = chat;\n chats.set(chatId, title === null ? rest : { ...rest, title });\n },\n\n async setChatClosed(chatId, closed) {\n const chat = chats.get(chatId);\n if (!chat) return;\n const { closed: _dropped, ...rest } = chat;\n chats.set(chatId, closed === null ? rest : { ...rest, closed: { code: closed.code, message: closed.message } });\n },\n\n async deleteChat(chatId) {\n if (!chats.delete(chatId)) return;\n turnLeases.delete(chatId);\n pendingBursts.delete(chatId);\n for (const message of [...messages.values()]) {\n if (message.chatId !== chatId) continue;\n messages.delete(message.id);\n aliveAt.delete(message.id);\n }\n // Записи сжатия — на месте, с обратным ходом, чтобы индексы не сдвигались.\n for (let index = compactions.length - 1; index >= 0; index -= 1) {\n if (compactions[index]!.chatId === chatId) compactions.splice(index, 1);\n }\n for (let index = branchSelections.length - 1; index >= 0; index -= 1) {\n if (branchSelections[index]!.chatId === chatId) branchSelections.splice(index, 1);\n }\n },\n\n async getMessage(messageId) {\n const message = messages.get(messageId);\n if (!message) return null;\n assertKnownFormatVersion(message);\n return readable(message);\n },\n\n async listMessages(chatId) {\n const found = sortedMessages(chatId);\n found.forEach(assertKnownFormatVersion);\n return found.map(readable);\n },\n\n async listMessageLinks(chatId) {\n return sortedMessages(chatId).map(({ id, parentId, createdAt }) => ({ id, parentId, createdAt }));\n },\n\n async listMessagePartsOfTypes(chatId, types) {\n return sortedMessages(chatId).map(({ id, parentId, createdAt, parts }) => ({\n id,\n parentId,\n createdAt,\n parts: structuredClone(parts.filter((part) => types.includes(part.type))),\n }));\n },\n\n async insertMessage(message) {\n if (messages.has(message.id)) throw new DuplicateMessageError(message.id);\n // Как внешний ключ в базе: сообщение без чата не пишется.\n const chat = chats.get(message.chatId);\n if (!chat) throw new Error(`Чата ${message.chatId} нет`);\n const { alive, ...stored } = message;\n messages.set(message.id, structuredClone(stored));\n if (alive) aliveAt.set(message.id, aliveAtByLocalClock(alive));\n chats.set(chat.id, { ...chat, updatedAt: message.createdAt });\n },\n\n async updateStreamingMessage(messageId, patch) {\n const message = streamingMessage(messageId);\n const { alive, ...rest } = structuredClone(patch);\n messages.set(messageId, { ...message, ...rest });\n if (alive) aliveAt.set(messageId, aliveAtByLocalClock(alive));\n },\n\n async markAnswerAlive(messageId) {\n streamingMessage(messageId);\n aliveAt.set(messageId, aliveAtByLocalClock(\"now\"));\n },\n\n async finishMessage(messageId, final) {\n const message = streamingMessage(messageId);\n messages.set(messageId, { ...message, ...structuredClone(final) });\n if (final.afterTurn === \"pending\") aliveAt.set(messageId, aliveAtByLocalClock(\"now\"));\n },\n\n async updateAwaitingMessage(messageId, patch) {\n const message = messages.get(messageId);\n if (!message) throw new Error(`Сообщения ${messageId} нет`);\n if (message.status !== \"awaiting_approval\") throw new MessageNotAwaitingError(messageId);\n if (patch.expectedParts && !sameMessageParts(message.parts, patch.expectedParts)) throw new MessageNotAwaitingError(messageId);\n messages.set(messageId, {\n ...message,\n parts: structuredClone(patch.parts),\n ...(patch.status && { status: patch.status }),\n });\n if (patch.alive) aliveAt.set(messageId, aliveAtByLocalClock(patch.alive));\n },\n\n async listStreamingMessageIds() {\n return [...messages.values()].filter((message) => message.status === \"streaming\").map((message) => message.id);\n },\n\n async listSilentAnswerIds({ silentForMs, chatId }) {\n const boundary = silenceBoundaryByLocalClock(silentForMs);\n return [...messages.values()]\n .filter((message) => message.status === \"streaming\" && (chatId === undefined || message.chatId === chatId))\n .filter((message) => (aliveAt.get(message.id) ?? message.createdAt) < boundary)\n .map((message) => message.id);\n },\n\n async claimChatTurn(chatId, holder, leaseMs) {\n const now = Date.now();\n const lease = turnLeases.get(chatId);\n if (lease && lease.holder !== holder && lease.until > now) return false;\n turnLeases.set(chatId, { holder, until: now + leaseMs });\n return true;\n },\n\n async releaseChatTurn(chatId, holder) {\n if (turnLeases.get(chatId)?.holder === holder) turnLeases.delete(chatId);\n },\n\n async clearChatTurnLeases() {\n turnLeases.clear();\n },\n\n async savePendingBurst(burst) {\n pendingBursts.set(burst.chatId, structuredClone(burst));\n },\n\n async takeAbandonedBursts({ abandonedBefore, holder, heldUntil, limit, exceptHolder }) {\n const abandoned = [...pendingBursts.values()]\n .filter((burst) => Date.parse(burst.heldUntil) <= Date.parse(abandonedBefore) && burst.holder !== exceptHolder)\n .sort((first, second) => Date.parse(first.dueAt) - Date.parse(second.dueAt))\n .slice(0, limit);\n return abandoned.map((burst) => {\n const taken = { ...burst, holder, heldUntil };\n pendingBursts.set(burst.chatId, taken);\n return structuredClone(taken);\n });\n },\n\n async renewPendingBurst({ chatId, messageId, holder, heldUntil }) {\n const burst = pendingBursts.get(chatId);\n if (burst?.messageId !== messageId || burst.holder !== holder) return false;\n pendingBursts.set(chatId, { ...burst, heldUntil });\n return true;\n },\n\n async removePendingBurst(chatId, messageId) {\n if (pendingBursts.get(chatId)?.messageId === messageId) pendingBursts.delete(chatId);\n },\n\n async insertCompaction(record) {\n if (compactions.some((existing) => existing.id === record.id)) throw new Error(`Запись сжатия ${record.id} уже есть`);\n compactions.push(structuredClone(record));\n },\n\n async listCompactions(chatId) {\n return structuredClone(compactions.filter((record) => record.chatId === chatId));\n },\n\n async insertBranchSelection(record) {\n if (branchSelections.some((existing) => existing.id === record.id)) throw new Error(`Выбор ветки ${record.id} уже есть`);\n branchSelections.push(structuredClone(record));\n },\n\n async listBranchSelections(chatId) {\n return structuredClone(branchSelections.filter((record) => record.chatId === chatId));\n },\n\n async insertUsageRecord(record) {\n if (usageRecords.some((existing) => existing.id === record.id)) throw new Error(`Запись расхода ${record.id} уже есть`);\n usageRecords.push(structuredClone(record));\n },\n\n async hasUsageRecord(id) {\n return usageRecords.some((record) => record.id === id);\n },\n\n async sumUsage({ ownerId, chatId, messageId, source, since }) {\n return sumRecords(\n usageRecords.filter(\n (record) =>\n (ownerId === undefined || record.ownerId === ownerId) &&\n (chatId === undefined || record.chatId === chatId) &&\n (messageId === undefined || record.messageId === messageId) &&\n (source === undefined || record.source === source) &&\n (since === undefined || record.createdAt >= since),\n ),\n (record) => ({ modelId: record.modelId, providerId: record.providerId }),\n );\n },\n\n async sumUsageByChat({ chatIds, ownerId, source, since }) {\n const wanted = new Set(chatIds);\n return sumRecords(\n usageRecords.filter(\n (record) =>\n wanted.has(record.chatId) &&\n (ownerId === undefined || record.ownerId === ownerId) &&\n (source === undefined || record.source === source) &&\n (since === undefined || record.createdAt >= since),\n ),\n (record) => ({ chatId: record.chatId, modelId: record.modelId, providerId: record.providerId }),\n );\n },\n\n async setVoiceCut({ ownerId, chatId, messageId, voiceCut }) {\n const message = messages.get(messageId);\n if (!message || message.chatId !== chatId || chats.get(chatId)?.ownerId !== ownerId) return;\n messages.set(messageId, { ...message, voiceCut: structuredClone(voiceCut) });\n },\n\n async getSpendingLimitState(key) {\n const record = spendingLimitStates.get(key);\n return record ? { ...record } : null;\n },\n\n async saveSpendingLimitState(key, value, expectedVersion) {\n const current = spendingLimitStates.get(key);\n if ((current?.version ?? null) !== expectedVersion) return false;\n spendingLimitStates.set(key, { value, version: (current?.version ?? 0) + 1 });\n return true;\n },\n\n async deleteSpendingLimitStates(keys) {\n for (const key of keys) spendingLimitStates.delete(key);\n },\n\n async getVoiceChoice(ownerId) {\n return voiceChoices.get(ownerId) ?? null;\n },\n\n async setVoiceChoice(ownerId, voiceId) {\n voiceChoices.set(ownerId, voiceId);\n },\n\n async getUserModelChoice(ownerId) {\n const choice = userModelChoices.get(ownerId);\n return choice ? structuredClone(choice) : null;\n },\n\n async saveUserModelChoice(ownerId, choice) {\n userModelChoices.set(ownerId, structuredClone(choice));\n },\n\n async listMemoryNotes(ownerId) {\n return visibleNotes(ownerId);\n },\n\n async insertMemoryNotes(ownerId, inputs) {\n return insertNotes(ownerId, inputs);\n },\n\n // Всё выполняется без ожидания, поэтому между проверкой и вставкой никто не успеет вклиниться.\n async insertMemoryNotesChecked(ownerId, inputs, guard) {\n const error = guard(visibleNotes(ownerId));\n return error ?? insertNotes(ownerId, inputs);\n },\n\n async updateMemoryNote(ownerId, id, expectedVersion, input) {\n return updateNote(ownerId, id, expectedVersion, input);\n },\n\n async updateMemoryNoteChecked(ownerId, id, expectedVersion, input, guard) {\n return guard(visibleNotes(ownerId)) ?? updateNote(ownerId, id, expectedVersion, input);\n },\n\n async deleteMemoryNote(ownerId, id, expectedVersion) {\n const notes = ownerNotes(ownerId);\n const note = notes.get(id);\n if (!note || note.mergedInto) return \"not_found\";\n if (note.version !== expectedVersion) return \"stale_version\";\n notes.delete(id);\n for (const [hiddenId, hidden] of notes) if (hidden.mergedInto === id) notes.delete(hiddenId);\n return \"ok\";\n },\n\n async clearMemoryNotes(ownerId) {\n const count = [...ownerNotes(ownerId).values()].filter((note) => !note.mergedInto).length;\n memoryNotes.delete(ownerId);\n optimizationRuns.delete(ownerId);\n return count;\n },\n\n async claimMemoryOptimizationRun(ownerId, rules, now) {\n const runs = ownerRuns(ownerId);\n const nowMs = Date.parse(now);\n for (const run of runs) {\n const leaseEnd = Date.parse(run.startedAt) + rules.leaseMs;\n if (run.status === \"running\" && nowMs >= leaseEnd) Object.assign(run, { status: \"failed\", error: \"lease_expired\", finishedAt: new Date(leaseEnd).toISOString() });\n }\n const blocked = isOptimizationBlocked(\n runs.map((run) => ({ status: run.status, startedAtMs: Date.parse(run.startedAt), endedAtMs: run.finishedAt ? Date.parse(run.finishedAt) : null })),\n rules,\n nowMs,\n );\n if (blocked) return null;\n const run: MemoryOptimizationRun = { id: crypto.randomUUID(), startedAt: now, finishedAt: null, status: \"running\", groups: [], error: null, usageRecordId: null };\n runs.push(run);\n return structuredClone(run);\n },\n\n async finishMemoryOptimizationRun(ownerId, runId, outcome) {\n const run = ownerRuns(ownerId).find((candidate) => candidate.id === runId);\n if (run?.status !== \"running\") return \"not_found\";\n Object.assign(run, { status: outcome.status, finishedAt: outcome.finishedAt, error: outcome.error ?? null, usageRecordId: outcome.usageRecordId ?? null });\n return \"ok\";\n },\n\n async applyMemoryMerge(ownerId, merge) {\n const { merges } = merge;\n if (merges.length === 0) throw new TypeError(\"Слияние без склеек\");\n const seen = new Set<string>();\n for (const { result, originals } of merges) {\n assertWellFormedNote(result);\n if (originals.length === 0) throw new TypeError(\"Склейка без оригиналов\");\n for (const { id } of originals) {\n if (seen.has(id)) throw new TypeError(`Запись ${id} названа в двух склейках`);\n seen.add(id);\n }\n }\n const run = ownerRuns(ownerId).find((candidate) => candidate.id === merge.runId);\n if (run?.status !== \"running\") return \"run_not_active\";\n const notes = ownerNotes(ownerId);\n for (const { originals } of merges) {\n for (const { id, version } of originals) {\n const original = notes.get(id);\n if (!original || original.mergedInto || original.version !== version) return \"stale_version\";\n }\n }\n const now = new Date().toISOString();\n const groups: MemoryMergeGroup[] = merges.map(({ result, originals }) => {\n const created: StoredNote = { id: crypto.randomUUID(), categories: [...result.categories], content: result.content, version: 1, createdAt: now, updatedAt: now };\n notes.set(created.id, created);\n for (const { id } of originals) notes.get(id)!.mergedInto = created.id;\n return { resultId: created.id, originalIds: originals.map(({ id }) => id), resultVersion: 1 };\n });\n Object.assign(run, { status: \"applied\", finishedAt: merge.finishedAt, groups, usageRecordId: merge.usageRecordId ?? null });\n return structuredClone(run);\n },\n\n async revertMemoryMerge(ownerId, runId, guard) {\n const run = ownerRuns(ownerId).find((candidate) => candidate.id === runId);\n if (run?.status !== \"applied\") return \"not_found\";\n const notes = ownerNotes(ownerId);\n const intact = run.groups.every(\n ({ resultId, originalIds, resultVersion }) =>\n notes.get(resultId)?.version === resultVersion && !notes.get(resultId)?.mergedInto && originalIds.every((id) => notes.get(id)?.mergedInto === resultId),\n );\n if (!intact) return \"conflict\";\n if (guard) {\n const resultIds = new Set(run.groups.map(({ resultId }) => resultId));\n const restoredIds = new Set(run.groups.flatMap(({ originalIds }) => originalIds));\n const inPlay = [...notes.values()];\n const remaining = newestFirst(inPlay.filter((note) => !note.mergedInto && !resultIds.has(note.id))).map(publicNote);\n const restored = newestFirst(inPlay.filter((note) => restoredIds.has(note.id))).map(publicNote);\n const error = guard(structuredClone(remaining), structuredClone(restored));\n if (error) return revertRefusal(error);\n }\n for (const { resultId, originalIds } of run.groups) {\n notes.delete(resultId);\n for (const id of originalIds) delete notes.get(id)!.mergedInto;\n }\n run.status = \"reverted\";\n return \"ok\";\n },\n\n async listMemoryOptimizationRuns(ownerId, filter) {\n const runs = ownerRuns(ownerId)\n .map((run, index) => ({ run, index }))\n .filter(({ run }) => !filter?.status || filter.status.includes(run.status))\n .sort((left, right) => right.run.startedAt.localeCompare(left.run.startedAt) || right.index - left.index)\n .map(({ run }) => run);\n return structuredClone(filter?.limit === undefined ? runs : runs.slice(0, filter.limit));\n },\n\n async listMergedMemoryNotes(ownerId) {\n const hidden = [...ownerNotes(ownerId).values()].filter((note): note is StoredNote & { mergedInto: string } => Boolean(note.mergedInto));\n return structuredClone(newestFirst(hidden) as MergedMemoryNote[]);\n },\n };\n}\n\n/** Подходит ли чат под фильтр `listChats` по ключу объекта. */\nfunction matchesSubjects(chat: Chat, subjects: ChatSubjectFilter = \"exclude\"): boolean {\n if (subjects === \"include\") return true;\n return subjects === \"only\" ? chat.subjectKey !== undefined : chat.subjectKey === undefined;\n}\n\n/** `data` и `subjectData` — один объект; данных нет — полей нет. */\nfunction withChatData<T extends Pick<Chat, \"data\" | \"subjectData\">>(chat: T): T {\n const { data: _data, subjectData: _subjectData, ...rest } = chat;\n return { ...rest, ...chatDataFields(chatDataOfInput(chat)) } as T;\n}\n\n/** Как база: поле без значения не хранится. */\nfunction withoutUndefined<T extends object>(value: T): T {\n return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined)) as T;\n}\n"],"mappings":"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","names":["run"]}
1
+ {"version":3,"sources":["../src/memory/memory-optimization.ts","../src/storage/postgres-migrations.ts","../src/request-origin/request-origin.ts","../src/storage/in-memory-storage.ts"],"sourcesContent":["import type { MemoryError, MemoryNote, MemoryNoteInput } from \"./long-term-memory\";\n\n/**\n * Состояние запуска оптимизации дублей: идёт → слито / нечего сливать / не удалось; слитое можно вернуть (`reverted`).\n * Запуск `running` у человека не больше одного.\n */\nexport type MemoryOptimizationStatus = \"running\" | \"applied\" | \"no_changes\" | \"failed\" | \"reverted\";\n\n/** Одна склейка: записи `originalIds` скрыты и подчинены новой записи `resultId`. */\nexport interface MemoryMergeGroup {\n resultId: string;\n originalIds: string[];\n /** Версия результата на момент слияния: пока она та же, результат не менялся и слияние можно вернуть. */\n resultVersion: number;\n}\n\n/** Запуск оптимизации. Владелец (`ownerId`) — свойство хранилища и наружу не выдаётся. */\nexport interface MemoryOptimizationRun {\n id: string;\n /** Время в формате ISO 8601. Часы даёт вызывающий: от них считаются срок между запусками и пауза после сбоя. */\n startedAt: string;\n finishedAt: string | null;\n status: MemoryOptimizationStatus;\n /** Склейки запуска; у не слитых запусков пусто. */\n groups: MemoryMergeGroup[];\n /** Причина сбоя одним словом-кодом; у остальных `null`. */\n error: string | null;\n /** Запись расхода модели, если вызывающий её знает. */\n usageRecordId: string | null;\n}\n\n/** Правила запуска: их проверяет хранилище в той же операции, что и берёт запуск, чтобы два процесса не начали его вдвоём. */\nexport interface MemoryOptimizationRules {\n /** Сколько должно пройти после законченного запуска (слито, нечего сливать, отменено) до следующего. */\n minIntervalMs: number;\n /**\n * Сколько должно пройти после неудачного запуска до повтора. Число — одна пауза на все неудачи; список — паузы после\n * 1-й, 2-й, … неудачи подряд (последняя держится дальше). Успешный, пустой или отменённый запуск начинает счёт заново.\n */\n retryAfterFailureMs: number | readonly number[];\n /** Сколько запуск `running` считается живым; после этого срока он становится неудачным (`failed`, ошибка `lease_expired`). */\n leaseMs: number;\n}\n\n/** Чем закончился запуск без слияния. */\nexport interface MemoryOptimizationOutcome {\n status: \"no_changes\" | \"failed\";\n finishedAt: string;\n error?: string | null;\n usageRecordId?: string | null;\n}\n\n/** Что слить: новая запись-результат вместо оригиналов; версии оригиналов — те, что видел планировщик. */\nexport interface MemoryMergeInput {\n runId: string;\n finishedAt: string;\n usageRecordId?: string | null;\n merges: { result: MemoryNoteInput; originals: { id: string; version: number }[] }[];\n}\n\n/** Скрытая запись слияния: оригинал, подчинённый результату. Список, блок памяти и поиск таких записей не видят. */\nexport interface MergedMemoryNote extends MemoryNote {\n mergedInto: string;\n}\n\nexport type MemoryMergeApplyResult = MemoryOptimizationRun | \"stale_version\" | \"run_not_active\";\n/** `limit_reached` и `duplicate` — возврат вышел бы за пределы памяти или вернул бы повтор записи, сделанной за это время. */\nexport type MemoryMergeRevertResult = \"ok\" | \"not_found\" | \"conflict\" | \"limit_reached\" | \"duplicate\";\n\n/** Запуск в том виде, что нужен для решения «можно ли начать новый»; время — миллисекунды. */\nexport interface OptimizationRunTiming {\n status: MemoryOptimizationStatus;\n startedAtMs: number;\n /** Конец запуска; у идущего его нет. */\n endedAtMs: number | null;\n}\n\n/** Пауза после неудачи с номером `failuresInRow` (с 1) подряд. */\nexport function pauseAfterFailure(rules: Pick<MemoryOptimizationRules, \"retryAfterFailureMs\">, failuresInRow: number): number {\n const { retryAfterFailureMs } = rules;\n if (typeof retryAfterFailureMs === \"number\") return retryAfterFailureMs;\n return retryAfterFailureMs[Math.min(failuresInRow, retryAfterFailureMs.length) - 1] ?? 0;\n}\n\n/**\n * Мешает ли история запусков начать новый: идёт другой, или не вышла пауза после последнего. Пауза после неудачи\n * растёт с числом неудач подряд. Все три хранилища решают этим одним правилом.\n */\nexport function isOptimizationBlocked(runs: readonly OptimizationRunTiming[], rules: MemoryOptimizationRules, nowMs: number): boolean {\n const ordered = [...runs].sort((left, right) => left.startedAtMs - right.startedAtMs);\n let failuresInRow = 0;\n for (const run of ordered) {\n if (run.status === \"running\") return true;\n failuresInRow = run.status === \"failed\" ? failuresInRow + 1 : 0;\n const pause = run.status === \"failed\" ? pauseAfterFailure(rules, failuresInRow) : rules.minIntervalMs;\n if (nowMs - (run.endedAtMs ?? run.startedAtMs) < pause) return true;\n }\n return false;\n}\n\n/** Ответ отката слияния на ошибку проверки: повтор записи — `duplicate`, всё остальное (число, объём) — `limit_reached`. */\nexport function revertRefusal(error: MemoryError): \"limit_reached\" | \"duplicate\" {\n return error.kind === \"duplicate\" ? \"duplicate\" : \"limit_reached\";\n}\n","/** Соединение PostgreSQL, которое нужно помощнику миграций. Подходит `PoolClient` из `pg`: core от `pg` не зависит. */\nexport interface PostgresMigrationConnection {\n query(text: string, values?: unknown[]): Promise<{ rows: unknown[] }>;\n release(): void;\n}\n\nexport interface PostgresMigrationOptions<Connection extends PostgresMigrationConnection> {\n /** Новое соединение из пула, например `() => pool.connect()`; на каждую попытку — своё. */\n connect(): Promise<Connection>;\n /** Своя блокировка пакета: одновременно мигрирует только одна копия этого пакета. */\n lockKey: number;\n /** Миграции пакета на этом соединении, например `migrate(drizzle(connection), …)`. */\n migrate(connection: Connection): Promise<void>;\n /** Паузы перед повторами, если всё же столкнулись с соседом. */\n retryDelaysMs?: readonly number[];\n}\n\n/** Схема всех пакетов SDK. */\nexport const AGENT_CHAT_SCHEMA = \"agent_chat\";\n\n/** Общая блокировка на создание схемы: одна на все пакеты, держится только на `create schema`. */\nexport const AGENT_CHAT_SCHEMA_LOCK_KEY = 7_311_900_441;\n\nconst DEFAULT_RETRY_DELAYS_MS = [25, 50, 100, 200];\n\n/** Коды PostgreSQL «такое уже есть»: сосед создал то же самое одновременно с нами. */\nconst DUPLICATE_CODES = new Set([\"23505\", \"42P06\", \"42P07\", \"42710\"]);\n\n/**\n * Создаёт схему `agent_chat` и применяет миграции пакета так, чтобы пакеты могли стартовать одновременно на пустой базе.\n * Схема создаётся под общей блокировкой, затем она отпускается и берётся своя блокировка пакета: блокировки не\n * вложены, поэтому взаимной блокировки нет, а разные пакеты ждут друг друга только на создании схемы.\n * Столкнулись всё же («уже есть») — повтор с начала на новом соединении.\n */\nexport async function migratePostgresSchema<Connection extends PostgresMigrationConnection>(options: PostgresMigrationOptions<Connection>): Promise<void> {\n const delays = options.retryDelaysMs ?? DEFAULT_RETRY_DELAYS_MS;\n for (let attempt = 0; ; attempt++) {\n try {\n return await migrateOnce(options);\n } catch (error) {\n if (attempt >= delays.length || !isDuplicateError(error)) throw error;\n await new Promise((resolve) => setTimeout(resolve, delays[attempt]));\n }\n }\n}\n\nasync function migrateOnce<Connection extends PostgresMigrationConnection>(options: PostgresMigrationOptions<Connection>): Promise<void> {\n const connection = await options.connect();\n try {\n if (!(await schemaExists(connection))) {\n await underLock(connection, AGENT_CHAT_SCHEMA_LOCK_KEY, () => connection.query(`create schema if not exists \"${AGENT_CHAT_SCHEMA}\"`));\n }\n await underLock(connection, options.lockKey, () => options.migrate(connection));\n } finally {\n connection.release();\n }\n}\n\nasync function schemaExists(connection: PostgresMigrationConnection): Promise<boolean> {\n const result = await connection.query(\"select 1 from pg_namespace where nspname = $1\", [AGENT_CHAT_SCHEMA]);\n return result.rows.length > 0;\n}\n\nasync function underLock(connection: PostgresMigrationConnection, key: number, work: () => Promise<unknown>): Promise<void> {\n await connection.query(\"select pg_advisory_lock($1)\", [key]);\n try {\n await work();\n } finally {\n await connection.query(\"select pg_advisory_unlock($1)\", [key]);\n }\n}\n\n/** Ошибка «уже есть» — у самой ошибки или в цепочке причин (drizzle заворачивает ошибку `pg` в свою). */\nfunction isDuplicateError(error: unknown): boolean {\n for (let current = error, depth = 0; current && depth < 5; current = (current as { cause?: unknown }).cause, depth++) {\n const code = (current as { code?: unknown }).code;\n if (typeof code === \"string\" && DUPLICATE_CODES.has(code)) return true;\n }\n return false;\n}\n","/**\n * Защита от запросов с чужих страниц. Браузер сам прикладывает к запросу куки человека, даже если запрос\n * отправила чужая страница (обычная HTML-форма, WebSocket). Отличить свою страницу от чужой помогают заголовки,\n * которые ставит только браузер: `Sec-Fetch-Site` и `Origin`. Порядок проверки как в Go 1.25 (CrossOriginProtection).\n */\n\nexport interface RequestOriginCheckOptions {\n /**\n * Чужие страницы, которым можно слать запросы с куками человека, например `[\"https://school.getcourse.ru\"]`\n * (схема, хост, порт). Шаблон `https://*.example.com` пускает все поддомены `example.com`, но не сам домен\n * и не `evil-example.com`. Свой сайт в список вносить не нужно.\n */\n allowedOrigins?: readonly string[] | undefined;\n /** Отказ: вызывается один раз на каждый новый адрес страницы, чтобы журнал не забивался. */\n onRefused?: (refusal: OriginRefusal) => void;\n}\n\n/**\n * Почему запрос отклонён. `cross_site` — браузер сам сказал, что страница чужая. `host_mismatch` — браузер старый\n * или адрес не https, и Origin не совпал ни с одним именем сервера (`hosts`): так бывает за прокси, который подменил Host.\n */\nexport type OriginRefusal = { origin: string; reason: \"cross_site\" } | { origin: string; reason: \"host_mismatch\"; hosts: string[] };\n\n/** Подсказка для журнала: что поправить, если отклонена своя страница. */\nexport function originRefusalHint(refusal: OriginRefusal): string {\n if (refusal.reason === \"host_mismatch\") {\n return (\n `отклонён запрос со страницы ${refusal.origin}: Origin не совпал с Host (${refusal.hosts.join(\", \")}). ` +\n \"Если сервер за прокси, за прокси передайте Host/X-Forwarded-Host (nginx: proxy_set_header Host $host; \" +\n \"proxy_set_header X-Forwarded-Host $host;). Если это чужая для сервера, но ваша страница, добавьте её адрес в allowedOrigins.\"\n );\n }\n return `отклонён запрос с чужой страницы ${refusal.origin}. Если это ваша страница, добавьте её адрес в allowedOrigins.`;\n}\n\n/** `true` — запрос со своей страницы, со страницы из списка или не из браузера. Метод запроса не смотрит. */\nexport type RequestOriginCheck = (request: Request) => boolean;\n\n/** Чтения (GET, HEAD, OPTIONS) не меняют данных: их ответ браузер чужой странице всё равно не покажет. */\nexport function isSafeMethod(method: string): boolean {\n const upper = method.toUpperCase();\n return upper === \"GET\" || upper === \"HEAD\" || upper === \"OPTIONS\";\n}\n\n/** Сколько разных отклонённых адресов запоминается для журнала. */\nconst MAX_REPORTED_ORIGINS = 100;\nconst MAX_REPORTED_LENGTH = 200;\n\nexport function createRequestOriginCheck(options: RequestOriginCheckOptions = {}): RequestOriginCheck {\n const isAllowed = allowedOriginsMatcher(options.allowedOrigins ?? []);\n const reported = new Set<string>();\n const refuse = (refusal: OriginRefusal): false => {\n const shown = refusal.origin.slice(0, MAX_REPORTED_LENGTH);\n if (options.onRefused && !reported.has(shown) && reported.size < MAX_REPORTED_ORIGINS) {\n reported.add(shown);\n options.onRefused({ ...refusal, origin: shown });\n }\n return false;\n };\n return (request) => {\n const origin = request.headers.get(\"origin\");\n if (origin !== null && isAllowed(origin)) return true;\n const site = request.headers.get(\"sec-fetch-site\");\n if (site !== null) {\n if (site === \"same-origin\" || site === \"none\") return true;\n return refuse({ origin: origin ?? `(без Origin, Sec-Fetch-Site: ${site})`, reason: \"cross_site\" });\n }\n // Нет ни того ни другого — не браузер (сервер, curl): куки человека чужая страница так не приложит.\n if (origin === null) return true;\n // Старый браузер без Sec-Fetch-Site: Origin должен совпасть с адресом самого сервера.\n const host = originHost(origin);\n const hosts = hostsOf(request);\n if (host !== null && hosts.has(host)) return true;\n return refuse({ origin, reason: \"host_mismatch\", hosts: [...hosts] });\n };\n}\n\n/** Метка вместо `*`: адрес с ней разбирает URL, а в настоящем имени хоста она не встречается. */\nconst WILDCARD_LABEL = \"wildcard-label-0\";\n\n/** Точные адреса и шаблоны поддоменов из `allowedOrigins`; неверная запись — ошибка сразу. */\nfunction allowedOriginsMatcher(entries: readonly string[]): (origin: string) => boolean {\n const exact = new Set<string>();\n const subdomainsOf: Array<{ protocol: string; port: string; suffix: string }> = [];\n for (const entry of entries) {\n const wildcard = /^(https?:\\/\\/)\\*\\.([^*]+)$/.exec(entry);\n const url = parsePageAddress(wildcard ? `${wildcard[1]}${WILDCARD_LABEL}.${wildcard[2]}` : entry);\n if (url === null || (!wildcard && entry.includes(\"*\"))) throw wrongEntry(entry);\n if (!wildcard) {\n exact.add(url.origin);\n continue;\n }\n const suffix = url.hostname.slice(WILDCARD_LABEL.length);\n // `*.com` пустил бы весь интернет: после звёздочки нужен домен хотя бы из двух частей (или localhost).\n // Точка в конце (`*.com.`) — тот же `*.com`. Общие суффиксы вроде `*.co.uk` так не отличить: вписывают свой домен.\n const tooBroad = suffix.endsWith(\".\") || (!suffix.slice(1).includes(\".\") && suffix !== \".localhost\");\n if (!url.hostname.startsWith(`${WILDCARD_LABEL}.`) || tooBroad) throw wrongEntry(entry);\n subdomainsOf.push({ protocol: url.protocol, port: url.port, suffix });\n }\n return (origin) => {\n const url = parseOrigin(origin);\n if (url === null) return false;\n if (exact.has(url.origin)) return true;\n return subdomainsOf.some(\n (pattern) =>\n url.protocol === pattern.protocol && url.port === pattern.port && url.hostname.endsWith(pattern.suffix) && url.hostname.length > pattern.suffix.length,\n );\n };\n}\n\n/** Адрес страницы: http(s), без пути, запроса, якоря и имени пользователя. Иначе `null`. */\nfunction parsePageAddress(entry: string): URL | null {\n try {\n const url = new URL(entry);\n const bare = url.pathname === \"/\" && !url.search && !url.hash && !url.username;\n return (url.protocol === \"http:\" || url.protocol === \"https:\") && bare ? url : null;\n } catch {\n return null;\n }\n}\n\nfunction wrongEntry(entry: string): Error {\n return new Error(\n `allowedOrigins: «${entry}» не адрес страницы; нужен вид https://app.example, http://localhost:5173 или https://*.example.com ` +\n \"(в шаблоне — свой домен: общие суффиксы вроде *.co.uk проверка не распознаёт)\",\n );\n}\n\nfunction parseOrigin(origin: string): URL | null {\n try {\n return new URL(origin);\n } catch {\n return null;\n }\n}\n\n/** Хост с портом из Origin; `null` и не адрес — `null`. */\nfunction originHost(origin: string): string | null {\n try {\n const url = new URL(origin);\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? url.host : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Под какими именами сервер знает себя: `Host` и `X-Forwarded-Host` от прокси. Адрес запроса — только без `Host`:\n * при подъёме WebSocket в Node (`@hono/node-ws`) он собран как `http://localhost/…` и порта в нём нет.\n */\nfunction hostsOf(request: Request): Set<string> {\n const host = request.headers.get(\"host\");\n const hosts = new Set<string>([host ? host.trim().toLowerCase() : new URL(request.url).host]);\n for (const forwarded of request.headers.get(\"x-forwarded-host\")?.split(\",\") ?? []) {\n if (forwarded.trim()) hosts.add(forwarded.trim().toLowerCase());\n }\n return hosts;\n}\n","import type { ModelChoice } from \"../models/model-choice\";\nimport type { MemoryNote, MemoryNoteInput } from \"../memory/long-term-memory\";\nimport { isOptimizationBlocked, revertRefusal } from \"../memory/memory-optimization\";\nimport type { MemoryMergeGroup, MemoryOptimizationRun, MergedMemoryNote } from \"../memory/memory-optimization\";\nimport type { Chat, StoredBranchSelection, StoredCompaction, StoredMessage, UsageRecord, UsageSum } from \"../conversation/types\";\nimport {\n aliveAtByLocalClock,\n assertKnownFormatVersion,\n ChatAlreadyExistsError,\n DuplicateChatSubjectError,\n DuplicateMessageError,\n MessageAlreadyFinishedError,\n MessageNotAwaitingError,\n sameMessageParts,\n silenceBoundaryByLocalClock,\n} from \"./chat-storage\";\nimport { assertChatDataFilter, chatDataFields, chatDataMatches, chatDataOfInput, mergeChatData } from \"./chat-data\";\nimport type { ChatListOptions, ChatStorage, ChatSubjectFilter, SpendingLimitStateRecord, StoredPendingBurst } from \"./chat-storage\";\n\n/** Хранилище в памяти процесса — для тестов. Копирует данные на входе и выходе, как база. */\ntype StoredNote = MemoryNote & { mergedInto?: string };\n\n/** Сумма записей расхода по ключу (`group` — поля ключа: пара «модель × провайдер», у суммы по чатам — ещё чат). */\nfunction sumRecords<K extends Pick<UsageSum, \"modelId\" | \"providerId\">>(records: readonly UsageRecord[], group: (record: UsageRecord) => K): Array<K & UsageSum> {\n const sums = new Map<string, K & UsageSum>();\n for (const record of records) {\n const fields = group(record);\n const key = Object.values(fields).join(\"\\u0000\");\n const sum = sums.get(key) ?? { ...fields, inputTokens: 0, cachedInputTokens: 0, cacheWriteTokens: 0, outputTokens: 0, reasoningTokens: 0, costUsd: 0 };\n sum.inputTokens += record.inputTokens;\n sum.cachedInputTokens += record.cachedInputTokens;\n sum.cacheWriteTokens += record.cacheWriteTokens;\n sum.outputTokens += record.outputTokens;\n sum.reasoningTokens += record.reasoningTokens;\n sum.costUsd += record.costUsd;\n sums.set(key, sum);\n }\n return [...sums.values()];\n}\n\nexport function createInMemoryChatStorage(): ChatStorage {\n const chats = new Map<string, Chat>();\n const messages = new Map<string, StoredMessage>();\n /** Время последнего знака жизни идущих ответов по id: при чтении не отдаётся. */\n const aliveAt = new Map<string, string>();\n /** Аренды хода по чатам: кто держит и до какого времени (мс по часам этой машины). */\n const turnLeases = new Map<string, { holder: string; until: number }>();\n /** Ждущие пачки по чатам. */\n const pendingBursts = new Map<string, StoredPendingBurst>();\n const compactions: StoredCompaction[] = [];\n const branchSelections: StoredBranchSelection[] = [];\n const usageRecords: UsageRecord[] = [];\n const userModelChoices = new Map<string, ModelChoice>();\n const voiceChoices = new Map<string, string>();\n const spendingLimitStates = new Map<string, SpendingLimitStateRecord>();\n /** Долгая память: у каждого человека свои записи; у скрытых оригиналов слияния есть `mergedInto`. */\n const memoryNotes = new Map<string, Map<string, StoredNote>>();\n const optimizationRuns = new Map<string, MemoryOptimizationRun[]>();\n\n function streamingMessage(messageId: string): StoredMessage {\n const message = messages.get(messageId);\n if (!message) throw new Error(`Сообщения ${messageId} нет`);\n if (message.status !== \"streaming\") throw new MessageAlreadyFinishedError(messageId);\n return message;\n }\n\n /** Сообщение для чтения: копия, `voiceCut` — `null`, если не задан. */\n function readable(message: StoredMessage): StoredMessage {\n const copy = structuredClone(message);\n return { ...copy, voiceCut: copy.voiceCut ?? null };\n }\n\n function ownerNotes(ownerId: string): Map<string, StoredNote> {\n let notes = memoryNotes.get(ownerId);\n if (!notes) {\n notes = new Map();\n memoryNotes.set(ownerId, notes);\n }\n return notes;\n }\n\n function ownerRuns(ownerId: string): MemoryOptimizationRun[] {\n let runs = optimizationRuns.get(ownerId);\n if (!runs) {\n runs = [];\n optimizationRuns.set(ownerId, runs);\n }\n return runs;\n }\n\n /** Порядок списка по договору: `updatedAt`, затем `createdAt`, затем `id` — новее и больше первыми. */\n function newestFirst<T extends MemoryNote>(notes: T[]): T[] {\n return notes.sort(\n (left, right) =>\n right.updatedAt.localeCompare(left.updatedAt) ||\n right.createdAt.localeCompare(left.createdAt) ||\n (right.id > left.id ? 1 : right.id < left.id ? -1 : 0),\n );\n }\n\n /** Обычная запись без служебного признака скрытия. */\n function publicNote({ mergedInto: _hidden, ...note }: StoredNote): MemoryNote {\n return note;\n }\n\n /** Как база с NOT NULL: разделы — список строк, текст — строка. */\n function assertWellFormedNote(note: MemoryNoteInput): void {\n if (!Array.isArray(note?.categories) || !note.categories.every((category) => typeof category === \"string\")) {\n throw new TypeError(\"Разделы записи памяти — список строк\");\n }\n if (typeof note.content !== \"string\") throw new TypeError(\"Текст записи памяти — строка\");\n }\n\n /** Записи, которые видит человек: без скрытых оригиналов, копиями. */\n function visibleNotes(ownerId: string): MemoryNote[] {\n const visible = [...ownerNotes(ownerId).values()].filter((note) => !note.mergedInto);\n return structuredClone(newestFirst(visible).map(publicNote));\n }\n\n function insertNotes(ownerId: string, inputs: MemoryNoteInput[]): MemoryNote[] {\n // Сначала проверяем весь пакет: негодная запись не оставляет в памяти и остальных.\n inputs.forEach(assertWellFormedNote);\n const now = new Date().toISOString();\n const created = inputs.map(\n ({ categories, content }): MemoryNote => ({\n id: crypto.randomUUID(),\n categories: [...categories],\n content,\n version: 1,\n createdAt: now,\n updatedAt: now,\n }),\n );\n const notes = ownerNotes(ownerId);\n for (const note of created) notes.set(note.id, note);\n return structuredClone(created);\n }\n\n function updateNote(ownerId: string, id: string, expectedVersion: number, input: MemoryNoteInput): MemoryNote | \"not_found\" | \"stale_version\" {\n assertWellFormedNote(input);\n const notes = ownerNotes(ownerId);\n const note = notes.get(id);\n if (!note || note.mergedInto) return \"not_found\";\n if (note.version !== expectedVersion) return \"stale_version\";\n const updated: MemoryNote = {\n ...note,\n categories: [...input.categories],\n content: input.content,\n version: note.version + 1,\n updatedAt: new Date().toISOString(),\n };\n notes.set(id, updated);\n return structuredClone(updated);\n }\n\n /** Как уникальный индекс (владелец, ключ): чат без ключа не мешает никому. */\n function assertSubjectFree(chat: Pick<Chat, \"ownerId\" | \"subjectKey\">): void {\n if (chat.subjectKey === undefined) return;\n for (const existing of chats.values()) {\n if (existing.ownerId === chat.ownerId && existing.subjectKey === chat.subjectKey) {\n throw new DuplicateChatSubjectError(chat.ownerId, chat.subjectKey);\n }\n }\n }\n\n function sortedMessages(chatId: string): StoredMessage[] {\n return [...messages.values()]\n .filter((message) => message.chatId === chatId)\n .sort((left, right) => left.createdAt.localeCompare(right.createdAt));\n }\n\n return {\n async createChat({ createdAt, ...input }) {\n const at = createdAt ?? new Date().toISOString();\n const chat: Chat = withChatData(withoutUndefined({ ...structuredClone(input), createdAt: at, updatedAt: at }));\n assertSubjectFree(chat);\n chats.set(chat.id, chat);\n return structuredClone(chat);\n },\n\n async findChatBySubject(ownerId, subjectKey) {\n const chat = [...chats.values()].find((existing) => existing.ownerId === ownerId && existing.subjectKey === subjectKey);\n return chat ? structuredClone(chat) : null;\n },\n\n async importChat(input, imported) {\n // Сначала все проверки: при ошибке ничего не записано.\n if (chats.has(input.id)) throw new ChatAlreadyExistsError(input.id);\n const chat: Chat = withChatData(withoutUndefined(structuredClone(input)));\n assertSubjectFree(chat);\n const ids = new Set<string>();\n for (const message of imported) {\n if (messages.has(message.id) || ids.has(message.id)) throw new DuplicateMessageError(message.id);\n ids.add(message.id);\n }\n chats.set(chat.id, chat);\n for (const message of imported) {\n const { alive: _alive, ...stored } = message;\n messages.set(message.id, structuredClone(stored));\n }\n },\n\n async setAfterTurnState(messageId, state, options) {\n const message = messages.get(messageId);\n if (!message || (state !== \"pending\" && message.afterTurn !== \"pending\")) return false;\n const parts = options?.appendParts ? [...message.parts, ...structuredClone(options.appendParts)] : message.parts;\n messages.set(messageId, { ...message, parts, afterTurn: state });\n // Отметка ожидания хука — в той же колонке, что знак жизни: у законченного ответа она больше ни для чего.\n if (state === \"pending\") aliveAt.set(messageId, aliveAtByLocalClock(\"now\"));\n return true;\n },\n\n async listPendingAfterTurn(filter) {\n const boundary = filter?.pendingForMs === undefined ? null : silenceBoundaryByLocalClock(filter.pendingForMs);\n return [...messages.values()]\n .filter((message) => message.afterTurn === \"pending\")\n .filter((message) => boundary === null || (aliveAt.get(message.id) ?? message.createdAt) < boundary)\n .map((message) => message.id);\n },\n\n async getChat(chatId) {\n const chat = chats.get(chatId);\n return chat ? structuredClone(chat) : null;\n },\n\n async updateChatModel(chatId, model) {\n const chat = chats.get(chatId);\n if (!chat) return;\n const { reasoning: _dropped, ...rest } = chat;\n chats.set(chatId, { ...rest, ...model });\n },\n\n async listChats(ownerId, options?: ChatListOptions) {\n if (options?.data) assertChatDataFilter(options.data);\n const owned = [...chats.values()].filter(\n (chat) => chat.ownerId === ownerId && matchesSubjects(chat, options?.subjects) && (!options?.data || chatDataMatches(chat.data, options.data)),\n );\n owned.sort(\n (left, right) =>\n right.updatedAt.localeCompare(left.updatedAt) ||\n right.createdAt.localeCompare(left.createdAt) ||\n (right.id > left.id ? 1 : right.id < left.id ? -1 : 0),\n );\n return structuredClone(owned);\n },\n\n async updateChatData(chatId, patch, { maxBytes }) {\n const chat = chats.get(chatId);\n if (!chat) return null;\n // Слияние и проверка размера до записи: при ошибке данные прежние.\n const merged = mergeChatData(chat.data, patch, maxBytes);\n const { data: _data, subjectData: _subjectData, ...rest } = chat;\n const updated: Chat = { ...rest, ...chatDataFields(merged) };\n chats.set(chatId, updated);\n return structuredClone(updated);\n },\n\n async renameChat(chatId, title) {\n const chat = chats.get(chatId);\n if (!chat) return;\n const { title: _dropped, ...rest } = chat;\n chats.set(chatId, title === null ? rest : { ...rest, title });\n },\n\n async setChatClosed(chatId, closed) {\n const chat = chats.get(chatId);\n if (!chat) return;\n const { closed: _dropped, ...rest } = chat;\n chats.set(chatId, closed === null ? rest : { ...rest, closed: { code: closed.code, message: closed.message } });\n },\n\n async deleteChat(chatId) {\n if (!chats.delete(chatId)) return;\n turnLeases.delete(chatId);\n pendingBursts.delete(chatId);\n for (const message of [...messages.values()]) {\n if (message.chatId !== chatId) continue;\n messages.delete(message.id);\n aliveAt.delete(message.id);\n }\n // Записи сжатия — на месте, с обратным ходом, чтобы индексы не сдвигались.\n for (let index = compactions.length - 1; index >= 0; index -= 1) {\n if (compactions[index]!.chatId === chatId) compactions.splice(index, 1);\n }\n for (let index = branchSelections.length - 1; index >= 0; index -= 1) {\n if (branchSelections[index]!.chatId === chatId) branchSelections.splice(index, 1);\n }\n },\n\n async getMessage(messageId) {\n const message = messages.get(messageId);\n if (!message) return null;\n assertKnownFormatVersion(message);\n return readable(message);\n },\n\n async listMessages(chatId) {\n const found = sortedMessages(chatId);\n found.forEach(assertKnownFormatVersion);\n return found.map(readable);\n },\n\n async listMessageLinks(chatId) {\n return sortedMessages(chatId).map(({ id, parentId, createdAt }) => ({ id, parentId, createdAt }));\n },\n\n async listMessagePartsOfTypes(chatId, types) {\n return sortedMessages(chatId).map(({ id, parentId, createdAt, parts }) => ({\n id,\n parentId,\n createdAt,\n parts: structuredClone(parts.filter((part) => types.includes(part.type))),\n }));\n },\n\n async insertMessage(message) {\n if (messages.has(message.id)) throw new DuplicateMessageError(message.id);\n // Как внешний ключ в базе: сообщение без чата не пишется.\n const chat = chats.get(message.chatId);\n if (!chat) throw new Error(`Чата ${message.chatId} нет`);\n const { alive, ...stored } = message;\n messages.set(message.id, structuredClone(stored));\n if (alive) aliveAt.set(message.id, aliveAtByLocalClock(alive));\n chats.set(chat.id, { ...chat, updatedAt: message.createdAt });\n },\n\n async updateStreamingMessage(messageId, patch) {\n const message = streamingMessage(messageId);\n const { alive, ...rest } = structuredClone(patch);\n messages.set(messageId, { ...message, ...rest });\n if (alive) aliveAt.set(messageId, aliveAtByLocalClock(alive));\n },\n\n async markAnswerAlive(messageId) {\n streamingMessage(messageId);\n aliveAt.set(messageId, aliveAtByLocalClock(\"now\"));\n },\n\n async finishMessage(messageId, final) {\n const message = streamingMessage(messageId);\n messages.set(messageId, { ...message, ...structuredClone(final) });\n if (final.afterTurn === \"pending\") aliveAt.set(messageId, aliveAtByLocalClock(\"now\"));\n },\n\n async updateAwaitingMessage(messageId, patch) {\n const message = messages.get(messageId);\n if (!message) throw new Error(`Сообщения ${messageId} нет`);\n if (message.status !== \"awaiting_approval\") throw new MessageNotAwaitingError(messageId);\n if (patch.expectedParts && !sameMessageParts(message.parts, patch.expectedParts)) throw new MessageNotAwaitingError(messageId);\n messages.set(messageId, {\n ...message,\n parts: structuredClone(patch.parts),\n ...(patch.status && { status: patch.status }),\n });\n if (patch.alive) aliveAt.set(messageId, aliveAtByLocalClock(patch.alive));\n },\n\n async listStreamingMessageIds() {\n return [...messages.values()].filter((message) => message.status === \"streaming\").map((message) => message.id);\n },\n\n async listSilentAnswerIds({ silentForMs, chatId }) {\n const boundary = silenceBoundaryByLocalClock(silentForMs);\n return [...messages.values()]\n .filter((message) => message.status === \"streaming\" && (chatId === undefined || message.chatId === chatId))\n .filter((message) => (aliveAt.get(message.id) ?? message.createdAt) < boundary)\n .map((message) => message.id);\n },\n\n async claimChatTurn(chatId, holder, leaseMs) {\n const now = Date.now();\n const lease = turnLeases.get(chatId);\n if (lease && lease.holder !== holder && lease.until > now) return false;\n turnLeases.set(chatId, { holder, until: now + leaseMs });\n return true;\n },\n\n async releaseChatTurn(chatId, holder) {\n if (turnLeases.get(chatId)?.holder === holder) turnLeases.delete(chatId);\n },\n\n async clearChatTurnLeases() {\n turnLeases.clear();\n },\n\n async savePendingBurst(burst) {\n pendingBursts.set(burst.chatId, structuredClone(burst));\n },\n\n async takeAbandonedBursts({ abandonedBefore, holder, heldUntil, limit, exceptHolder }) {\n const abandoned = [...pendingBursts.values()]\n .filter((burst) => Date.parse(burst.heldUntil) <= Date.parse(abandonedBefore) && burst.holder !== exceptHolder)\n .sort((first, second) => Date.parse(first.dueAt) - Date.parse(second.dueAt))\n .slice(0, limit);\n return abandoned.map((burst) => {\n const taken = { ...burst, holder, heldUntil };\n pendingBursts.set(burst.chatId, taken);\n return structuredClone(taken);\n });\n },\n\n async renewPendingBurst({ chatId, messageId, holder, heldUntil }) {\n const burst = pendingBursts.get(chatId);\n if (burst?.messageId !== messageId || burst.holder !== holder) return false;\n pendingBursts.set(chatId, { ...burst, heldUntil });\n return true;\n },\n\n async removePendingBurst(chatId, messageId) {\n if (pendingBursts.get(chatId)?.messageId === messageId) pendingBursts.delete(chatId);\n },\n\n async insertCompaction(record) {\n if (compactions.some((existing) => existing.id === record.id)) throw new Error(`Запись сжатия ${record.id} уже есть`);\n compactions.push(structuredClone(record));\n },\n\n async listCompactions(chatId) {\n return structuredClone(compactions.filter((record) => record.chatId === chatId));\n },\n\n async insertBranchSelection(record) {\n if (branchSelections.some((existing) => existing.id === record.id)) throw new Error(`Выбор ветки ${record.id} уже есть`);\n branchSelections.push(structuredClone(record));\n },\n\n async listBranchSelections(chatId) {\n return structuredClone(branchSelections.filter((record) => record.chatId === chatId));\n },\n\n async insertUsageRecord(record) {\n if (usageRecords.some((existing) => existing.id === record.id)) throw new Error(`Запись расхода ${record.id} уже есть`);\n usageRecords.push(structuredClone(record));\n },\n\n async hasUsageRecord(id) {\n return usageRecords.some((record) => record.id === id);\n },\n\n async sumUsage({ ownerId, chatId, messageId, source, since }) {\n return sumRecords(\n usageRecords.filter(\n (record) =>\n (ownerId === undefined || record.ownerId === ownerId) &&\n (chatId === undefined || record.chatId === chatId) &&\n (messageId === undefined || record.messageId === messageId) &&\n (source === undefined || record.source === source) &&\n (since === undefined || record.createdAt >= since),\n ),\n (record) => ({ modelId: record.modelId, providerId: record.providerId }),\n );\n },\n\n async sumUsageByChat({ chatIds, ownerId, source, since }) {\n const wanted = new Set(chatIds);\n return sumRecords(\n usageRecords.filter(\n (record) =>\n wanted.has(record.chatId) &&\n (ownerId === undefined || record.ownerId === ownerId) &&\n (source === undefined || record.source === source) &&\n (since === undefined || record.createdAt >= since),\n ),\n (record) => ({ chatId: record.chatId, modelId: record.modelId, providerId: record.providerId }),\n );\n },\n\n async setVoiceCut({ ownerId, chatId, messageId, voiceCut }) {\n const message = messages.get(messageId);\n if (!message || message.chatId !== chatId || chats.get(chatId)?.ownerId !== ownerId) return;\n messages.set(messageId, { ...message, voiceCut: structuredClone(voiceCut) });\n },\n\n async getSpendingLimitState(key) {\n const record = spendingLimitStates.get(key);\n return record ? { ...record } : null;\n },\n\n async saveSpendingLimitState(key, value, expectedVersion) {\n const current = spendingLimitStates.get(key);\n if ((current?.version ?? null) !== expectedVersion) return false;\n spendingLimitStates.set(key, { value, version: (current?.version ?? 0) + 1 });\n return true;\n },\n\n async deleteSpendingLimitStates(keys) {\n for (const key of keys) spendingLimitStates.delete(key);\n },\n\n async getVoiceChoice(ownerId) {\n return voiceChoices.get(ownerId) ?? null;\n },\n\n async setVoiceChoice(ownerId, voiceId) {\n voiceChoices.set(ownerId, voiceId);\n },\n\n async getUserModelChoice(ownerId) {\n const choice = userModelChoices.get(ownerId);\n return choice ? structuredClone(choice) : null;\n },\n\n async saveUserModelChoice(ownerId, choice) {\n userModelChoices.set(ownerId, structuredClone(choice));\n },\n\n async listMemoryNotes(ownerId) {\n return visibleNotes(ownerId);\n },\n\n async insertMemoryNotes(ownerId, inputs) {\n return insertNotes(ownerId, inputs);\n },\n\n // Всё выполняется без ожидания, поэтому между проверкой и вставкой никто не успеет вклиниться.\n async insertMemoryNotesChecked(ownerId, inputs, guard) {\n const error = guard(visibleNotes(ownerId));\n return error ?? insertNotes(ownerId, inputs);\n },\n\n async updateMemoryNote(ownerId, id, expectedVersion, input) {\n return updateNote(ownerId, id, expectedVersion, input);\n },\n\n async updateMemoryNoteChecked(ownerId, id, expectedVersion, input, guard) {\n return guard(visibleNotes(ownerId)) ?? updateNote(ownerId, id, expectedVersion, input);\n },\n\n async deleteMemoryNote(ownerId, id, expectedVersion) {\n const notes = ownerNotes(ownerId);\n const note = notes.get(id);\n if (!note || note.mergedInto) return \"not_found\";\n if (note.version !== expectedVersion) return \"stale_version\";\n notes.delete(id);\n for (const [hiddenId, hidden] of notes) if (hidden.mergedInto === id) notes.delete(hiddenId);\n return \"ok\";\n },\n\n async clearMemoryNotes(ownerId) {\n const count = [...ownerNotes(ownerId).values()].filter((note) => !note.mergedInto).length;\n memoryNotes.delete(ownerId);\n optimizationRuns.delete(ownerId);\n return count;\n },\n\n async claimMemoryOptimizationRun(ownerId, rules, now) {\n const runs = ownerRuns(ownerId);\n const nowMs = Date.parse(now);\n for (const run of runs) {\n const leaseEnd = Date.parse(run.startedAt) + rules.leaseMs;\n if (run.status === \"running\" && nowMs >= leaseEnd) Object.assign(run, { status: \"failed\", error: \"lease_expired\", finishedAt: new Date(leaseEnd).toISOString() });\n }\n const blocked = isOptimizationBlocked(\n runs.map((run) => ({ status: run.status, startedAtMs: Date.parse(run.startedAt), endedAtMs: run.finishedAt ? Date.parse(run.finishedAt) : null })),\n rules,\n nowMs,\n );\n if (blocked) return null;\n const run: MemoryOptimizationRun = { id: crypto.randomUUID(), startedAt: now, finishedAt: null, status: \"running\", groups: [], error: null, usageRecordId: null };\n runs.push(run);\n return structuredClone(run);\n },\n\n async finishMemoryOptimizationRun(ownerId, runId, outcome) {\n const run = ownerRuns(ownerId).find((candidate) => candidate.id === runId);\n if (run?.status !== \"running\") return \"not_found\";\n Object.assign(run, { status: outcome.status, finishedAt: outcome.finishedAt, error: outcome.error ?? null, usageRecordId: outcome.usageRecordId ?? null });\n return \"ok\";\n },\n\n async applyMemoryMerge(ownerId, merge) {\n const { merges } = merge;\n if (merges.length === 0) throw new TypeError(\"Слияние без склеек\");\n const seen = new Set<string>();\n for (const { result, originals } of merges) {\n assertWellFormedNote(result);\n if (originals.length === 0) throw new TypeError(\"Склейка без оригиналов\");\n for (const { id } of originals) {\n if (seen.has(id)) throw new TypeError(`Запись ${id} названа в двух склейках`);\n seen.add(id);\n }\n }\n const run = ownerRuns(ownerId).find((candidate) => candidate.id === merge.runId);\n if (run?.status !== \"running\") return \"run_not_active\";\n const notes = ownerNotes(ownerId);\n for (const { originals } of merges) {\n for (const { id, version } of originals) {\n const original = notes.get(id);\n if (!original || original.mergedInto || original.version !== version) return \"stale_version\";\n }\n }\n const now = new Date().toISOString();\n const groups: MemoryMergeGroup[] = merges.map(({ result, originals }) => {\n const created: StoredNote = { id: crypto.randomUUID(), categories: [...result.categories], content: result.content, version: 1, createdAt: now, updatedAt: now };\n notes.set(created.id, created);\n for (const { id } of originals) notes.get(id)!.mergedInto = created.id;\n return { resultId: created.id, originalIds: originals.map(({ id }) => id), resultVersion: 1 };\n });\n Object.assign(run, { status: \"applied\", finishedAt: merge.finishedAt, groups, usageRecordId: merge.usageRecordId ?? null });\n return structuredClone(run);\n },\n\n async revertMemoryMerge(ownerId, runId, guard) {\n const run = ownerRuns(ownerId).find((candidate) => candidate.id === runId);\n if (run?.status !== \"applied\") return \"not_found\";\n const notes = ownerNotes(ownerId);\n const intact = run.groups.every(\n ({ resultId, originalIds, resultVersion }) =>\n notes.get(resultId)?.version === resultVersion && !notes.get(resultId)?.mergedInto && originalIds.every((id) => notes.get(id)?.mergedInto === resultId),\n );\n if (!intact) return \"conflict\";\n if (guard) {\n const resultIds = new Set(run.groups.map(({ resultId }) => resultId));\n const restoredIds = new Set(run.groups.flatMap(({ originalIds }) => originalIds));\n const inPlay = [...notes.values()];\n const remaining = newestFirst(inPlay.filter((note) => !note.mergedInto && !resultIds.has(note.id))).map(publicNote);\n const restored = newestFirst(inPlay.filter((note) => restoredIds.has(note.id))).map(publicNote);\n const error = guard(structuredClone(remaining), structuredClone(restored));\n if (error) return revertRefusal(error);\n }\n for (const { resultId, originalIds } of run.groups) {\n notes.delete(resultId);\n for (const id of originalIds) delete notes.get(id)!.mergedInto;\n }\n run.status = \"reverted\";\n return \"ok\";\n },\n\n async listMemoryOptimizationRuns(ownerId, filter) {\n const runs = ownerRuns(ownerId)\n .map((run, index) => ({ run, index }))\n .filter(({ run }) => !filter?.status || filter.status.includes(run.status))\n .sort((left, right) => right.run.startedAt.localeCompare(left.run.startedAt) || right.index - left.index)\n .map(({ run }) => run);\n return structuredClone(filter?.limit === undefined ? runs : runs.slice(0, filter.limit));\n },\n\n async listMergedMemoryNotes(ownerId) {\n const hidden = [...ownerNotes(ownerId).values()].filter((note): note is StoredNote & { mergedInto: string } => Boolean(note.mergedInto));\n return structuredClone(newestFirst(hidden) as MergedMemoryNote[]);\n },\n };\n}\n\n/** Подходит ли чат под фильтр `listChats` по ключу объекта. */\nfunction matchesSubjects(chat: Chat, subjects: ChatSubjectFilter = \"exclude\"): boolean {\n if (subjects === \"include\") return true;\n return subjects === \"only\" ? chat.subjectKey !== undefined : chat.subjectKey === undefined;\n}\n\n/** `data` и `subjectData` — один объект; данных нет — полей нет. */\nfunction withChatData<T extends Pick<Chat, \"data\" | \"subjectData\">>(chat: T): T {\n const { data: _data, subjectData: _subjectData, ...rest } = chat;\n return { ...rest, ...chatDataFields(chatDataOfInput(chat)) } as T;\n}\n\n/** Как база: поле без значения не хранится. */\nfunction withoutUndefined<T extends object>(value: T): T {\n return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined)) as T;\n}\n"],"mappings":"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","names":["run"]}
@@ -13,7 +13,7 @@ import {
13
13
  extendCatalog,
14
14
  newStoredMessage,
15
15
  standardCatalog
16
- } from "./chunk-6KHSUGHE.js";
16
+ } from "./chunk-BQTUOC3T.js";
17
17
  import "./chunk-CVRDB3NM.js";
18
18
 
19
19
  // src/storage/storage-contract.ts
@@ -19,7 +19,7 @@ export interface AppStateRecord {
19
19
  set?: Record<string, unknown>;
20
20
  unset?: string[];
21
21
  }
22
- /** Состояние чата для инструмента (`model.state`): служебных ключей SDK не видит и не пишет. */
22
+ /** Состояние чата для инструмента (`model.state`): верхних ключей `$` не видит; результаты проверки flow читает, но не пишет. */
23
23
  export interface AppStateAccess {
24
24
  /** Значение по пути (копия); без пути — всё состояние. Нет значения — `undefined`. */
25
25
  get(path?: string): unknown;
@@ -68,4 +68,6 @@ export declare function createAppStateWriter(context: {
68
68
  logError: (message: string) => void;
69
69
  chatId: string;
70
70
  reserved?: boolean;
71
+ /** @internal Только доверенная серверная подготовка хода и flows compare-head commit. Не часть model.state. */
72
+ serverFlowChecks?: true;
71
73
  }): AppStateWriter;
@@ -0,0 +1,22 @@
1
+ /** @internal Серверный порт расширения flows. Не инструмент и не HTTP-метод. Пишет только ключ flow. */
2
+ export interface FlowStateCommitInput {
3
+ ownerId: string;
4
+ chatId: string;
5
+ flowId: string;
6
+ revision: string;
7
+ expectedAtMessageId: string | null;
8
+ /** Повтор операции на той же ветке ничего не дописывает. Другой payload с тем же ключом — ошибка. */
9
+ idempotencyKey: string;
10
+ flow: Record<string, unknown>;
11
+ }
12
+ export type FlowStateCommitResult = {
13
+ status: "applied" | "same";
14
+ atMessageId: string;
15
+ } | {
16
+ status: "stale" | "busy" | "not_found";
17
+ };
18
+ export type FlowStateCommit = (input: FlowStateCommitInput) => Promise<FlowStateCommitResult>;
19
+ /** @internal Регистрируется только ядром; данные из запроса человека сюда не маршрутизируются. */
20
+ export declare function registerFlowStateCommit(service: object, commit: FlowStateCommit): void;
21
+ /** @internal Порт живой службы для flows; у пользовательских адаптеров без capability — undefined. */
22
+ export declare function flowStateCommitter(service: object): FlowStateCommit | undefined;
@@ -4,7 +4,7 @@ import type { AppStateValues } from "./app-state.js";
4
4
  * Почему сменилось состояние: ход в конце ветки (`turn`), смена ветки или запись проверки ответа виджета
5
5
  * (`widgetAnswer`: `ctx.state` в `checkAnswer`, сразу после записи ответа, до продолжения хода).
6
6
  */
7
- export type StateChangeReason = "turn" | "editMessage" | "regenerate" | "retry" | "selectBranch" | "editAnswer" | "widgetAnswer";
7
+ export type StateChangeReason = "turn" | "editMessage" | "regenerate" | "retry" | "selectBranch" | "editAnswer" | "widgetAnswer" | "flowCommit";
8
8
  /** Что получает `onStateChanged`: состояние без служебных ключей SDK. */
9
9
  export interface StateChangedInput {
10
10
  ownerId: string;
@@ -53,6 +53,9 @@ export { CLOSED_STATE_KEY, hiddenStateKeyOf, isStateMessage, publicAppStateParts
53
53
  export type { HiddenStateKey } from "./app-state/state-messages.js";
54
54
  export { DEFAULT_STATE_CHANGED_ATTEMPTS, DEFAULT_STATE_CHANGED_TIMEOUT_MS } from "./app-state/state-changed.js";
55
55
  export type { StateChangedHandler, StateChangedInput, StateChangeReason } from "./app-state/state-changed.js";
56
+ /** @internal Серверная capability расширения flows; не часть ChatService/HTTP. */
57
+ export { flowStateCommitter } from "./app-state/flow-state-commit.js";
58
+ export type { FlowStateCommit, FlowStateCommitInput, FlowStateCommitResult } from "./app-state/flow-state-commit.js";
56
59
  export type { AbandonedAnswers, AfterTurnHandler, AppEvent, BeforeTurnDecision, BeforeTurnHandler, StaffActor, TurnAction, TurnNotStartedHandler, TurnNotStartedReason, TurnHold, TurnRefusal, TurnReply, TurnResult, TurnStopReason, } from "./turn-hooks/turn-hooks.js";
57
60
  export { DEFAULT_REPLY_CHECKS_WEB, resolveReplyChecks } from "./agent-run/reply-check-types.js";
58
61
  export type { KnownTexts, KnownTextsContext, ReplyCheck, ReplyCheckInput, ReplyCheckResult, ReplyChecksSetting, ReplyChecksWebMode } from "./agent-run/reply-check-types.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ykurilov/agent-chat-core",
3
- "version": "0.76.0",
3
+ "version": "0.77.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,