@ykurilov/agent-chat-flows 0.56.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/dist/index.js +55 -11
- package/dist/index.js.map +1 -1
- package/dist/postgres.js +33 -12
- package/dist/postgres.js.map +1 -1
- package/dist/react.js +9 -7
- package/dist/react.js.map +1 -1
- package/dist/sqlite.js +25 -1
- package/dist/sqlite.js.map +1 -1
- package/dist/types/labels/editor.d.ts +2 -0
- package/dist/types/react/editor/use-flow-editor.d.ts +1 -1
- package/dist/types/runtime/flow-types.d.ts +37 -5
- package/dist/types/runtime/migrate-chats.d.ts +5 -0
- package/dist/types/store/flow-store.d.ts +13 -0
- package/dist/types/store/postgres.d.ts +2 -2
- package/package.json +4 -4
package/dist/sqlite.js
CHANGED
|
@@ -230,9 +230,33 @@ function createSqliteFlowStore(filePath, migrationsFolder) {
|
|
|
230
230
|
const rows = db.select({ versionId: flowChats.versionId, stateId: flowChats.stateId, chats: sql`count(*)` }).from(flowChats).where(eq(flowChats.flowId, flowId)).groupBy(flowChats.versionId, flowChats.stateId).all();
|
|
231
231
|
return rows.map((row) => ({ ...row, chats: Number(row.chats) })).sort(byVersionAndState);
|
|
232
232
|
},
|
|
233
|
+
async listFlowChats({ flowId, exceptVersionId, chatIds }) {
|
|
234
|
+
if (chatIds?.length === 0) return [];
|
|
235
|
+
const rows = db.select({
|
|
236
|
+
chatId: flowChats.chatId,
|
|
237
|
+
ownerId: flowChats.ownerId,
|
|
238
|
+
flowId: flowChats.flowId,
|
|
239
|
+
versionId: flowChats.versionId,
|
|
240
|
+
stateId: flowChats.stateId,
|
|
241
|
+
endedAt: flowChats.endedAt,
|
|
242
|
+
endReason: flowChats.endReason
|
|
243
|
+
}).from(flowChats).where(
|
|
244
|
+
and(
|
|
245
|
+
eq(flowChats.flowId, flowId),
|
|
246
|
+
exceptVersionId === void 0 ? void 0 : ne(flowChats.versionId, exceptVersionId),
|
|
247
|
+
chatIds === void 0 ? void 0 : inArray(flowChats.chatId, chatIds)
|
|
248
|
+
)
|
|
249
|
+
).orderBy(asc(flowChats.chatId)).all();
|
|
250
|
+
return rows.map((row) => ({ ...row, endedAt: row.endedAt === null ? null : toIso(row.endedAt) }));
|
|
251
|
+
},
|
|
233
252
|
async migrateBatch(input) {
|
|
234
253
|
const change = migratePending(input);
|
|
235
|
-
|
|
254
|
+
if (input.chatIds?.length === 0) return { moved: [] };
|
|
255
|
+
const notYetMoved = and(
|
|
256
|
+
eq(flowChats.flowId, input.flowId),
|
|
257
|
+
ne(flowChats.versionId, input.toVersionId),
|
|
258
|
+
input.chatIds === void 0 ? void 0 : inArray(flowChats.chatId, input.chatIds)
|
|
259
|
+
);
|
|
236
260
|
const batch = db.select({ chatId: flowChats.chatId }).from(flowChats).where(notYetMoved).orderBy(asc(flowChats.chatId)).limit(input.limit);
|
|
237
261
|
const moved = db.update(flowChats).set({ versionId: input.toVersionId, stateId: mappedStateSql(input.map), pending: pendingWith(change), updatedAt: toMs(input.at) }).where(and(inArray(flowChats.chatId, batch), notYetMoved)).returning({ chatId: flowChats.chatId }).all();
|
|
238
262
|
return { moved: moved.map((row) => row.chatId) };
|
package/dist/sqlite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sqlite.ts","../src/store/sqlite.ts","../src/store/sqlite-schema.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport { createSqliteFlowStore, type SqliteFlowStore } from \"./store/sqlite\";\n\nexport type { SqliteFlowStore } from \"./store/sqlite\";\nexport { FLOWS_MIGRATIONS_TABLE } from \"./store/migration-settings\";\nexport { flowChats, flowClientCards, flowFirstMessages, flowVersions } from \"./store/sqlite-schema\";\n\n/** Миграции лежат в пакете рядом с `dist`: `drizzle/sqlite`. */\nconst MIGRATIONS_FOLDER = fileURLToPath(new URL(\"../drizzle/sqlite\", import.meta.url));\n\n/**\n * Сценарии в файле SQLite; файл может быть общим с хранилищем чатов (`createSqliteChatStorage`).\n * Таблицы приводятся к последней миграции при открытии.\n */\nexport function sqliteFlowStore(filePath: string): SqliteFlowStore {\n return createSqliteFlowStore(filePath, MIGRATIONS_FOLDER);\n}\n","import Database from \"better-sqlite3\";\nimport { and, asc, eq, inArray, isNull, lt, lte, ne, or, sql, type SQL } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { migrate } from \"drizzle-orm/better-sqlite3/migrator\";\nimport type { StateMap } from \"../engine/migrate-run\";\nimport type { FlowChatRecord, FlowStore, PendingFlowChange, SideTaskRecord } from \"./flow-store\";\nimport { appliedInTime, byVersionAndState, compareText, migratePending, pendingAdded } from \"./flow-store-shared\";\nimport { FLOWS_MIGRATIONS_TABLE } from \"./migration-settings\";\nimport { flowChats, flowClientCards, flowFirstMessages, flowPeople, flowSideTasks, flowVersions } from \"./sqlite-schema\";\n\n/** Хранилище в файле SQLite: `close` обязателен — файл закрывает приложение при остановке. */\nexport type SqliteFlowStore = FlowStore & Required<Pick<FlowStore, \"close\">>;\n\ntype ChatRow = typeof flowChats.$inferSelect;\n\nconst toMs = (iso: string) => Date.parse(iso);\nconst toIso = (ms: number) => new Date(ms).toISOString();\n\nfunction toRecord(row: ChatRow): FlowChatRecord {\n return {\n chatId: row.chatId,\n ownerId: row.ownerId,\n flowId: row.flowId,\n versionId: row.versionId,\n stateId: row.stateId,\n pending: row.pending,\n dueAt: row.dueAt === null ? null : toIso(row.dueAt),\n dueExitId: row.dueExitId,\n dueBranchHead: row.dueBranchHead,\n timeZone: row.timeZone,\n updatedAt: toIso(row.updatedAt),\n endedAt: row.endedAt === null ? null : toIso(row.endedAt),\n endReason: row.endReason,\n pausedUntil: row.pausedUntil === null ? null : toIso(row.pausedUntil),\n windowClosed: row.windowClosed,\n dayClosedOn: row.dayClosedOn,\n delivery: row.delivery,\n placement: row.placement,\n };\n}\n\ntype SideTaskRow = typeof flowSideTasks.$inferSelect;\nconst toSideTask = (row: SideTaskRow): SideTaskRecord => ({\n taskId: row.taskId,\n chatId: row.chatId === \"\" ? null : row.chatId,\n asks: row.asks,\n doneAt: row.doneAt === null ? null : toIso(row.doneAt),\n});\n\n/** Дописать изменение в конец `pending`, если такого ключа там ещё нет. Одно выражение — одна запись базы. */\nconst pendingWith = (change: PendingFlowChange): SQL =>\n sql`case when exists (select 1 from json_each(${flowChats.pending}) where json_extract(value, '$.key') = ${change.key})\n then ${flowChats.pending} else json_insert(${flowChats.pending}, '$[#]', json(${JSON.stringify(change)})) end`;\n\n/** Состояние по карте перевода: удалённые — в выбранные, остальные как были. */\nfunction mappedStateSql(map: StateMap): SQL {\n const entries = Object.entries(map);\n if (entries.length === 0) return sql`${flowChats.stateId}`;\n const cases = entries.map(([from, to]) => sql`when ${from} then ${to}`);\n return sql`case ${flowChats.stateId} ${sql.join(cases, sql` `)} else ${flowChats.stateId} end`;\n}\n\n/** Таймер наступил, никто его не держит, разговор идёт и не на паузе. Аренда до `lease_until` включительно ещё живая. */\nconst dueAndFree = (now: number) =>\n and(\n lte(flowChats.dueAt, now),\n or(isNull(flowChats.leaseUntil), lt(flowChats.leaseUntil, now)),\n isNull(flowChats.endedAt),\n or(isNull(flowChats.pausedUntil), lte(flowChats.pausedUntil, now)),\n );\n\n/**\n * Сценарии в файле SQLite. Схема приводится к последней миграции при открытии; файл может быть общим с хранилищем\n * чатов. Несколько копий сервера на одном файле делят таймеры арендой: каждое изменение — одно условное `update`\n * или короткая транзакция с захватом записи (`immediate`).\n */\nexport function createSqliteFlowStore(filePath: string, migrationsFolder: string): SqliteFlowStore {\n const sqlite = new Database(filePath);\n sqlite.pragma(\"journal_mode = WAL\");\n sqlite.pragma(\"busy_timeout = 5000\");\n const db = drizzle(sqlite);\n migrate(db, { migrationsFolder, migrationsTable: FLOWS_MIGRATIONS_TABLE });\n\n const getChat = (chatId: string) => db.select().from(flowChats).where(eq(flowChats.chatId, chatId)).get();\n\n return {\n async pinChat(input) {\n db.insert(flowChats)\n .values({\n chatId: input.chatId,\n ownerId: input.ownerId,\n flowId: input.flowId,\n versionId: input.versionId,\n stateId: input.stateId,\n pending: [],\n timeZone: input.timeZone,\n ...(input.placement && { placement: input.placement }),\n updatedAt: toMs(input.at),\n })\n .onConflictDoNothing()\n .run();\n return toRecord(getChat(input.chatId)!);\n },\n async getChat(chatId) {\n const row = getChat(chatId);\n return row ? toRecord(row) : null;\n },\n async updateChat(chatId, patch, at) {\n const row = db\n .update(flowChats)\n .set({ versionId: patch.versionId, stateId: patch.stateId, timeZone: patch.timeZone, placement: patch.placement, updatedAt: toMs(at) })\n .where(eq(flowChats.chatId, chatId))\n .returning()\n .get();\n return row ? toRecord(row) : null;\n },\n async deleteChat(chatId) {\n db.delete(flowChats).where(eq(flowChats.chatId, chatId)).run();\n },\n\n async addPending(chatId, change) {\n // Прочитать и дописать под захватом записи: две копии с одним ключом не дают двух записей.\n const add = sqlite.transaction(() => {\n const row = db.select({ pending: flowChats.pending }).from(flowChats).where(eq(flowChats.chatId, chatId)).get();\n const next = row && pendingAdded(row.pending, change);\n if (next) db.update(flowChats).set({ pending: next }).where(eq(flowChats.chatId, chatId)).run();\n });\n add.immediate();\n },\n async takePending(chatId) {\n // Прочитать и очистить под захватом записи: вторая копия ждёт и видит уже пустой список (кроме применённых в срок).\n const take = sqlite.transaction(() => {\n const row = db.select({ pending: flowChats.pending }).from(flowChats).where(eq(flowChats.chatId, chatId)).get();\n if (!row || row.pending.length === 0) return [];\n db.update(flowChats).set({ pending: row.pending.filter(appliedInTime) }).where(eq(flowChats.chatId, chatId)).run();\n return row.pending;\n });\n return take.immediate();\n },\n\n async setDue(chatId, due, options) {\n db.update(flowChats)\n .set({\n dueAt: due ? toMs(due.at) : null,\n dueExitId: due?.exitId ?? null,\n dueBranchHead: due?.branchHead ?? null,\n leaseUntil: null,\n leaseOwner: null,\n })\n .where(and(eq(flowChats.chatId, chatId), options?.unlessDelivery ? isNull(flowChats.delivery) : undefined))\n .run();\n },\n async leaseDue({ now, owner, leaseMs, limit }) {\n const nowMs = toMs(now);\n // Выбор и аренда — одно `update`: из двух копий, пришедших одновременно, строку получит только первая.\n const due = db\n .select({ chatId: flowChats.chatId })\n .from(flowChats)\n .where(dueAndFree(nowMs))\n .orderBy(asc(flowChats.dueAt), asc(flowChats.chatId))\n .limit(limit);\n const rows = db\n .update(flowChats)\n .set({ leaseOwner: owner, leaseUntil: nowMs + leaseMs })\n .where(and(inArray(flowChats.chatId, due), dueAndFree(nowMs)))\n .returning()\n .all();\n return rows.map(toRecord).sort((left, right) => compareText(left.dueAt!, right.dueAt!) || compareText(left.chatId, right.chatId));\n },\n async releaseLease(chatId, owner) {\n db.update(flowChats)\n .set({ leaseOwner: null, leaseUntil: null })\n .where(and(eq(flowChats.chatId, chatId), eq(flowChats.leaseOwner, owner)))\n .run();\n },\n async renewLease({ chatId, owner, now, leaseMs }) {\n const renewed = db\n .update(flowChats)\n .set({ leaseUntil: toMs(now) + leaseMs })\n .where(and(eq(flowChats.chatId, chatId), eq(flowChats.leaseOwner, owner)))\n .returning({ chatId: flowChats.chatId })\n .all();\n return renewed.length > 0;\n },\n\n async saveVersion(input) {\n const inserted = db\n .insert(flowVersions)\n .values({ flowId: input.flowId, versionId: input.versionId, hash: input.hash, flow: input.flow, createdAt: toMs(input.at) })\n .onConflictDoNothing()\n .returning({ hash: flowVersions.hash })\n .all();\n if (inserted.length > 0) return \"new\";\n const saved = db\n .select({ hash: flowVersions.hash })\n .from(flowVersions)\n .where(and(eq(flowVersions.flowId, input.flowId), eq(flowVersions.versionId, input.versionId)))\n .get();\n return saved?.hash === input.hash ? \"same\" : \"conflict\";\n },\n async getVersion(flowId, versionId) {\n const row = db\n .select()\n .from(flowVersions)\n .where(and(eq(flowVersions.flowId, flowId), eq(flowVersions.versionId, versionId)))\n .get();\n return row ? { ...row, createdAt: toIso(row.createdAt) } : null;\n },\n\n async countChatsByState({ flowId }) {\n const rows = db\n .select({ versionId: flowChats.versionId, stateId: flowChats.stateId, chats: sql<number>`count(*)` })\n .from(flowChats)\n .where(eq(flowChats.flowId, flowId))\n .groupBy(flowChats.versionId, flowChats.stateId)\n .all();\n return rows.map((row) => ({ ...row, chats: Number(row.chats) })).sort(byVersionAndState);\n },\n async migrateBatch(input) {\n const change = migratePending(input);\n const notYetMoved = and(eq(flowChats.flowId, input.flowId), ne(flowChats.versionId, input.toVersionId));\n const batch = db.select({ chatId: flowChats.chatId }).from(flowChats).where(notYetMoved).orderBy(asc(flowChats.chatId)).limit(input.limit);\n const moved = db\n .update(flowChats)\n .set({ versionId: input.toVersionId, stateId: mappedStateSql(input.map), pending: pendingWith(change), updatedAt: toMs(input.at) })\n .where(and(inArray(flowChats.chatId, batch), notYetMoved))\n .returning({ chatId: flowChats.chatId })\n .all();\n return { moved: moved.map((row) => row.chatId) };\n },\n\n async readCard(ownerId) {\n const rows = db.select().from(flowClientCards).where(eq(flowClientCards.ownerId, ownerId)).all();\n return rows\n .map((row) => ({ fieldId: row.fieldId, value: row.value, updatedAt: toIso(row.updatedAt), chatId: row.chatId, flowId: row.flowId }))\n .sort((left, right) => compareText(left.fieldId, right.fieldId));\n },\n async writeCardField(input) {\n const entry = { value: input.value, updatedAt: toMs(input.at), chatId: input.chatId, flowId: input.flowId };\n db.insert(flowClientCards)\n .values({ ownerId: input.ownerId, fieldId: input.fieldId, ...entry })\n .onConflictDoUpdate({ target: [flowClientCards.ownerId, flowClientCards.fieldId], set: entry })\n .run();\n },\n async forgetCard(ownerId, fieldId) {\n db.delete(flowClientCards)\n .where(and(eq(flowClientCards.ownerId, ownerId), fieldId === \"all\" ? undefined : eq(flowClientCards.fieldId, fieldId)))\n .run();\n },\n\n async endChat(chatId, { reason, at }) {\n db.update(flowChats)\n .set({ endedAt: toMs(at), endReason: reason, dueAt: null, dueExitId: null, dueBranchHead: null, leaseUntil: null, leaseOwner: null })\n .where(and(eq(flowChats.chatId, chatId), isNull(flowChats.endedAt)))\n .run();\n const row = getChat(chatId);\n return row ? toRecord(row) : null;\n },\n async setPause(chatId, until) {\n const row = db\n .update(flowChats)\n .set({ pausedUntil: until === null ? null : toMs(until) })\n .where(eq(flowChats.chatId, chatId))\n .returning()\n .get();\n return row ? toRecord(row) : null;\n },\n async setWindowClosed(chatId, closed) {\n db.update(flowChats).set({ windowClosed: closed }).where(eq(flowChats.chatId, chatId)).run();\n },\n async setDayClosed(chatId, day) {\n db.update(flowChats).set({ dayClosedOn: day }).where(eq(flowChats.chatId, chatId)).run();\n },\n async setDelivery(chatId, delivery) {\n const clean = delivery === null ? null : { ...delivery, retryAt: toIso(toMs(delivery.retryAt)) };\n db.update(flowChats).set({ delivery: clean }).where(eq(flowChats.chatId, chatId)).run();\n },\n\n async readTimeZone(ownerId) {\n return db.select({ timeZone: flowPeople.timeZone }).from(flowPeople).where(eq(flowPeople.ownerId, ownerId)).get()?.timeZone ?? null;\n },\n async writeTimeZone(ownerId, timeZone, at) {\n db.insert(flowPeople)\n .values({ ownerId, timeZone, updatedAt: toMs(at) })\n .onConflictDoUpdate({ target: flowPeople.ownerId, set: { timeZone, updatedAt: toMs(at) } })\n .run();\n },\n\n async readSideTasks(ownerId, chatId) {\n const rows = db\n .select()\n .from(flowSideTasks)\n .where(and(eq(flowSideTasks.ownerId, ownerId), or(eq(flowSideTasks.chatId, \"\"), eq(flowSideTasks.chatId, chatId))))\n .all();\n return rows.map(toSideTask).sort((left, right) => compareText(left.taskId, right.taskId));\n },\n async forgetSideTasks(ownerId) {\n db.delete(flowSideTasks).where(eq(flowSideTasks.ownerId, ownerId)).run();\n },\n async noteSideTask(input) {\n const asked = input.status === \"asked\";\n const doneAt = asked ? null : toMs(input.at);\n const row = db\n .insert(flowSideTasks)\n .values({ ownerId: input.ownerId, taskId: input.taskId, chatId: input.chatId ?? \"\", asks: asked ? 1 : 0, doneAt })\n .onConflictDoUpdate({\n target: [flowSideTasks.ownerId, flowSideTasks.taskId, flowSideTasks.chatId],\n set: asked ? { asks: sql`${flowSideTasks.asks} + 1` } : { doneAt: sql`coalesce(${flowSideTasks.doneAt}, ${doneAt})` },\n })\n .returning()\n .get();\n return toSideTask(row!);\n },\n\n async countFirstMessages(ownerId, day) {\n const row = db\n .select({ count: flowFirstMessages.count })\n .from(flowFirstMessages)\n .where(and(eq(flowFirstMessages.ownerId, ownerId), eq(flowFirstMessages.day, day)))\n .get();\n return row?.count ?? 0;\n },\n async addFirstMessage(ownerId, day) {\n const row = db\n .insert(flowFirstMessages)\n .values({ ownerId, day, count: 1 })\n .onConflictDoUpdate({ target: [flowFirstMessages.ownerId, flowFirstMessages.day], set: { count: sql`${flowFirstMessages.count} + 1` } })\n .returning({ count: flowFirstMessages.count })\n .get();\n return row!.count;\n },\n\n async close() {\n if (sqlite.open) sqlite.close();\n },\n };\n}\n","import type { AgentFlow } from \"../format/flow-format\";\nimport type { FlowEndReason } from \"../engine/flow-run\";\nimport type { FlowChatPlacement, FlowPendingDelivery, PendingFlowChange } from \"./flow-store\";\nimport { index, integer, primaryKey, sqliteTable, text } from \"drizzle-orm/sqlite-core\";\n\n// Времена — миллисекунды с 1970 года, JSON — текстом.\n\n/** Чаты, которые идут по сценарию. */\nexport const flowChats = sqliteTable(\n \"flow_chats\",\n {\n chatId: text(\"chat_id\").primaryKey(),\n ownerId: text(\"owner_id\").notNull(),\n flowId: text(\"flow_id\").notNull(),\n versionId: text(\"version_id\").notNull(),\n stateId: text(\"state_id\").notNull(),\n pending: text(\"pending\", { mode: \"json\" }).$type<PendingFlowChange[]>().notNull().default([]),\n dueAt: integer(\"due_at\"),\n dueExitId: text(\"due_exit_id\"),\n dueBranchHead: text(\"due_branch_head\"),\n timeZone: text(\"time_zone\").notNull(),\n leaseUntil: integer(\"lease_until\"),\n leaseOwner: text(\"lease_owner\"),\n updatedAt: integer(\"updated_at\").notNull(),\n endedAt: integer(\"ended_at\"),\n endReason: text(\"end_reason\").$type<FlowEndReason>(),\n pausedUntil: integer(\"paused_until\"),\n windowClosed: integer(\"window_closed\", { mode: \"boolean\" }).notNull().default(false),\n dayClosedOn: text(\"day_closed_on\"),\n delivery: text(\"delivery\", { mode: \"json\" }).$type<FlowPendingDelivery>(),\n placement: text(\"placement\", { mode: \"json\" }).$type<FlowChatPlacement>(),\n },\n (table) => [\n index(\"flow_chats_due_idx\").on(table.dueAt),\n index(\"flow_chats_flow_state_idx\").on(table.flowId, table.versionId, table.stateId),\n index(\"flow_chats_owner_idx\").on(table.ownerId),\n ],\n);\n\n/** Сохранённые версии сценариев: чат идёт по своей версии, пока его не перевели. */\nexport const flowVersions = sqliteTable(\n \"flow_versions\",\n {\n flowId: text(\"flow_id\").notNull(),\n versionId: text(\"version_id\").notNull(),\n hash: text(\"hash\").notNull(),\n flow: text(\"flow\", { mode: \"json\" }).$type<AgentFlow>().notNull(),\n createdAt: integer(\"created_at\").notNull(),\n },\n (table) => [primaryKey({ columns: [table.flowId, table.versionId] })],\n);\n\n/** Карточка клиента: поле за полем, переживает чаты. */\nexport const flowClientCards = sqliteTable(\n \"flow_client_cards\",\n {\n ownerId: text(\"owner_id\").notNull(),\n fieldId: text(\"field_id\").notNull(),\n value: text(\"value\", { mode: \"json\" }).notNull(),\n updatedAt: integer(\"updated_at\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n flowId: text(\"flow_id\").notNull(),\n },\n (table) => [primaryKey({ columns: [table.ownerId, table.fieldId] })],\n);\n\n/** Сколько сообщений агент написал первым за сутки человека. */\nexport const flowFirstMessages = sqliteTable(\n \"flow_first_messages\",\n {\n ownerId: text(\"owner_id\").notNull(),\n day: text(\"day\").notNull(),\n count: integer(\"count\").notNull().default(0),\n },\n (table) => [primaryKey({ columns: [table.ownerId, table.day] })],\n);\n\n/** Пояс человека: общий для всех его чатов. */\nexport const flowPeople = sqliteTable(\"flow_people\", {\n ownerId: text(\"owner_id\").primaryKey(),\n timeZone: text(\"time_zone\").notNull(),\n updatedAt: integer(\"updated_at\").notNull(),\n});\n\n/** Попытки попутных задач. `chat_id` пустой — попытки на человека, иначе — на разговор. */\nexport const flowSideTasks = sqliteTable(\n \"flow_side_tasks\",\n {\n ownerId: text(\"owner_id\").notNull(),\n taskId: text(\"task_id\").notNull(),\n chatId: text(\"chat_id\").notNull().default(\"\"),\n asks: integer(\"asks\").notNull().default(0),\n doneAt: integer(\"done_at\"),\n },\n (table) => [primaryKey({ columns: [table.ownerId, table.taskId, table.chatId] })],\n);\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/sqlite.ts","../src/store/sqlite.ts","../src/store/sqlite-schema.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport { createSqliteFlowStore, type SqliteFlowStore } from \"./store/sqlite\";\n\nexport type { SqliteFlowStore } from \"./store/sqlite\";\nexport { FLOWS_MIGRATIONS_TABLE } from \"./store/migration-settings\";\nexport { flowChats, flowClientCards, flowFirstMessages, flowVersions } from \"./store/sqlite-schema\";\n\n/** Миграции лежат в пакете рядом с `dist`: `drizzle/sqlite`. */\nconst MIGRATIONS_FOLDER = fileURLToPath(new URL(\"../drizzle/sqlite\", import.meta.url));\n\n/**\n * Сценарии в файле SQLite; файл может быть общим с хранилищем чатов (`createSqliteChatStorage`).\n * Таблицы приводятся к последней миграции при открытии.\n */\nexport function sqliteFlowStore(filePath: string): SqliteFlowStore {\n return createSqliteFlowStore(filePath, MIGRATIONS_FOLDER);\n}\n","import Database from \"better-sqlite3\";\nimport { and, asc, eq, inArray, isNull, lt, lte, ne, or, sql, type SQL } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { migrate } from \"drizzle-orm/better-sqlite3/migrator\";\nimport type { StateMap } from \"../engine/migrate-run\";\nimport type { FlowChatRecord, FlowStore, PendingFlowChange, SideTaskRecord } from \"./flow-store\";\nimport { appliedInTime, byVersionAndState, compareText, migratePending, pendingAdded } from \"./flow-store-shared\";\nimport { FLOWS_MIGRATIONS_TABLE } from \"./migration-settings\";\nimport { flowChats, flowClientCards, flowFirstMessages, flowPeople, flowSideTasks, flowVersions } from \"./sqlite-schema\";\n\n/** Хранилище в файле SQLite: `close` обязателен — файл закрывает приложение при остановке. */\nexport type SqliteFlowStore = FlowStore & Required<Pick<FlowStore, \"close\">>;\n\ntype ChatRow = typeof flowChats.$inferSelect;\n\nconst toMs = (iso: string) => Date.parse(iso);\nconst toIso = (ms: number) => new Date(ms).toISOString();\n\nfunction toRecord(row: ChatRow): FlowChatRecord {\n return {\n chatId: row.chatId,\n ownerId: row.ownerId,\n flowId: row.flowId,\n versionId: row.versionId,\n stateId: row.stateId,\n pending: row.pending,\n dueAt: row.dueAt === null ? null : toIso(row.dueAt),\n dueExitId: row.dueExitId,\n dueBranchHead: row.dueBranchHead,\n timeZone: row.timeZone,\n updatedAt: toIso(row.updatedAt),\n endedAt: row.endedAt === null ? null : toIso(row.endedAt),\n endReason: row.endReason,\n pausedUntil: row.pausedUntil === null ? null : toIso(row.pausedUntil),\n windowClosed: row.windowClosed,\n dayClosedOn: row.dayClosedOn,\n delivery: row.delivery,\n placement: row.placement,\n };\n}\n\ntype SideTaskRow = typeof flowSideTasks.$inferSelect;\nconst toSideTask = (row: SideTaskRow): SideTaskRecord => ({\n taskId: row.taskId,\n chatId: row.chatId === \"\" ? null : row.chatId,\n asks: row.asks,\n doneAt: row.doneAt === null ? null : toIso(row.doneAt),\n});\n\n/** Дописать изменение в конец `pending`, если такого ключа там ещё нет. Одно выражение — одна запись базы. */\nconst pendingWith = (change: PendingFlowChange): SQL =>\n sql`case when exists (select 1 from json_each(${flowChats.pending}) where json_extract(value, '$.key') = ${change.key})\n then ${flowChats.pending} else json_insert(${flowChats.pending}, '$[#]', json(${JSON.stringify(change)})) end`;\n\n/** Состояние по карте перевода: удалённые — в выбранные, остальные как были. */\nfunction mappedStateSql(map: StateMap): SQL {\n const entries = Object.entries(map);\n if (entries.length === 0) return sql`${flowChats.stateId}`;\n const cases = entries.map(([from, to]) => sql`when ${from} then ${to}`);\n return sql`case ${flowChats.stateId} ${sql.join(cases, sql` `)} else ${flowChats.stateId} end`;\n}\n\n/** Таймер наступил, никто его не держит, разговор идёт и не на паузе. Аренда до `lease_until` включительно ещё живая. */\nconst dueAndFree = (now: number) =>\n and(\n lte(flowChats.dueAt, now),\n or(isNull(flowChats.leaseUntil), lt(flowChats.leaseUntil, now)),\n isNull(flowChats.endedAt),\n or(isNull(flowChats.pausedUntil), lte(flowChats.pausedUntil, now)),\n );\n\n/**\n * Сценарии в файле SQLite. Схема приводится к последней миграции при открытии; файл может быть общим с хранилищем\n * чатов. Несколько копий сервера на одном файле делят таймеры арендой: каждое изменение — одно условное `update`\n * или короткая транзакция с захватом записи (`immediate`).\n */\nexport function createSqliteFlowStore(filePath: string, migrationsFolder: string): SqliteFlowStore {\n const sqlite = new Database(filePath);\n sqlite.pragma(\"journal_mode = WAL\");\n sqlite.pragma(\"busy_timeout = 5000\");\n const db = drizzle(sqlite);\n migrate(db, { migrationsFolder, migrationsTable: FLOWS_MIGRATIONS_TABLE });\n\n const getChat = (chatId: string) => db.select().from(flowChats).where(eq(flowChats.chatId, chatId)).get();\n\n return {\n async pinChat(input) {\n db.insert(flowChats)\n .values({\n chatId: input.chatId,\n ownerId: input.ownerId,\n flowId: input.flowId,\n versionId: input.versionId,\n stateId: input.stateId,\n pending: [],\n timeZone: input.timeZone,\n ...(input.placement && { placement: input.placement }),\n updatedAt: toMs(input.at),\n })\n .onConflictDoNothing()\n .run();\n return toRecord(getChat(input.chatId)!);\n },\n async getChat(chatId) {\n const row = getChat(chatId);\n return row ? toRecord(row) : null;\n },\n async updateChat(chatId, patch, at) {\n const row = db\n .update(flowChats)\n .set({ versionId: patch.versionId, stateId: patch.stateId, timeZone: patch.timeZone, placement: patch.placement, updatedAt: toMs(at) })\n .where(eq(flowChats.chatId, chatId))\n .returning()\n .get();\n return row ? toRecord(row) : null;\n },\n async deleteChat(chatId) {\n db.delete(flowChats).where(eq(flowChats.chatId, chatId)).run();\n },\n\n async addPending(chatId, change) {\n // Прочитать и дописать под захватом записи: две копии с одним ключом не дают двух записей.\n const add = sqlite.transaction(() => {\n const row = db.select({ pending: flowChats.pending }).from(flowChats).where(eq(flowChats.chatId, chatId)).get();\n const next = row && pendingAdded(row.pending, change);\n if (next) db.update(flowChats).set({ pending: next }).where(eq(flowChats.chatId, chatId)).run();\n });\n add.immediate();\n },\n async takePending(chatId) {\n // Прочитать и очистить под захватом записи: вторая копия ждёт и видит уже пустой список (кроме применённых в срок).\n const take = sqlite.transaction(() => {\n const row = db.select({ pending: flowChats.pending }).from(flowChats).where(eq(flowChats.chatId, chatId)).get();\n if (!row || row.pending.length === 0) return [];\n db.update(flowChats).set({ pending: row.pending.filter(appliedInTime) }).where(eq(flowChats.chatId, chatId)).run();\n return row.pending;\n });\n return take.immediate();\n },\n\n async setDue(chatId, due, options) {\n db.update(flowChats)\n .set({\n dueAt: due ? toMs(due.at) : null,\n dueExitId: due?.exitId ?? null,\n dueBranchHead: due?.branchHead ?? null,\n leaseUntil: null,\n leaseOwner: null,\n })\n .where(and(eq(flowChats.chatId, chatId), options?.unlessDelivery ? isNull(flowChats.delivery) : undefined))\n .run();\n },\n async leaseDue({ now, owner, leaseMs, limit }) {\n const nowMs = toMs(now);\n // Выбор и аренда — одно `update`: из двух копий, пришедших одновременно, строку получит только первая.\n const due = db\n .select({ chatId: flowChats.chatId })\n .from(flowChats)\n .where(dueAndFree(nowMs))\n .orderBy(asc(flowChats.dueAt), asc(flowChats.chatId))\n .limit(limit);\n const rows = db\n .update(flowChats)\n .set({ leaseOwner: owner, leaseUntil: nowMs + leaseMs })\n .where(and(inArray(flowChats.chatId, due), dueAndFree(nowMs)))\n .returning()\n .all();\n return rows.map(toRecord).sort((left, right) => compareText(left.dueAt!, right.dueAt!) || compareText(left.chatId, right.chatId));\n },\n async releaseLease(chatId, owner) {\n db.update(flowChats)\n .set({ leaseOwner: null, leaseUntil: null })\n .where(and(eq(flowChats.chatId, chatId), eq(flowChats.leaseOwner, owner)))\n .run();\n },\n async renewLease({ chatId, owner, now, leaseMs }) {\n const renewed = db\n .update(flowChats)\n .set({ leaseUntil: toMs(now) + leaseMs })\n .where(and(eq(flowChats.chatId, chatId), eq(flowChats.leaseOwner, owner)))\n .returning({ chatId: flowChats.chatId })\n .all();\n return renewed.length > 0;\n },\n\n async saveVersion(input) {\n const inserted = db\n .insert(flowVersions)\n .values({ flowId: input.flowId, versionId: input.versionId, hash: input.hash, flow: input.flow, createdAt: toMs(input.at) })\n .onConflictDoNothing()\n .returning({ hash: flowVersions.hash })\n .all();\n if (inserted.length > 0) return \"new\";\n const saved = db\n .select({ hash: flowVersions.hash })\n .from(flowVersions)\n .where(and(eq(flowVersions.flowId, input.flowId), eq(flowVersions.versionId, input.versionId)))\n .get();\n return saved?.hash === input.hash ? \"same\" : \"conflict\";\n },\n async getVersion(flowId, versionId) {\n const row = db\n .select()\n .from(flowVersions)\n .where(and(eq(flowVersions.flowId, flowId), eq(flowVersions.versionId, versionId)))\n .get();\n return row ? { ...row, createdAt: toIso(row.createdAt) } : null;\n },\n\n async countChatsByState({ flowId }) {\n const rows = db\n .select({ versionId: flowChats.versionId, stateId: flowChats.stateId, chats: sql<number>`count(*)` })\n .from(flowChats)\n .where(eq(flowChats.flowId, flowId))\n .groupBy(flowChats.versionId, flowChats.stateId)\n .all();\n return rows.map((row) => ({ ...row, chats: Number(row.chats) })).sort(byVersionAndState);\n },\n async listFlowChats({ flowId, exceptVersionId, chatIds }) {\n if (chatIds?.length === 0) return [];\n const rows = db\n .select({\n chatId: flowChats.chatId,\n ownerId: flowChats.ownerId,\n flowId: flowChats.flowId,\n versionId: flowChats.versionId,\n stateId: flowChats.stateId,\n endedAt: flowChats.endedAt,\n endReason: flowChats.endReason,\n })\n .from(flowChats)\n .where(\n and(\n eq(flowChats.flowId, flowId),\n exceptVersionId === undefined ? undefined : ne(flowChats.versionId, exceptVersionId),\n chatIds === undefined ? undefined : inArray(flowChats.chatId, chatIds),\n ),\n )\n .orderBy(asc(flowChats.chatId))\n .all();\n return rows.map((row) => ({ ...row, endedAt: row.endedAt === null ? null : toIso(row.endedAt) }));\n },\n async migrateBatch(input) {\n const change = migratePending(input);\n if (input.chatIds?.length === 0) return { moved: [] };\n const notYetMoved = and(\n eq(flowChats.flowId, input.flowId),\n ne(flowChats.versionId, input.toVersionId),\n input.chatIds === undefined ? undefined : inArray(flowChats.chatId, input.chatIds),\n );\n const batch = db.select({ chatId: flowChats.chatId }).from(flowChats).where(notYetMoved).orderBy(asc(flowChats.chatId)).limit(input.limit);\n const moved = db\n .update(flowChats)\n .set({ versionId: input.toVersionId, stateId: mappedStateSql(input.map), pending: pendingWith(change), updatedAt: toMs(input.at) })\n .where(and(inArray(flowChats.chatId, batch), notYetMoved))\n .returning({ chatId: flowChats.chatId })\n .all();\n return { moved: moved.map((row) => row.chatId) };\n },\n\n async readCard(ownerId) {\n const rows = db.select().from(flowClientCards).where(eq(flowClientCards.ownerId, ownerId)).all();\n return rows\n .map((row) => ({ fieldId: row.fieldId, value: row.value, updatedAt: toIso(row.updatedAt), chatId: row.chatId, flowId: row.flowId }))\n .sort((left, right) => compareText(left.fieldId, right.fieldId));\n },\n async writeCardField(input) {\n const entry = { value: input.value, updatedAt: toMs(input.at), chatId: input.chatId, flowId: input.flowId };\n db.insert(flowClientCards)\n .values({ ownerId: input.ownerId, fieldId: input.fieldId, ...entry })\n .onConflictDoUpdate({ target: [flowClientCards.ownerId, flowClientCards.fieldId], set: entry })\n .run();\n },\n async forgetCard(ownerId, fieldId) {\n db.delete(flowClientCards)\n .where(and(eq(flowClientCards.ownerId, ownerId), fieldId === \"all\" ? undefined : eq(flowClientCards.fieldId, fieldId)))\n .run();\n },\n\n async endChat(chatId, { reason, at }) {\n db.update(flowChats)\n .set({ endedAt: toMs(at), endReason: reason, dueAt: null, dueExitId: null, dueBranchHead: null, leaseUntil: null, leaseOwner: null })\n .where(and(eq(flowChats.chatId, chatId), isNull(flowChats.endedAt)))\n .run();\n const row = getChat(chatId);\n return row ? toRecord(row) : null;\n },\n async setPause(chatId, until) {\n const row = db\n .update(flowChats)\n .set({ pausedUntil: until === null ? null : toMs(until) })\n .where(eq(flowChats.chatId, chatId))\n .returning()\n .get();\n return row ? toRecord(row) : null;\n },\n async setWindowClosed(chatId, closed) {\n db.update(flowChats).set({ windowClosed: closed }).where(eq(flowChats.chatId, chatId)).run();\n },\n async setDayClosed(chatId, day) {\n db.update(flowChats).set({ dayClosedOn: day }).where(eq(flowChats.chatId, chatId)).run();\n },\n async setDelivery(chatId, delivery) {\n const clean = delivery === null ? null : { ...delivery, retryAt: toIso(toMs(delivery.retryAt)) };\n db.update(flowChats).set({ delivery: clean }).where(eq(flowChats.chatId, chatId)).run();\n },\n\n async readTimeZone(ownerId) {\n return db.select({ timeZone: flowPeople.timeZone }).from(flowPeople).where(eq(flowPeople.ownerId, ownerId)).get()?.timeZone ?? null;\n },\n async writeTimeZone(ownerId, timeZone, at) {\n db.insert(flowPeople)\n .values({ ownerId, timeZone, updatedAt: toMs(at) })\n .onConflictDoUpdate({ target: flowPeople.ownerId, set: { timeZone, updatedAt: toMs(at) } })\n .run();\n },\n\n async readSideTasks(ownerId, chatId) {\n const rows = db\n .select()\n .from(flowSideTasks)\n .where(and(eq(flowSideTasks.ownerId, ownerId), or(eq(flowSideTasks.chatId, \"\"), eq(flowSideTasks.chatId, chatId))))\n .all();\n return rows.map(toSideTask).sort((left, right) => compareText(left.taskId, right.taskId));\n },\n async forgetSideTasks(ownerId) {\n db.delete(flowSideTasks).where(eq(flowSideTasks.ownerId, ownerId)).run();\n },\n async noteSideTask(input) {\n const asked = input.status === \"asked\";\n const doneAt = asked ? null : toMs(input.at);\n const row = db\n .insert(flowSideTasks)\n .values({ ownerId: input.ownerId, taskId: input.taskId, chatId: input.chatId ?? \"\", asks: asked ? 1 : 0, doneAt })\n .onConflictDoUpdate({\n target: [flowSideTasks.ownerId, flowSideTasks.taskId, flowSideTasks.chatId],\n set: asked ? { asks: sql`${flowSideTasks.asks} + 1` } : { doneAt: sql`coalesce(${flowSideTasks.doneAt}, ${doneAt})` },\n })\n .returning()\n .get();\n return toSideTask(row!);\n },\n\n async countFirstMessages(ownerId, day) {\n const row = db\n .select({ count: flowFirstMessages.count })\n .from(flowFirstMessages)\n .where(and(eq(flowFirstMessages.ownerId, ownerId), eq(flowFirstMessages.day, day)))\n .get();\n return row?.count ?? 0;\n },\n async addFirstMessage(ownerId, day) {\n const row = db\n .insert(flowFirstMessages)\n .values({ ownerId, day, count: 1 })\n .onConflictDoUpdate({ target: [flowFirstMessages.ownerId, flowFirstMessages.day], set: { count: sql`${flowFirstMessages.count} + 1` } })\n .returning({ count: flowFirstMessages.count })\n .get();\n return row!.count;\n },\n\n async close() {\n if (sqlite.open) sqlite.close();\n },\n };\n}\n","import type { AgentFlow } from \"../format/flow-format\";\nimport type { FlowEndReason } from \"../engine/flow-run\";\nimport type { FlowChatPlacement, FlowPendingDelivery, PendingFlowChange } from \"./flow-store\";\nimport { index, integer, primaryKey, sqliteTable, text } from \"drizzle-orm/sqlite-core\";\n\n// Времена — миллисекунды с 1970 года, JSON — текстом.\n\n/** Чаты, которые идут по сценарию. */\nexport const flowChats = sqliteTable(\n \"flow_chats\",\n {\n chatId: text(\"chat_id\").primaryKey(),\n ownerId: text(\"owner_id\").notNull(),\n flowId: text(\"flow_id\").notNull(),\n versionId: text(\"version_id\").notNull(),\n stateId: text(\"state_id\").notNull(),\n pending: text(\"pending\", { mode: \"json\" }).$type<PendingFlowChange[]>().notNull().default([]),\n dueAt: integer(\"due_at\"),\n dueExitId: text(\"due_exit_id\"),\n dueBranchHead: text(\"due_branch_head\"),\n timeZone: text(\"time_zone\").notNull(),\n leaseUntil: integer(\"lease_until\"),\n leaseOwner: text(\"lease_owner\"),\n updatedAt: integer(\"updated_at\").notNull(),\n endedAt: integer(\"ended_at\"),\n endReason: text(\"end_reason\").$type<FlowEndReason>(),\n pausedUntil: integer(\"paused_until\"),\n windowClosed: integer(\"window_closed\", { mode: \"boolean\" }).notNull().default(false),\n dayClosedOn: text(\"day_closed_on\"),\n delivery: text(\"delivery\", { mode: \"json\" }).$type<FlowPendingDelivery>(),\n placement: text(\"placement\", { mode: \"json\" }).$type<FlowChatPlacement>(),\n },\n (table) => [\n index(\"flow_chats_due_idx\").on(table.dueAt),\n index(\"flow_chats_flow_state_idx\").on(table.flowId, table.versionId, table.stateId),\n index(\"flow_chats_owner_idx\").on(table.ownerId),\n ],\n);\n\n/** Сохранённые версии сценариев: чат идёт по своей версии, пока его не перевели. */\nexport const flowVersions = sqliteTable(\n \"flow_versions\",\n {\n flowId: text(\"flow_id\").notNull(),\n versionId: text(\"version_id\").notNull(),\n hash: text(\"hash\").notNull(),\n flow: text(\"flow\", { mode: \"json\" }).$type<AgentFlow>().notNull(),\n createdAt: integer(\"created_at\").notNull(),\n },\n (table) => [primaryKey({ columns: [table.flowId, table.versionId] })],\n);\n\n/** Карточка клиента: поле за полем, переживает чаты. */\nexport const flowClientCards = sqliteTable(\n \"flow_client_cards\",\n {\n ownerId: text(\"owner_id\").notNull(),\n fieldId: text(\"field_id\").notNull(),\n value: text(\"value\", { mode: \"json\" }).notNull(),\n updatedAt: integer(\"updated_at\").notNull(),\n chatId: text(\"chat_id\").notNull(),\n flowId: text(\"flow_id\").notNull(),\n },\n (table) => [primaryKey({ columns: [table.ownerId, table.fieldId] })],\n);\n\n/** Сколько сообщений агент написал первым за сутки человека. */\nexport const flowFirstMessages = sqliteTable(\n \"flow_first_messages\",\n {\n ownerId: text(\"owner_id\").notNull(),\n day: text(\"day\").notNull(),\n count: integer(\"count\").notNull().default(0),\n },\n (table) => [primaryKey({ columns: [table.ownerId, table.day] })],\n);\n\n/** Пояс человека: общий для всех его чатов. */\nexport const flowPeople = sqliteTable(\"flow_people\", {\n ownerId: text(\"owner_id\").primaryKey(),\n timeZone: text(\"time_zone\").notNull(),\n updatedAt: integer(\"updated_at\").notNull(),\n});\n\n/** Попытки попутных задач. `chat_id` пустой — попытки на человека, иначе — на разговор. */\nexport const flowSideTasks = sqliteTable(\n \"flow_side_tasks\",\n {\n ownerId: text(\"owner_id\").notNull(),\n taskId: text(\"task_id\").notNull(),\n chatId: text(\"chat_id\").notNull().default(\"\"),\n asks: integer(\"asks\").notNull().default(0),\n doneAt: integer(\"done_at\"),\n },\n (table) => [primaryKey({ columns: [table.ownerId, table.taskId, table.chatId] })],\n);\n"],"mappings":"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","names":[]}
|
|
@@ -23,7 +23,7 @@ export type FlowDialog = "json" | "migrate" | null;
|
|
|
23
23
|
/**
|
|
24
24
|
* Перевод идущих чатов на текущую версию. Данные и действие даёт приложение — редактор не знает, где сервер:
|
|
25
25
|
* `chatsByState` — чаты на прежних версиях (`flow.chatsByState`), `migrate` — перевод с картой удалённых состояний
|
|
26
|
-
* (`flow.migrateChats`).
|
|
26
|
+
* (`flow.migrateChats`). Отбор чатов в обоих вызовах один и тот же: окно показывает ровно тех, кого переведёт.
|
|
27
27
|
*/
|
|
28
28
|
export interface FlowMigration {
|
|
29
29
|
chatsByState(): Promise<ChatsInState[]>;
|
|
@@ -398,7 +398,41 @@ export interface FlowFinish {
|
|
|
398
398
|
run: FlowRun;
|
|
399
399
|
at: Date;
|
|
400
400
|
}
|
|
401
|
-
|
|
401
|
+
/** Чат сценария, который отбор (`filter`) решает считать и переводить или нет. */
|
|
402
|
+
export interface FlowChatCandidate {
|
|
403
|
+
chatId: string;
|
|
404
|
+
ownerId: string;
|
|
405
|
+
flowId: string;
|
|
406
|
+
/** Версия, на которой чат сейчас. */
|
|
407
|
+
versionId: string;
|
|
408
|
+
stateId: string;
|
|
409
|
+
/** Разговор закончился (`flow.end`, конец сценария, срок вышел). */
|
|
410
|
+
ended: boolean;
|
|
411
|
+
endReason: FlowEndReason | null;
|
|
412
|
+
/** Тестовый чат редактора (`FlowTestChat`, создан с `flowTestChatData()`). */
|
|
413
|
+
test: boolean;
|
|
414
|
+
/** Данные чата (`Chat.data`): пометки приложения. Чата уже нет в службе — пусто. */
|
|
415
|
+
data: Record<string, unknown>;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Какие чаты сценария считать и переводить. Одинаково в `chatsByState` и `migrateChats`: что посчитали, то и переведут.
|
|
419
|
+
* По умолчанию закончившиеся разговоры и тестовые чаты редактора не берутся.
|
|
420
|
+
*/
|
|
421
|
+
export interface FlowChatSelection {
|
|
422
|
+
/** Только эти чаты. */
|
|
423
|
+
chatIds?: string[];
|
|
424
|
+
/** Свой отбор приложения: `false` — чат не считать и не переводить. */
|
|
425
|
+
filter?: (chat: FlowChatCandidate) => boolean | Promise<boolean>;
|
|
426
|
+
/** Брать и закончившиеся разговоры. */
|
|
427
|
+
includeEnded?: boolean;
|
|
428
|
+
/** Брать и тестовые чаты редактора. */
|
|
429
|
+
includeTestChats?: boolean;
|
|
430
|
+
}
|
|
431
|
+
export interface ChatsByStateOptions extends FlowChatSelection {
|
|
432
|
+
/** Без чатов, которые уже на этой версии. */
|
|
433
|
+
exceptVersionId?: string;
|
|
434
|
+
}
|
|
435
|
+
export interface MigrateChatsInput extends FlowChatSelection {
|
|
402
436
|
flowId: string;
|
|
403
437
|
toVersionId: string;
|
|
404
438
|
/** Куда перевести чаты из состояний, которых нет в новой версии: id старого → id нового. */
|
|
@@ -411,10 +445,8 @@ export interface MigrateChatsInput {
|
|
|
411
445
|
}
|
|
412
446
|
export interface ChatMigrator {
|
|
413
447
|
/** Сколько чатов в каких состояниях; `exceptVersionId` — без чатов, которые уже на этой версии. */
|
|
414
|
-
chatsByState(flowId: string, options?:
|
|
415
|
-
|
|
416
|
-
}): Promise<ChatsInState[]>;
|
|
417
|
-
/** Переводит пачками; чаты применят перевод в начале своего следующего хода. */
|
|
448
|
+
chatsByState(flowId: string, options?: ChatsByStateOptions): Promise<ChatsInState[]>;
|
|
449
|
+
/** Переводит пачками отобранные чаты; они применят перевод в начале своего следующего хода. */
|
|
418
450
|
migrateChats(input: MigrateChatsInput): Promise<{
|
|
419
451
|
moved: number;
|
|
420
452
|
}>;
|
|
@@ -15,6 +15,11 @@ export interface ChatMigratorDeps {
|
|
|
15
15
|
flow: AgentFlow;
|
|
16
16
|
map: StateMap;
|
|
17
17
|
}): Promise<void>;
|
|
18
|
+
/** Данные чата (`Chat.data`) для отбора; чата нет в службе — `{}`. */
|
|
19
|
+
chatData(chat: {
|
|
20
|
+
ownerId: string;
|
|
21
|
+
chatId: string;
|
|
22
|
+
}): Promise<Record<string, unknown>>;
|
|
18
23
|
}
|
|
19
24
|
/** Перевод чатов на новую версию пачками: версия меняется сразу, состояние — в начале следующего хода чата. */
|
|
20
25
|
export declare function createChatMigrator(deps: ChatMigratorDeps): ChatMigrator;
|
|
@@ -130,11 +130,22 @@ export interface ChatsInState {
|
|
|
130
130
|
stateId: string;
|
|
131
131
|
chats: number;
|
|
132
132
|
}
|
|
133
|
+
/** Чат сценария для отбора при переводе на новую версию. */
|
|
134
|
+
export type FlowChatSummary = Pick<FlowChatRecord, "chatId" | "ownerId" | "flowId" | "versionId" | "stateId" | "endedAt" | "endReason">;
|
|
135
|
+
export interface ListFlowChatsInput {
|
|
136
|
+
flowId: string;
|
|
137
|
+
/** Без чатов, которые уже на этой версии. */
|
|
138
|
+
exceptVersionId?: string;
|
|
139
|
+
/** Только эти чаты. */
|
|
140
|
+
chatIds?: string[];
|
|
141
|
+
}
|
|
133
142
|
export interface MigrateBatchInput {
|
|
134
143
|
flowId: string;
|
|
135
144
|
toVersionId: string;
|
|
136
145
|
/** Куда перевести чаты из удалённых состояний. */
|
|
137
146
|
map: StateMap;
|
|
147
|
+
/** Только эти чаты; нет — любые чаты сценария не на `toVersionId`. */
|
|
148
|
+
chatIds?: string[];
|
|
138
149
|
limit: number;
|
|
139
150
|
at: string;
|
|
140
151
|
}
|
|
@@ -205,6 +216,8 @@ export interface FlowStore {
|
|
|
205
216
|
countChatsByState(input: {
|
|
206
217
|
flowId: string;
|
|
207
218
|
}): Promise<ChatsInState[]>;
|
|
219
|
+
/** Чаты сценария по порядку id: для отбора перед подсчётом и переводом. */
|
|
220
|
+
listFlowChats(input: ListFlowChatsInput): Promise<FlowChatSummary[]>;
|
|
208
221
|
/** Перевести до `limit` чатов на версию `toVersionId`: версия меняется сразу, перевод состояния — ожидающим `migrate`. */
|
|
209
222
|
migrateBatch(input: MigrateBatchInput): Promise<{
|
|
210
223
|
moved: string[];
|
|
@@ -3,8 +3,8 @@ import type { FlowStore } from "./flow-store.js";
|
|
|
3
3
|
/** Хранилище, у которого можно дождаться миграции перед закрытием пула приложения. */
|
|
4
4
|
export type PostgresFlowStore = FlowStore & Required<Pick<FlowStore, "close">>;
|
|
5
5
|
/**
|
|
6
|
-
* Приводит таблицы сценариев к последней миграции.
|
|
7
|
-
*
|
|
6
|
+
* Приводит таблицы сценариев к последней миграции. Схема `agent_chat` создаётся под общей блокировкой всех пакетов, миграции — под своей:
|
|
7
|
+
* копии пакета ждут друг друга, а разные пакеты стартуют разом на пустой базе без гонки за схему.
|
|
8
8
|
*/
|
|
9
9
|
export declare function migrateFlowsToLatest(pool: Pool, migrationsFolder: string): Promise<void>;
|
|
10
10
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ykurilov/agent-chat-flows",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.58.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -56,13 +56,13 @@
|
|
|
56
56
|
"ai": "7.0.122",
|
|
57
57
|
"drizzle-orm": "^0.45.3",
|
|
58
58
|
"zod": "^4.6.5",
|
|
59
|
-
"@ykurilov/agent-chat-core": "0.
|
|
59
|
+
"@ykurilov/agent-chat-core": "0.58.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"better-sqlite3": "^13.0.3",
|
|
63
63
|
"pg": "^8.23.0",
|
|
64
64
|
"react": "^19.3.0",
|
|
65
|
-
"@ykurilov/agent-chat-react": "^0.
|
|
65
|
+
"@ykurilov/agent-chat-react": "^0.58.0"
|
|
66
66
|
},
|
|
67
67
|
"peerDependenciesMeta": {
|
|
68
68
|
"@ykurilov/agent-chat-react": {
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"pg": "^8.23.0",
|
|
91
91
|
"react": "^19.3.0",
|
|
92
92
|
"tsx": "4.23.15",
|
|
93
|
-
"@ykurilov/agent-chat-react": "0.
|
|
93
|
+
"@ykurilov/agent-chat-react": "0.58.0"
|
|
94
94
|
},
|
|
95
95
|
"scripts": {
|
|
96
96
|
"build": "node ../../scripts/build-package.mjs",
|