@hexabot-ai/api 3.2.2-alpha.3 → 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.
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.3",
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",
@@ -90,8 +90,8 @@
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.3",
94
- "@hexabot-ai/types": "3.0.2-alpha.3"
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.3"
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",
@@ -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',
@@ -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
  }
@@ -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
  }