@chatman-media/conversation-engine 1.3.1 → 1.4.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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=contact-resolver.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contact-resolver.test.d.ts","sourceRoot":"","sources":["../src/contact-resolver.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=conversation-resolver.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-resolver.test.d.ts","sourceRoot":"","sources":["../src/conversation-resolver.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=experiments.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"experiments.integration.test.d.ts","sourceRoot":"","sources":["../../src/dal/experiments.integration.test.ts"],"names":[],"mappings":""}
@@ -4,6 +4,8 @@ export interface LeadRow {
4
4
  tenantId: number;
5
5
  userId: number;
6
6
  state: string;
7
+ /** Тип запроса (concierge): exchange | transfer | … . NULL для одно-воронных вертикалей. */
8
+ requestType: string | null;
7
9
  assignedAdminId?: number | null;
8
10
  intakeJson: string | null;
9
11
  visaDocsJson: string | null;
@@ -20,17 +22,25 @@ export interface LeadRow {
20
22
  }
21
23
  /**
22
24
  * Leads repo. Schema-колонка user_id хранит contact.id (см. ConversationsRepo
23
- * docstring о legacy-имени). UNIQUE(user_id) гарантирует один лид на контакт
24
- * даже при нескольких conversations (bot + userbot) лид всё ещё один.
25
+ * docstring о legacy-имени). UNIQUE(user_id) снят в migration 0032 — concierge
26
+ * держит несколько лидов на контакт (по одному на request_type). Для одно-
27
+ * воронных вертикалей лид по-прежнему один (гарантия на уровне приложения),
28
+ * поэтому find-методы детерминированы (самый свежий по updated_at).
25
29
  */
26
30
  export declare class LeadsRepo {
27
31
  private readonly ctx;
28
32
  constructor(ctx: RepoCtx);
29
33
  byId(id: number): Promise<LeadRow | null>;
34
+ /** Самый свежий лид контакта. Для одно-воронных вертикалей он единственный. */
30
35
  findByContactId(contactId: number): Promise<LeadRow | null>;
36
+ /** Все лиды контакта (concierge: N запросов на гостя), свежие первыми. */
37
+ findAllByContact(contactId: number): Promise<LeadRow[]>;
38
+ /** Самый свежий лид контакта заданного request_type (concierge). */
39
+ findByContactAndType(contactId: number, requestType: string): Promise<LeadRow | null>;
31
40
  create(opts: {
32
41
  contactId: number;
33
42
  state: string;
43
+ requestType?: string | null;
34
44
  nowEpoch: number;
35
45
  }): Promise<LeadRow>;
36
46
  updateState(leadId: number, newState: string, nowEpoch: number): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"leads.d.ts","sourceRoot":"","sources":["../../src/dal/leads.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,qBAAa,SAAS;IACR,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,OAAO;IAEnC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAQzC,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAU3D,MAAM,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAetF,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMrF"}
1
+ {"version":3,"file":"leads.d.ts","sourceRoot":"","sources":["../../src/dal/leads.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,4FAA4F;IAC5F,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;GAMG;AACH,qBAAa,SAAS;IACR,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,OAAO;IAEnC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAQ/C,+EAA+E;IACzE,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAYjE,0EAA0E;IACpE,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAW7D,oEAAoE;IAC9D,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAgBpB,MAAM,CAAC,IAAI,EAAE;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC5B,QAAQ,EAAE,MAAM,CAAC;KAClB,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBd,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAMrF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=notifications.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.integration.test.d.ts","sourceRoot":"","sources":["../../src/dal/notifications.integration.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=outbound.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound.integration.test.d.ts","sourceRoot":"","sources":["../../src/dal/outbound.integration.test.ts"],"names":[],"mappings":""}
package/dist/index.js CHANGED
@@ -429,7 +429,7 @@ function parseAllocation(allocationJson) {
429
429
  }
430
430
  // src/dal/leads.ts
431
431
  import { leads as leadsTable } from "@chatman-media/storage";
432
- import { and as and5, eq as eq5 } from "drizzle-orm";
432
+ import { and as and5, desc as desc2, eq as eq5 } from "drizzle-orm";
433
433
 
434
434
  class LeadsRepo {
435
435
  ctx;
@@ -441,7 +441,15 @@ class LeadsRepo {
441
441
  return row ?? null;
442
442
  }
443
443
  async findByContactId(contactId) {
444
- const [row] = await this.ctx.db.select().from(leadsTable).where(and5(eq5(leadsTable.tenantId, this.ctx.tenantId), eq5(leadsTable.userId, contactId)));
444
+ const [row] = await this.ctx.db.select().from(leadsTable).where(and5(eq5(leadsTable.tenantId, this.ctx.tenantId), eq5(leadsTable.userId, contactId))).orderBy(desc2(leadsTable.updatedAt)).limit(1);
445
+ return row ?? null;
446
+ }
447
+ async findAllByContact(contactId) {
448
+ const rows = await this.ctx.db.select().from(leadsTable).where(and5(eq5(leadsTable.tenantId, this.ctx.tenantId), eq5(leadsTable.userId, contactId))).orderBy(desc2(leadsTable.updatedAt));
449
+ return rows;
450
+ }
451
+ async findByContactAndType(contactId, requestType) {
452
+ const [row] = await this.ctx.db.select().from(leadsTable).where(and5(eq5(leadsTable.tenantId, this.ctx.tenantId), eq5(leadsTable.userId, contactId), eq5(leadsTable.requestType, requestType))).orderBy(desc2(leadsTable.updatedAt)).limit(1);
445
453
  return row ?? null;
446
454
  }
447
455
  async create(opts) {
@@ -449,6 +457,7 @@ class LeadsRepo {
449
457
  tenantId: this.ctx.tenantId,
450
458
  userId: opts.contactId,
451
459
  state: opts.state,
460
+ requestType: opts.requestType ?? null,
452
461
  createdAt: opts.nowEpoch,
453
462
  updatedAt: opts.nowEpoch
454
463
  }).returning();
@@ -462,7 +471,7 @@ class LeadsRepo {
462
471
  }
463
472
  // src/dal/messages.ts
464
473
  import { messages as messagesTable } from "@chatman-media/storage";
465
- import { and as and6, desc as desc2, eq as eq6, isNull, sql as sql3 } from "drizzle-orm";
474
+ import { and as and6, desc as desc3, eq as eq6, isNull, sql as sql3 } from "drizzle-orm";
466
475
 
467
476
  class MessagesRepo {
468
477
  ctx;
@@ -493,7 +502,7 @@ class MessagesRepo {
493
502
  return row ?? null;
494
503
  }
495
504
  async recent(conversationId, limit) {
496
- const rows = await this.ctx.db.select().from(messagesTable).where(and6(eq6(messagesTable.tenantId, this.ctx.tenantId), eq6(messagesTable.conversationId, conversationId), isNull(messagesTable.deletedAt), sql3`role <> 'system'`)).orderBy(desc2(messagesTable.createdAt)).limit(limit);
505
+ const rows = await this.ctx.db.select().from(messagesTable).where(and6(eq6(messagesTable.tenantId, this.ctx.tenantId), eq6(messagesTable.conversationId, conversationId), isNull(messagesTable.deletedAt), sql3`role <> 'system'`)).orderBy(desc3(messagesTable.createdAt)).limit(limit);
497
506
  return rows.reverse();
498
507
  }
499
508
  async countByConversation(conversationId) {
@@ -693,7 +702,7 @@ class StylesRepo {
693
702
  }
694
703
  }
695
704
  // src/dal/notifications.ts
696
- import { and as and10, desc as desc3, eq as eq10, gte, inArray, isNull as isNull2, sql as sql7 } from "drizzle-orm";
705
+ import { and as and10, desc as desc4, eq as eq10, gte, inArray, isNull as isNull2, sql as sql7 } from "drizzle-orm";
697
706
  import { admins, adminNotifications, notificationRules, operatorSettings, notificationTemplates, notificationGroupTokens } from "@chatman-media/storage";
698
707
 
699
708
  class NotificationsRepo {
@@ -846,11 +855,11 @@ class NotificationsRepo {
846
855
  await this.db.update(adminNotifications).set({ deliveredAt }).where(eq10(adminNotifications.id, id));
847
856
  }
848
857
  async findRecentByDedup(tenantId, dedupKey, sinceEpoch) {
849
- const rows = await this.db.select().from(adminNotifications).where(and10(eq10(adminNotifications.tenantId, tenantId), eq10(adminNotifications.dedupKey, dedupKey), gte(adminNotifications.createdAt, sinceEpoch))).orderBy(desc3(adminNotifications.createdAt)).limit(1);
858
+ const rows = await this.db.select().from(adminNotifications).where(and10(eq10(adminNotifications.tenantId, tenantId), eq10(adminNotifications.dedupKey, dedupKey), gte(adminNotifications.createdAt, sinceEpoch))).orderBy(desc4(adminNotifications.createdAt)).limit(1);
850
859
  return rows[0];
851
860
  }
852
861
  async listRecentNotifications(tenantId, adminId, limit = 10) {
853
- return this.db.select().from(adminNotifications).where(and10(eq10(adminNotifications.tenantId, tenantId), eq10(adminNotifications.adminId, adminId))).orderBy(desc3(adminNotifications.createdAt)).limit(limit);
862
+ return this.db.select().from(adminNotifications).where(and10(eq10(adminNotifications.tenantId, tenantId), eq10(adminNotifications.adminId, adminId))).orderBy(desc4(adminNotifications.createdAt)).limit(limit);
854
863
  }
855
864
  async listPendingDigest(tenantId, adminId) {
856
865
  return this.db.select().from(adminNotifications).where(and10(eq10(adminNotifications.tenantId, tenantId), eq10(adminNotifications.adminId, adminId), isNull2(adminNotifications.deliveredAt), isNull2(adminNotifications.digestBatchId))).orderBy(adminNotifications.createdAt);
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=outbound-dispatch.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbound-dispatch.test.d.ts","sourceRoot":"","sources":["../src/outbound-dispatch.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=stage-classifier.integration.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stage-classifier.integration.test.d.ts","sourceRoot":"","sources":["../src/stage-classifier.integration.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatman-media/conversation-engine",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Channel-agnostic pipeline обработки inbound сообщений: contact-resolve → conversation lookup → mode routing → AI-reply / queued / human → outbound. Сердце data plane.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",