@hexabot-ai/api 3.2.2-alpha.2 → 3.2.2-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +1 -1
  2. package/dist/cms/services/content-type.service.d.ts +1 -1
  3. package/dist/cms/services/content-type.service.js.map +1 -1
  4. package/dist/cms/services/rag.service.d.ts +1 -1
  5. package/dist/database/decorators/json-column.decorator.js +1 -0
  6. package/dist/database/decorators/json-column.decorator.js.map +1 -1
  7. package/dist/database/typeorm-config.service.js +1 -1
  8. package/dist/database/typeorm-config.service.js.map +1 -1
  9. package/dist/extensions/actions/ai/mcp.binding.js +1 -1
  10. package/dist/extensions/actions/ai/mcp.binding.js.map +1 -1
  11. package/dist/extensions/channels/web/base-web-channel.d.ts +3 -1
  12. package/dist/extensions/channels/web/base-web-channel.js +35 -15
  13. package/dist/extensions/channels/web/base-web-channel.js.map +1 -1
  14. package/dist/extensions/channels/web/outbound/web-outbound-message-encoder.d.ts +2 -2
  15. package/dist/extensions/channels/web/outbound/web-outbound-message-encoder.js +4 -4
  16. package/dist/extensions/channels/web/outbound/web-outbound-message-encoder.js.map +1 -1
  17. package/dist/extensions/channels/web/services/web-session.service.d.ts +6 -0
  18. package/dist/extensions/channels/web/services/web-session.service.js +15 -0
  19. package/dist/extensions/channels/web/services/web-session.service.js.map +1 -1
  20. package/dist/static/assets/{browser-ponyfill-BHrZk0Ba.js → browser-ponyfill-BydLWDRe.js} +1 -1
  21. package/dist/static/assets/{cssMode-BMhPv6LQ.js → cssMode-DtNyPUki.js} +1 -1
  22. package/dist/static/assets/{freemarker2-BeZMhdTv.js → freemarker2-C2XfZce5.js} +1 -1
  23. package/dist/static/assets/{handlebars-BKG3g2Ua.js → handlebars-C8bj58KH.js} +1 -1
  24. package/dist/static/assets/{html-DGzTITbD.js → html-A6xaHkJC.js} +1 -1
  25. package/dist/static/assets/{htmlMode-BqkaqtYx.js → htmlMode-BaS0W7vp.js} +1 -1
  26. package/dist/static/assets/{index-BZ6XUYgl.js → index-BoAD0ucA.js} +2058 -2058
  27. package/dist/static/assets/{javascript-b5wI9R64.js → javascript-B0ziESIH.js} +1 -1
  28. package/dist/static/assets/{jsonMode-CHkAHll-.js → jsonMode-Ce5isNkh.js} +1 -1
  29. package/dist/static/assets/{liquid-DWtcf_ol.js → liquid-DgK1yfp4.js} +1 -1
  30. package/dist/static/assets/{lspLanguageFeatures-CogY5n0Q.js → lspLanguageFeatures-Dm_9PmPd.js} +1 -1
  31. package/dist/static/assets/{mdx-Bahv9Yam.js → mdx-DoyiAUI-.js} +1 -1
  32. package/dist/static/assets/{python-BMSmNQlB.js → python-D3i33SIz.js} +1 -1
  33. package/dist/static/assets/{razor-gQN4hgMu.js → razor-CmE-9YdK.js} +1 -1
  34. package/dist/static/assets/{tsMode-lBRxNuK8.js → tsMode-Ct_NvUwl.js} +1 -1
  35. package/dist/static/assets/{typescript-BIY26e43.js → typescript-ynHJup65.js} +1 -1
  36. package/dist/static/assets/{xml-CU3COlzI.js → xml-BymLyoKX.js} +1 -1
  37. package/dist/static/assets/{yaml-Bu2tB__J.js → yaml-Cn2RaxHO.js} +1 -1
  38. package/dist/static/index.html +1 -1
  39. package/dist/tsconfig.build.tsbuildinfo +1 -1
  40. package/dist/utils/test/fixtures/workflow.d.ts +1 -1
  41. package/dist/utils/test/providers/i18n-service.provider.d.ts +1 -1
  42. package/dist/utils/test/providers/i18n-service.provider.js +2 -2
  43. package/dist/utils/test/providers/i18n-service.provider.js.map +1 -1
  44. package/dist/utils/test/providers/mailer-service.provider.d.ts +3 -3
  45. package/dist/utils/test/providers/mailer-service.provider.js +2 -2
  46. package/dist/utils/test/providers/mailer-service.provider.js.map +1 -1
  47. package/dist/utils/test/providers/setting-service.provider.d.ts +1 -1
  48. package/dist/utils/test/providers/setting-service.provider.js +2 -2
  49. package/dist/utils/test/providers/setting-service.provider.js.map +1 -1
  50. package/dist/utils/test/utils.js +23 -6
  51. package/dist/utils/test/utils.js.map +1 -1
  52. package/dist/workflow/contexts/workflow-runtime.context.d.ts +2 -2
  53. package/dist/workflow/contexts/workflow-runtime.context.js +6 -6
  54. package/dist/workflow/contexts/workflow-runtime.context.js.map +1 -1
  55. package/dist/workflow/controllers/workflow.controller.d.ts +1 -1
  56. package/dist/workflow/controllers/workflow.controller.js +9 -9
  57. package/dist/workflow/controllers/workflow.controller.js.map +1 -1
  58. package/dist/workflow/entities/mcp-server.entity.d.ts +1 -1
  59. package/dist/workflow/entities/mcp-server.entity.js +4 -4
  60. package/dist/workflow/entities/mcp-server.entity.js.map +1 -1
  61. package/dist/workflow/entities/workflow.entity.js +2 -1
  62. package/dist/workflow/entities/workflow.entity.js.map +1 -1
  63. package/dist/workflow/services/workflow-scheduler.service.d.ts +1 -1
  64. package/dist/workflow/services/workflow-scheduler.service.js +2 -2
  65. package/dist/workflow/services/workflow-scheduler.service.js.map +1 -1
  66. package/dist/workflow/workflow.module.js +4 -4
  67. package/dist/workflow/workflow.module.js.map +1 -1
  68. package/package.json +5 -5
  69. package/src/cms/services/content-type.service.ts +1 -1
  70. package/src/cms/services/rag.service.ts +1 -1
  71. package/src/database/decorators/json-column.decorator.ts +1 -0
  72. package/src/database/typeorm-config.service.ts +1 -1
  73. package/src/extensions/actions/ai/mcp.binding.ts +1 -1
  74. package/src/extensions/channels/web/base-web-channel.ts +48 -11
  75. package/src/extensions/channels/web/outbound/web-outbound-message-encoder.ts +2 -2
  76. package/src/extensions/channels/web/services/web-session.service.ts +35 -0
  77. package/src/utils/test/fixtures/workflow.ts +1 -1
  78. package/src/utils/test/providers/i18n-service.provider.ts +1 -1
  79. package/src/utils/test/providers/mailer-service.provider.ts +1 -1
  80. package/src/utils/test/providers/setting-service.provider.ts +1 -1
  81. package/src/utils/test/utils.ts +55 -6
  82. package/src/workflow/contexts/workflow-runtime.context.ts +2 -2
  83. package/src/workflow/controllers/workflow.controller.ts +2 -2
  84. package/src/workflow/entities/mcp-server.entity.ts +2 -2
  85. package/src/workflow/entities/workflow.entity.ts +2 -1
  86. package/src/workflow/services/workflow-scheduler.service.ts +1 -1
  87. package/src/workflow/workflow.module.ts +2 -2
@@ -12,8 +12,8 @@ const schedule_1 = require("@nestjs/schedule");
12
12
  const typeorm_1 = require("@nestjs/typeorm");
13
13
  const chat_module_1 = require("../chat/chat.module");
14
14
  const thread_entity_1 = require("../chat/entities/thread.entity");
15
- const cms_1 = require("../cms");
16
- const user_1 = require("../user");
15
+ const cms_module_1 = require("../cms/cms.module");
16
+ const user_module_1 = require("../user/user.module");
17
17
  const conversational_workflow_context_1 = require("./contexts/conversational-workflow.context");
18
18
  const manual_workflow_context_1 = require("./contexts/manual-workflow.context");
19
19
  const scheduled_workflow_context_1 = require("./contexts/scheduled-workflow.context");
@@ -62,9 +62,9 @@ exports.WorkflowModule = WorkflowModule = __decorate([
62
62
  mcp_server_entity_1.McpServerOrmEntity,
63
63
  thread_entity_1.ThreadOrmEntity,
64
64
  ]),
65
- (0, common_1.forwardRef)(() => cms_1.CmsModule),
65
+ (0, common_1.forwardRef)(() => cms_module_1.CmsModule),
66
66
  (0, common_1.forwardRef)(() => chat_module_1.ChatModule),
67
- user_1.UserModule,
67
+ user_module_1.UserModule,
68
68
  ],
69
69
  controllers: [
70
70
  workflow_controller_1.WorkflowController,
@@ -1 +1 @@
1
- {"version":3,"file":"workflow.module.js","sourceRoot":"","sources":["../../src/workflow/workflow.module.ts"],"names":[],"mappings":";;;;;;;;;AAMA,2CAAoD;AACpD,+CAAqD;AACrD,6CAAgD;AAEhD,qDAAgD;AAChD,kEAAgE;AAChE,gCAAkC;AAClC,kCAAoC;AAEpC,gGAA2F;AAC3F,gFAA2E;AAC3E,sFAAiF;AACjF,kFAA6E;AAC7E,+EAA0E;AAC1E,6FAAwF;AACxF,mFAA8E;AAC9E,2FAAsF;AACtF,2EAAuE;AACvE,oEAAkE;AAClE,kFAAgF;AAChF,0EAAwE;AACxE,wEAAsE;AACtE,gFAA8E;AAC9E,gEAA+D;AAC/D,gFAA2E;AAC3E,8FAAyF;AACzF,sFAAiF;AACjF,oFAA+E;AAC/E,4FAAuF;AACvF,4EAAwE;AACxE,2EAAwE;AACxE,yDAAuD;AACvD,gEAA4D;AAC5D,gFAA0E;AAC1E,sEAAiE;AACjE,oFAA+E;AAC/E,4EAAuE;AACvE,8DAA0D;AAC1D,0EAAqE;AACrE,sFAAiF;AACjF,kFAA6E;AAC7E,kEAA8D;AAoEvD,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,wCAAc;yBAAd,cAAc;IAlE1B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC;gBACvB,mCAAiB;gBACjB,kDAAwB;gBACxB,0CAAoB;gBACpB,oDAAyB;gBACzB,4CAAqB;gBACrB,sCAAkB;gBAClB,+BAAe;aAChB,CAAC;YACF,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,eAAS,CAAC;YAC3B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,wBAAU,CAAC;YAC5B,iBAAU;SACX;QACD,WAAW,EAAE;YACX,wCAAkB;YAClB,uDAAyB;YACzB,+CAAqB;YACrB,yDAA0B;YAC1B,2CAAmB;SACpB;QACD,SAAS,EAAE;YACT,wCAAkB;YAClB,uDAAyB;YACzB,+CAAqB;YACrB,yDAA0B;YAC1B,iDAAsB;YACtB,2CAAmB;YACnB,+CAAsB;YACtB,8BAAc;YACd,kCAAe;YACf,iDAAsB;YACtB,yCAAkB;YAClB,mDAAuB;YACvB,2CAAmB;YACnB,8BAAa;YACb,qCAAgB;YAChB,8CAAoB;YACpB,4BAAiB;YACjB,qDAAwB;YACxB,+DAA6B;YAC7B,+CAAqB;YACrB,qDAAwB;YACxB,iDAAsB;YACtB,gCAAc;SACf;QACD,OAAO,EAAE;YACP,wCAAkB;YAClB,uDAAyB;YACzB,+CAAqB;YACrB,yDAA0B;YAC1B,iDAAsB;YACtB,2CAAmB;YACnB,kCAAe;YACf,iDAAsB;YACtB,yCAAkB;YAClB,mDAAuB;YACvB,2CAAmB;YACnB,8BAAa;YACb,qCAAgB;YAChB,8CAAoB;YACpB,+DAA6B;YAC7B,gCAAc;SACf;KACF,CAAC;GACW,cAAc,CAAG"}
1
+ {"version":3,"file":"workflow.module.js","sourceRoot":"","sources":["../../src/workflow/workflow.module.ts"],"names":[],"mappings":";;;;;;;;;AAMA,2CAAoD;AACpD,+CAAqD;AACrD,6CAAgD;AAEhD,qDAAgD;AAChD,kEAAgE;AAChE,kDAA6C;AAC7C,qDAAgD;AAEhD,gGAA2F;AAC3F,gFAA2E;AAC3E,sFAAiF;AACjF,kFAA6E;AAC7E,+EAA0E;AAC1E,6FAAwF;AACxF,mFAA8E;AAC9E,2FAAsF;AACtF,2EAAuE;AACvE,oEAAkE;AAClE,kFAAgF;AAChF,0EAAwE;AACxE,wEAAsE;AACtE,gFAA8E;AAC9E,gEAA+D;AAC/D,gFAA2E;AAC3E,8FAAyF;AACzF,sFAAiF;AACjF,oFAA+E;AAC/E,4FAAuF;AACvF,4EAAwE;AACxE,2EAAwE;AACxE,yDAAuD;AACvD,gEAA4D;AAC5D,gFAA0E;AAC1E,sEAAiE;AACjE,oFAA+E;AAC/E,4EAAuE;AACvE,8DAA0D;AAC1D,0EAAqE;AACrE,sFAAiF;AACjF,kFAA6E;AAC7E,kEAA8D;AAoEvD,IAAM,cAAc,GAApB,MAAM,cAAc;CAAG,CAAA;AAAjB,wCAAc;yBAAd,cAAc;IAlE1B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC;gBACvB,mCAAiB;gBACjB,kDAAwB;gBACxB,0CAAoB;gBACpB,oDAAyB;gBACzB,4CAAqB;gBACrB,sCAAkB;gBAClB,+BAAe;aAChB,CAAC;YACF,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,sBAAS,CAAC;YAC3B,IAAA,mBAAU,EAAC,GAAG,EAAE,CAAC,wBAAU,CAAC;YAC5B,wBAAU;SACX;QACD,WAAW,EAAE;YACX,wCAAkB;YAClB,uDAAyB;YACzB,+CAAqB;YACrB,yDAA0B;YAC1B,2CAAmB;SACpB;QACD,SAAS,EAAE;YACT,wCAAkB;YAClB,uDAAyB;YACzB,+CAAqB;YACrB,yDAA0B;YAC1B,iDAAsB;YACtB,2CAAmB;YACnB,+CAAsB;YACtB,8BAAc;YACd,kCAAe;YACf,iDAAsB;YACtB,yCAAkB;YAClB,mDAAuB;YACvB,2CAAmB;YACnB,8BAAa;YACb,qCAAgB;YAChB,8CAAoB;YACpB,4BAAiB;YACjB,qDAAwB;YACxB,+DAA6B;YAC7B,+CAAqB;YACrB,qDAAwB;YACxB,iDAAsB;YACtB,gCAAc;SACf;QACD,OAAO,EAAE;YACP,wCAAkB;YAClB,uDAAyB;YACzB,+CAAqB;YACrB,yDAA0B;YAC1B,iDAAsB;YACtB,2CAAmB;YACnB,kCAAe;YACf,iDAAsB;YACtB,yCAAkB;YAClB,mDAAuB;YACvB,2CAAmB;YACnB,8BAAa;YACb,qCAAgB;YAChB,8CAAoB;YACpB,+DAA6B;YAC7B,gCAAc;SACf;KACF,CAAC;GACW,cAAc,CAAG"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hexabot-ai/api",
3
3
  "private": false,
4
- "version": "3.2.2-alpha.2",
4
+ "version": "3.2.2-alpha.4",
5
5
  "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.",
6
6
  "author": "Hexastack",
7
7
  "license": "FCL-1.0-ALv2",
@@ -51,6 +51,7 @@
51
51
  "ai": "^6.0.17",
52
52
  "async-mutex": "^0.5.0",
53
53
  "axios": "^1.15.2",
54
+ "better-sqlite3": "^12.9.0",
54
55
  "bcryptjs": "^3.0.2",
55
56
  "cache-manager": "^7.2.4",
56
57
  "cacheable": "^2.1.0",
@@ -86,12 +87,11 @@
86
87
  "rxjs": "^7.8.2",
87
88
  "sanitize-filename": "^1.6.3",
88
89
  "slug": "^11.0.0",
89
- "sqlite3": "^6.0.1",
90
90
  "typeorm": "^0.3.28",
91
91
  "yaml": "^2.8.3",
92
92
  "zod": "^4.3.6",
93
- "@hexabot-ai/agentic": "3.1.2-alpha.2",
94
- "@hexabot-ai/types": "3.0.2-alpha.2"
93
+ "@hexabot-ai/types": "3.0.2-alpha.4",
94
+ "@hexabot-ai/agentic": "3.1.2-alpha.4"
95
95
  },
96
96
  "devDependencies": {
97
97
  "@compodoc/compodoc": "^1.1.31",
@@ -142,7 +142,7 @@
142
142
  "tsconfig-paths": "^4.2.0",
143
143
  "tsconfig-paths-jest": "^0.0.1",
144
144
  "typescript": "^5.1.3",
145
- "@hexabot-ai/frontend": "3.2.2-alpha.2"
145
+ "@hexabot-ai/frontend": "3.2.2-alpha.4"
146
146
  },
147
147
  "optionalDependencies": {
148
148
  "@css-inline/css-inline-linux-arm64-musl": "^0.14.1",
@@ -8,8 +8,8 @@ import { ContentType } from '@hexabot-ai/types';
8
8
  import { Injectable } from '@nestjs/common';
9
9
  import { JSONSchema7 } from 'json-schema';
10
10
 
11
- import { InferCreateDto } from '@/utils';
12
11
  import { BaseOrmService } from '@/utils/generics/base-orm.service';
12
+ import type { InferCreateDto } from '@/utils/types/dto.types';
13
13
 
14
14
  import { ContentTypeOrmEntity } from '../entities/content-type.entity';
15
15
  import { ContentTypeRepository } from '../repositories/content-type.repository';
@@ -9,7 +9,7 @@ import { OnEvent } from '@nestjs/event-emitter';
9
9
 
10
10
  import { LoggerService } from '@/logger/logger.service';
11
11
  import { SettingService } from '@/setting/services/setting.service';
12
- import { UserOrmEntity } from '@/user';
12
+ import type { UserOrmEntity } from '@/user/entities/user.entity';
13
13
  import {
14
14
  DeleteEntityEvent,
15
15
  InsertEntityEvent,
@@ -12,6 +12,7 @@ type JsonColumnOptions = Omit<ColumnOptions, 'type'>;
12
12
 
13
13
  const dbTypeToJsonColumn: Record<string, ColumnType> = {
14
14
  sqlite: 'simple-json',
15
+ 'better-sqlite3': 'simple-json',
15
16
  postgres: 'json',
16
17
  mysql: 'json',
17
18
  mariadb: 'json',
@@ -51,7 +51,7 @@ export class TypeormConfigService implements TypeOrmOptionsFactory {
51
51
  // @ts-expect-error type mismatch)
52
52
  return {
53
53
  ...base,
54
- type: 'sqlite',
54
+ type: 'better-sqlite3',
55
55
  database: db.sqlitePath ?? 'hexabot.sqlite',
56
56
  };
57
57
  }
@@ -30,7 +30,7 @@ export const aiMcpToolBindingSchema = z.strictObject({
30
30
  'Optional allow-list of MCP tool names. Leave empty to expose all server tools.',
31
31
  'ui:widget': 'AutoCompleteWidget',
32
32
  'ui:options': {
33
- entity: 'McpServerTool',
33
+ entity: 'McpServer',
34
34
  valueKey: 'name',
35
35
  labelKey: 'name',
36
36
  idFormPath: 'server_id',
@@ -38,16 +38,16 @@ import {
38
38
  import {
39
39
  ChannelCapabilities,
40
40
  DEFAULT_CHANNEL_CAPABILITIES,
41
- ExtensionInject,
42
- WebSocketChannelHandler,
43
- } from '@/channel';
41
+ } from '@/channel/lib/channel-capabilities';
42
+ import { ExtensionInject } from '@/channel/lib/extension-inject.decorator';
44
43
  import { MessageInboundEvent } from '@/channel/lib/inbound-events';
44
+ import { WebSocketChannelHandler } from '@/channel/lib/transports/websocket-channel-handler';
45
45
  import { SourceService } from '@/channel/services/source.service';
46
46
  import { ChannelName } from '@/channel/types';
47
- import { SubscriberChannelData } from '@/chat';
48
47
  import { MessageCreateDto } from '@/chat/dto/message.dto';
49
48
  import { SubscriberCreateDto } from '@/chat/dto/subscriber.dto';
50
49
  import { MessageService } from '@/chat/services/message.service';
50
+ import type { SubscriberChannelData } from '@/chat/types/channel';
51
51
  import { MenuService } from '@/cms/services/menu.service';
52
52
  import { config } from '@/config';
53
53
  import { SocketRequest } from '@/websocket/utils/socket-request';
@@ -74,6 +74,10 @@ import { WebSessionService } from './services/web-session.service';
74
74
  import { WEB_CHANNEL_NAME } from './settings.schema';
75
75
  import { Web } from './types';
76
76
 
77
+ type WebSocketData = Socket['data'] & {
78
+ webMessageQueue?: Promise<void>;
79
+ };
80
+
77
81
  /**
78
82
  * Base handler for the Socket.IO-backed "web" channel.
79
83
  *
@@ -161,6 +165,30 @@ export default abstract class BaseWebChannelHandler<N extends ChannelName>
161
165
  return normalizedSourceId.length > 0 ? normalizedSourceId : null;
162
166
  }
163
167
 
168
+ private enqueueMessageDispatch(
169
+ req: SocketRequest,
170
+ event: MessageInboundEvent,
171
+ ): void {
172
+ const socket = req.socket as Socket & { data?: WebSocketData };
173
+ const socketData = (socket.data ??= {});
174
+ // Keep chatbot processing ordered per socket without making the client ack
175
+ // wait for slow actions, LLM calls, or external integrations.
176
+ const previous = socketData.webMessageQueue ?? Promise.resolve();
177
+ const next = previous
178
+ .catch(() => undefined)
179
+ .then(() => this.channelEventBus.emitMessage(event))
180
+ .catch((err) => {
181
+ this.logger.error('Failed to process web socket message', err);
182
+ });
183
+
184
+ socketData.webMessageQueue = next;
185
+ void next.finally(() => {
186
+ if (socketData.webMessageQueue === next) {
187
+ delete socketData.webMessageQueue;
188
+ }
189
+ });
190
+ }
191
+
164
192
  @OnEvent('hook:websocket:connection', { async: true })
165
193
  async onWebSocketConnection(client: Socket) {
166
194
  try {
@@ -547,15 +575,24 @@ export default abstract class BaseWebChannelHandler<N extends ChannelName>
547
575
  messageEvent.setAuthorForeignId(profile.foreignId);
548
576
  }
549
577
  messageEvent.setCreatedAt(new Date());
578
+ // Resolve the thread before acknowledging the socket request so the
579
+ // client receives a stable thread_id, then dispatch chatbot work later.
580
+ const thread = await this.sessionService.resolveThreadForIncoming(
581
+ req,
582
+ profile.id,
583
+ {
584
+ explicitThreadId: messageEvent.getThreadId(),
585
+ inactivityHours: this.sessionService.resolveInactivityHours(
586
+ source.settings,
587
+ ),
588
+ sourceId: source.id,
589
+ },
590
+ );
591
+ messageEvent.setThreadId(thread.id);
592
+ messageEvent.setThreadIdOnRaw(thread.id);
550
593
 
551
594
  this.broadcast(profile, StdEventType.message, messageEvent.getRaw());
552
- await this.channelEventBus.emitMessage(messageEvent);
553
-
554
- const resolvedThreadId = messageEvent.getThreadId();
555
- if (resolvedThreadId) {
556
- messageEvent.setThreadIdOnRaw(resolvedThreadId);
557
- if (req.session.web) req.session.web.threadId = resolvedThreadId;
558
- }
595
+ this.enqueueMessageDispatch(req, messageEvent);
559
596
 
560
597
  continue;
561
598
  }
@@ -24,8 +24,8 @@ import { ChannelOutboundMessageEncoder } from '@/channel/lib/outbound';
24
24
  import { ChannelAttachmentService } from '@/channel/services/channel-attachment.service';
25
25
  import { VIEW_MORE_PAYLOAD } from '@/chat/helpers/constants';
26
26
  import { ContentOrmEntity } from '@/cms/entities/content.entity';
27
- import { I18nService } from '@/i18n';
28
- import { LoggerService } from '@/logger';
27
+ import { I18nService } from '@/i18n/services/i18n.service';
28
+ import { LoggerService } from '@/logger/logger.service';
29
29
 
30
30
  import { Web } from '../types';
31
31
 
@@ -215,6 +215,10 @@ export class WebSessionService {
215
215
  return this.threadService.resolveThread({ subscriberId, explicitThreadId });
216
216
  }
217
217
 
218
+ resolveInactivityHours(settings: unknown): number {
219
+ return this.threadService.resolveInactivityHours(settings);
220
+ }
221
+
218
222
  /**
219
223
  * Resolves a thread from the request query/body and writes the result back
220
224
  * to the session. Used by the subscribe/history flows.
@@ -232,4 +236,35 @@ export class WebSessionService {
232
236
 
233
237
  return thread;
234
238
  }
239
+
240
+ /**
241
+ * Resolves the writable thread for an incoming message and persists it on the
242
+ * socket session before chatbot processing continues asynchronously.
243
+ */
244
+ async resolveThreadForIncoming(
245
+ req: SocketRequest,
246
+ subscriberId: string,
247
+ {
248
+ explicitThreadId,
249
+ inactivityHours,
250
+ sourceId,
251
+ }: {
252
+ explicitThreadId?: string;
253
+ inactivityHours?: number;
254
+ sourceId?: string;
255
+ },
256
+ ): Promise<Thread> {
257
+ const thread = await this.threadService.resolveThreadForIncoming({
258
+ subscriberId,
259
+ explicitThreadId,
260
+ inactivityHours,
261
+ sourceId,
262
+ });
263
+
264
+ if (req.session.web) {
265
+ req.session.web.threadId = thread.id;
266
+ }
267
+
268
+ return thread;
269
+ }
235
270
  }
@@ -11,7 +11,7 @@ import {
11
11
  installUserFixturesTypeOrm,
12
12
  userFixtureIds,
13
13
  } from '@/utils/test/fixtures/user';
14
- import { WorkflowCreateDto } from '@/workflow';
14
+ import { WorkflowCreateDto } from '@/workflow/dto/workflow.dto';
15
15
  import { WorkflowVersionOrmEntity } from '@/workflow/entities/workflow-version.entity';
16
16
  import { WorkflowOrmEntity } from '@/workflow/entities/workflow.entity';
17
17
  import {
@@ -4,7 +4,7 @@
4
4
  * Full terms: see LICENSE.md.
5
5
  */
6
6
 
7
- import { I18nService } from '@/i18n';
7
+ import { I18nService } from '@/i18n/services/i18n.service';
8
8
 
9
9
  export const I18nServiceProvider = {
10
10
  provide: I18nService,
@@ -4,7 +4,7 @@
4
4
  * Full terms: see LICENSE.md.
5
5
  */
6
6
 
7
- import { MailerService } from '@/mailer';
7
+ import { MailerService } from '@/mailer/mailer.service';
8
8
 
9
9
  export const mailerMock = {
10
10
  sendMail: jest.fn((_options) =>
@@ -4,7 +4,7 @@
4
4
  * Full terms: see LICENSE.md.
5
5
  */
6
6
 
7
- import { SettingService } from '@/setting';
7
+ import { SettingService } from '@/setting/services/setting.service';
8
8
 
9
9
  export const SettingServiceProvider = {
10
10
  provide: SettingService,
@@ -14,6 +14,7 @@ import {
14
14
  DataSource,
15
15
  DataSourceOptions,
16
16
  EntitySchema,
17
+ EntitySubscriberInterface,
17
18
  EntityTarget,
18
19
  getMetadataArgsStorage,
19
20
  } from 'typeorm';
@@ -258,15 +259,38 @@ const addEntityToLookup = (
258
259
  const refreshEntityLookup = (lookup: Map<string, EntityTarget<any>>): void => {
259
260
  buildEntityLookup().forEach((entity, key) => lookup.set(key, entity));
260
261
  };
261
- const registeredSubscribers = new Set<unknown>();
262
+ const GENERATED_ORM_HOOK_SUBSCRIBER = Symbol('generatedOrmHookSubscriber');
263
+ type SubscriberWithListenTo = EntitySubscriberInterface & {
264
+ listenTo: NonNullable<EntitySubscriberInterface['listenTo']>;
265
+ };
266
+ type GeneratedOrmHookSubscriber = SubscriberWithListenTo & {
267
+ [GENERATED_ORM_HOOK_SUBSCRIBER]: true;
268
+ };
269
+ const isGeneratedOrmHookSubscriber = (
270
+ subscriber: unknown,
271
+ ): subscriber is GeneratedOrmHookSubscriber =>
272
+ Boolean(
273
+ subscriber &&
274
+ typeof subscriber === 'object' &&
275
+ (subscriber as Record<PropertyKey, unknown>)[
276
+ GENERATED_ORM_HOOK_SUBSCRIBER
277
+ ],
278
+ );
279
+ const hasListenTo = (
280
+ subscriber: unknown,
281
+ ): subscriber is SubscriberWithListenTo =>
282
+ Boolean(
283
+ subscriber &&
284
+ typeof subscriber === 'object' &&
285
+ typeof (subscriber as { listenTo?: unknown }).listenTo === 'function',
286
+ );
262
287
  const registerAllRepositorySubscribers = (
263
288
  dataSource: DataSource,
264
289
  entityClasses: (new (...args: any[]) => BaseOrmEntity<any>)[],
265
290
  ): void => {
266
291
  for (const EntityClass of entityClasses) {
267
- if (registeredSubscribers.has(EntityClass)) continue;
268
-
269
292
  const subscriber = {
293
+ [GENERATED_ORM_HOOK_SUBSCRIBER]: true,
270
294
  listenTo: () => EntityClass,
271
295
  ...Object.fromEntries(
272
296
  ORM_HOOK_NAMES.map((name) => [
@@ -283,14 +307,34 @@ const registerAllRepositorySubscribers = (
283
307
  ),
284
308
  };
285
309
  const exists = dataSource.subscribers.some(
286
- (s) => typeof s.listenTo === 'function' && s.listenTo() === EntityClass,
310
+ (subscriber) =>
311
+ hasListenTo(subscriber) && subscriber.listenTo() === EntityClass,
287
312
  );
288
313
  if (!exists) {
289
314
  dataSource.subscribers.push(subscriber as any);
290
- registeredSubscribers.add(EntityClass);
291
315
  }
292
316
  }
293
317
  };
318
+ const pruneGeneratedRepositorySubscribers = (dataSource: DataSource): void => {
319
+ const concreteSubscriberTargets = new Set(
320
+ dataSource.subscribers
321
+ .filter(
322
+ (subscriber): subscriber is SubscriberWithListenTo =>
323
+ !isGeneratedOrmHookSubscriber(subscriber) && hasListenTo(subscriber),
324
+ )
325
+ .map((subscriber) => subscriber.listenTo()),
326
+ );
327
+ const subscribers = dataSource.subscribers.filter(
328
+ (subscriber) =>
329
+ !isGeneratedOrmHookSubscriber(subscriber) ||
330
+ !concreteSubscriberTargets.has(subscriber.listenTo()),
331
+ );
332
+ dataSource.subscribers.splice(
333
+ 0,
334
+ dataSource.subscribers.length,
335
+ ...subscribers,
336
+ );
337
+ };
294
338
  const normalizeRepositoryToken = (token: string): string | undefined => {
295
339
  if (!token.endsWith(TYPEORM_REPOSITORY_SUFFIX)) {
296
340
  return undefined;
@@ -687,10 +731,11 @@ export const buildTestingMocks = async ({
687
731
  const entitiesArray = Array.from(typeOrmEntities);
688
732
  let typeOrmRootModule: DynamicModule | undefined;
689
733
  let typeOrmProviders: Provider[] = [];
734
+ let typeOrmDataSource: DataSource | undefined;
690
735
 
691
736
  if (shouldUseTypeOrm) {
692
737
  const baseOptions: DataSourceOptions = {
693
- type: 'sqlite',
738
+ type: 'better-sqlite3',
694
739
  database: ':memory:',
695
740
  synchronize: true,
696
741
  dropSchema: true,
@@ -700,6 +745,7 @@ export const buildTestingMocks = async ({
700
745
  } as DataSourceOptions;
701
746
  const dataSource = new DataSource(baseOptions);
702
747
  await dataSource.initialize();
748
+ typeOrmDataSource = dataSource;
703
749
  registerAllRepositorySubscribers(dataSource, entitiesArray as any[]);
704
750
  await runTypeOrmFixtures(dataSource);
705
751
  registerTypeOrmDataSource(dataSource);
@@ -732,6 +778,9 @@ export const buildTestingMocks = async ({
732
778
  ...rest,
733
779
  });
734
780
  const module = await testingModuleBuilder.compile();
781
+ if (typeOrmDataSource) {
782
+ pruneGeneratedRepositorySubscribers(typeOrmDataSource);
783
+ }
735
784
  registerTestingModule(module);
736
785
 
737
786
  return {
@@ -9,7 +9,7 @@ import { WorkflowRunFull } from '@hexabot-ai/types';
9
9
  import { forwardRef, Inject, Injectable, Scope } from '@nestjs/common';
10
10
  import { EventEmitter2 } from '@nestjs/event-emitter';
11
11
 
12
- import { ActionService } from '@/actions';
12
+ import { ActionService } from '@/actions/actions.service';
13
13
  import { MessageService } from '@/chat/services/message.service';
14
14
  import { SubscriberService } from '@/chat/services/subscriber.service';
15
15
  import { ContentTypeService } from '@/cms/services/content-type.service';
@@ -17,7 +17,7 @@ import { ContentService } from '@/cms/services/content.service';
17
17
  import { I18nService } from '@/i18n/services/i18n.service';
18
18
  import { LoggerService } from '@/logger/logger.service';
19
19
  import { SettingService } from '@/setting/services/setting.service';
20
- import { CredentialService } from '@/user';
20
+ import { CredentialService } from '@/user/services/credential.service';
21
21
  import { cloneObject } from '@/utils/helpers/clone';
22
22
  import { WorkflowContextState, WorkflowType } from '@/workflow/types';
23
23
 
@@ -27,8 +27,8 @@ import { DeleteResult } from 'typeorm/driver/mongodb/typings';
27
27
 
28
28
  import { ActionService } from '@/actions/actions.service';
29
29
  import { I18nService } from '@/i18n/services/i18n.service';
30
- import { UserService } from '@/user';
31
- import { UuidParam } from '@/utils';
30
+ import { UserService } from '@/user/services/user.service';
31
+ import { UuidParam } from '@/utils/decorators/uuid-param.decorator';
32
32
  import { BaseOrmController } from '@/utils/generics/base-orm.controller';
33
33
  import { PopulatePipe } from '@/utils/pipes/populate.pipe';
34
34
  import { TypeOrmSearchFilterPipe } from '@/utils/pipes/typeorm-search-filter.pipe';
@@ -24,8 +24,8 @@ import {
24
24
  OnBeforeUpdate,
25
25
  } from '@/database/decorators/orm-event-hooks.decorator';
26
26
  import { BaseOrmEntity } from '@/database/entities/base.entity';
27
- import { CredentialOrmEntity } from '@/user';
28
- import { AsRelation } from '@/utils';
27
+ import { CredentialOrmEntity } from '@/user/entities/credential.entity';
28
+ import { AsRelation } from '@/utils/decorators/relation-ref.decorator';
29
29
 
30
30
  import { McpServerDto } from '../dto/mcp-server.dto';
31
31
  import { McpServerTransport } from '../types';
@@ -181,7 +181,8 @@ export class WorkflowOrmEntity extends BaseOrmEntity<WorkflowDto> {
181
181
  });
182
182
  await event.manager.save(WorkflowVersionOrmEntity, version);
183
183
  } catch (error: any) {
184
- if (error.code !== 'SQLITE_CONSTRAINT') {
184
+ const code = error?.code ?? error?.driverError?.code;
185
+ if (typeof code !== 'string' || !code.startsWith('SQLITE_CONSTRAINT')) {
185
186
  throw error;
186
187
  }
187
188
  }
@@ -13,7 +13,7 @@ import { RemoveEvent } from 'typeorm';
13
13
  import { AppInstance } from '@/app.instance';
14
14
  import { config } from '@/config';
15
15
  import { LoggerService } from '@/logger/logger.service';
16
- import { UserService } from '@/user';
16
+ import { UserService } from '@/user/services/user.service';
17
17
  import {
18
18
  InsertEntityEvent,
19
19
  UpdateEntityEvent,
@@ -10,8 +10,8 @@ import { TypeOrmModule } from '@nestjs/typeorm';
10
10
 
11
11
  import { ChatModule } from '@/chat/chat.module';
12
12
  import { ThreadOrmEntity } from '@/chat/entities/thread.entity';
13
- import { CmsModule } from '@/cms';
14
- import { UserModule } from '@/user';
13
+ import { CmsModule } from '@/cms/cms.module';
14
+ import { UserModule } from '@/user/user.module';
15
15
 
16
16
  import { ConversationalWorkflowContext } from './contexts/conversational-workflow.context';
17
17
  import { ManualWorkflowContext } from './contexts/manual-workflow.context';