@multiplayer-app/ai-agent-node 0.1.0-beta.73 → 0.1.0-beta.75

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 (71) hide show
  1. package/dist/cjs/helpers/AIHelper.cjs +5 -3
  2. package/dist/cjs/helpers/AIHelper.cjs.map +1 -1
  3. package/dist/cjs/helpers/AIHelper.d.ts +2 -1
  4. package/dist/cjs/helpers/AIHelper.d.ts.map +1 -1
  5. package/dist/cjs/processors/ActivityProcessor.cjs +16 -2
  6. package/dist/cjs/processors/ActivityProcessor.cjs.map +1 -1
  7. package/dist/cjs/processors/ActivityProcessor.d.ts +2 -0
  8. package/dist/cjs/processors/ActivityProcessor.d.ts.map +1 -1
  9. package/dist/cjs/processors/ActivityProcessor.test.cjs +63 -8
  10. package/dist/cjs/processors/ActivityProcessor.test.cjs.map +1 -1
  11. package/dist/cjs/processors/AgentProcessor.cjs +4 -3
  12. package/dist/cjs/processors/AgentProcessor.cjs.map +1 -1
  13. package/dist/cjs/processors/AgentProcessor.d.ts.map +1 -1
  14. package/dist/cjs/processors/ChatProcessor.cjs +330 -32
  15. package/dist/cjs/processors/ChatProcessor.cjs.map +1 -1
  16. package/dist/cjs/processors/ChatProcessor.d.ts +12 -2
  17. package/dist/cjs/processors/ChatProcessor.d.ts.map +1 -1
  18. package/dist/cjs/processors/ChatProcessor.test.cjs +41 -0
  19. package/dist/cjs/processors/ChatProcessor.test.cjs.map +1 -1
  20. package/dist/cjs/services/AIService.cjs +25 -20
  21. package/dist/cjs/services/AIService.cjs.map +1 -1
  22. package/dist/cjs/services/AIService.d.ts.map +1 -1
  23. package/dist/cjs/store/AgentStore.cjs +11 -0
  24. package/dist/cjs/store/AgentStore.cjs.map +1 -1
  25. package/dist/cjs/store/AgentStore.d.ts +1 -0
  26. package/dist/cjs/store/AgentStore.d.ts.map +1 -1
  27. package/dist/cjs/store/ConfigStore.cjs +7 -0
  28. package/dist/cjs/store/ConfigStore.cjs.map +1 -1
  29. package/dist/cjs/store/ConfigStore.d.ts +1 -0
  30. package/dist/cjs/store/ConfigStore.d.ts.map +1 -1
  31. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  32. package/dist/cjs/utils/utils.cjs +31 -0
  33. package/dist/cjs/utils/utils.cjs.map +1 -0
  34. package/dist/cjs/utils/utils.d.ts +5 -0
  35. package/dist/cjs/utils/utils.d.ts.map +1 -0
  36. package/dist/esm/helpers/AIHelper.d.ts +2 -1
  37. package/dist/esm/helpers/AIHelper.d.ts.map +1 -1
  38. package/dist/esm/helpers/AIHelper.js +5 -3
  39. package/dist/esm/helpers/AIHelper.js.map +1 -1
  40. package/dist/esm/processors/ActivityProcessor.d.ts +2 -0
  41. package/dist/esm/processors/ActivityProcessor.d.ts.map +1 -1
  42. package/dist/esm/processors/ActivityProcessor.js +16 -2
  43. package/dist/esm/processors/ActivityProcessor.js.map +1 -1
  44. package/dist/esm/processors/ActivityProcessor.test.js +63 -8
  45. package/dist/esm/processors/ActivityProcessor.test.js.map +1 -1
  46. package/dist/esm/processors/AgentProcessor.d.ts.map +1 -1
  47. package/dist/esm/processors/AgentProcessor.js +4 -3
  48. package/dist/esm/processors/AgentProcessor.js.map +1 -1
  49. package/dist/esm/processors/ChatProcessor.d.ts +12 -2
  50. package/dist/esm/processors/ChatProcessor.d.ts.map +1 -1
  51. package/dist/esm/processors/ChatProcessor.js +330 -32
  52. package/dist/esm/processors/ChatProcessor.js.map +1 -1
  53. package/dist/esm/processors/ChatProcessor.test.js +41 -0
  54. package/dist/esm/processors/ChatProcessor.test.js.map +1 -1
  55. package/dist/esm/services/AIService.d.ts.map +1 -1
  56. package/dist/esm/services/AIService.js +25 -20
  57. package/dist/esm/services/AIService.js.map +1 -1
  58. package/dist/esm/store/AgentStore.d.ts +1 -0
  59. package/dist/esm/store/AgentStore.d.ts.map +1 -1
  60. package/dist/esm/store/AgentStore.js +11 -0
  61. package/dist/esm/store/AgentStore.js.map +1 -1
  62. package/dist/esm/store/ConfigStore.d.ts +1 -0
  63. package/dist/esm/store/ConfigStore.d.ts.map +1 -1
  64. package/dist/esm/store/ConfigStore.js +7 -0
  65. package/dist/esm/store/ConfigStore.js.map +1 -1
  66. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  67. package/dist/esm/utils/utils.d.ts +5 -0
  68. package/dist/esm/utils/utils.d.ts.map +1 -0
  69. package/dist/esm/utils/utils.js +26 -0
  70. package/dist/esm/utils/utils.js.map +1 -0
  71. package/package.json +3 -3
@@ -3,12 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ChatProcessor = void 0;
4
4
  const ai_agent_types_1 = require("@multiplayer-app/ai-agent-types");
5
5
  const zod_1 = require("zod");
6
- const helpers_1 = require("../helpers/index.cjs");
7
6
  const store_1 = require("../store/index.cjs");
8
- const helpers_2 = require("../helpers/index.cjs");
7
+ const helpers_1 = require("../helpers/index.cjs");
9
8
  const stream_1 = require("stream");
10
9
  const logger_1 = require("../libs/logger/index.cjs");
11
- const store_2 = require("../store/index.cjs");
10
+ const ai_agent_types_2 = require("@multiplayer-app/ai-agent-types");
11
+ const utils_1 = require("../utils/utils.cjs");
12
12
  class ChatProcessor {
13
13
  constructor(params) {
14
14
  this.chatRepository = params.chatRepository;
@@ -43,6 +43,117 @@ class ChatProcessor {
43
43
  }
44
44
  return payload.tenants ?? {};
45
45
  }
46
+ async updateChatAndEmit(chat, update, excludeSocketId) {
47
+ await this.chatRepository.update(chat.id, update);
48
+ const updatedChat = await this.chatRepository.findById(chat.id);
49
+ const chatToEmit = updatedChat ?? { ...chat, ...update };
50
+ this.socketService.emitChatUpdate(chat.userId, chatToEmit, excludeSocketId);
51
+ return chatToEmit;
52
+ }
53
+ async startSubagentProcess(params) {
54
+ const childChat = await this.chatRepository.create({
55
+ title: `${params.subAgentConfig.name} subagent`,
56
+ type: ai_agent_types_1.ChatType.Agent,
57
+ status: ai_agent_types_1.AgentStatus.Streaming,
58
+ sessionKind: ai_agent_types_2.AgentSessionKind.SUBAGENT,
59
+ parentChatId: params.parentChat.id,
60
+ parentMessageId: params.parentMessage.id,
61
+ parentToolCallId: params.parentToolCallId,
62
+ contextKey: params.parentChat.contextKey,
63
+ userId: params.parentChat.userId,
64
+ model: params.subAgentConfig.defaultModel || params.parentChat.model,
65
+ });
66
+ try {
67
+ const parentToolCall = params.parentMessage.toolCalls?.find((toolCall) => toolCall.id === params.parentToolCallId);
68
+ if (parentToolCall) {
69
+ parentToolCall.input = {
70
+ ...(parentToolCall.input ?? {}),
71
+ subagentChatId: childChat.id,
72
+ };
73
+ parentToolCall.subagentChatId = childChat.id;
74
+ const updatedParentMessage = await this.messageRepository.update(params.parentMessage.id, {
75
+ toolCalls: params.parentMessage.toolCalls,
76
+ });
77
+ if (updatedParentMessage) {
78
+ this.socketService.emitMessageUpdate(params.parentChat.userId, updatedParentMessage);
79
+ }
80
+ }
81
+ this.socketService.emitChatUpdate(params.parentChat.userId, childChat);
82
+ const executionTenants = this.getExecutionTenants(params);
83
+ const abortController = this.agentStore.registerSubAgentProcess(childChat.id, params.parentChat.id);
84
+ const userMessage = await this.createMessage(childChat, ai_agent_types_1.MessageRole.User, {
85
+ content: JSON.stringify(params.input),
86
+ agentName: undefined
87
+ });
88
+ await this.agentStore.shareAgentProcessEvent(childChat.id, {
89
+ type: store_1.AgentProcessEventType.Update,
90
+ data: userMessage
91
+ });
92
+ let assistantMessage = await this.createMessage(childChat, ai_agent_types_1.MessageRole.Assistant, {
93
+ content: '',
94
+ agentName: params.subAgentConfig.name
95
+ });
96
+ const parentActivity = await this.activityRepository.create({
97
+ ownerId: params.parentChat.userId,
98
+ groupId: params.parentChat.id,
99
+ name: ai_agent_types_1.ActivityOperationName.SUBAGENT,
100
+ tenants: executionTenants,
101
+ sourceId: assistantMessage.id,
102
+ sourceType: 'AgentMessage',
103
+ metadata: {
104
+ modelId: this.aiHelper.getLanguageModelId(childChat.model),
105
+ agentOptions: {
106
+ name: params.subAgentConfig.name,
107
+ modelId: this.aiHelper.getLanguageModelId(params.subAgentConfig.defaultModel),
108
+ temperature: params.subAgentConfig.temperature,
109
+ maxOutputTokens: params.subAgentConfig.maxOutputTokens,
110
+ topP: params.subAgentConfig.topP,
111
+ topK: params.subAgentConfig.topK,
112
+ presencePenalty: params.subAgentConfig.presencePenalty,
113
+ frequencyPenalty: params.subAgentConfig.frequencyPenalty,
114
+ stopSequences: params.subAgentConfig.stopSequences,
115
+ seed: params.subAgentConfig.seed,
116
+ },
117
+ },
118
+ });
119
+ const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
120
+ activity: parentActivity.id
121
+ });
122
+ const agentOptions = this.aiHelper.getAgentOptionsFromConfig(params.subAgentConfig, params.context);
123
+ agentOptions.onStepFinish = (stepResult) => {
124
+ return this.storeStepActivity({
125
+ chat: params.parentChat,
126
+ parentId: assistantMessage.activity,
127
+ stepResult,
128
+ name: ai_agent_types_1.ActivityOperationName.STEP_FINISHED,
129
+ sourceId: assistantMessage.id,
130
+ sourceType: 'AgentMessage',
131
+ tenants: executionTenants,
132
+ });
133
+ };
134
+ const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(childChat.userId, agentOptions.name);
135
+ agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
136
+ await this.streamMessageStep({
137
+ chat: childChat,
138
+ existingMessages: [userMessage, updatedAssistantMessage],
139
+ assistantMessage: updatedAssistantMessage,
140
+ agentOptions,
141
+ signal: abortController.signal,
142
+ tenants: executionTenants,
143
+ executionContext: params.executionContext,
144
+ context: params.context,
145
+ });
146
+ }
147
+ catch (error) {
148
+ await this.storeSubagentResponse({
149
+ subagentChat: childChat,
150
+ content: undefined,
151
+ error: error,
152
+ context: params.context,
153
+ executionContext: params.executionContext,
154
+ });
155
+ }
156
+ }
46
157
  mergeMissingUsageFields(target, source) {
47
158
  for (const [key, sourceValue] of Object.entries(source)) {
48
159
  const targetValue = target[key];
@@ -114,8 +225,12 @@ class ChatProcessor {
114
225
  }
115
226
  }
116
227
  async listChats(params) {
117
- // Build filter object from params
118
- const filter = {};
228
+ // Build filter object from params. Always exclude subagent chats:
229
+ // the repository translates ROOT into `sessionKind !== SUBAGENT`, which
230
+ // also includes legacy chats where the field is unset.
231
+ const filter = {
232
+ sessionKind: ai_agent_types_2.AgentSessionKind.ROOT
233
+ };
119
234
  if (params?.userId) {
120
235
  filter.userId = params.userId;
121
236
  }
@@ -164,15 +279,48 @@ class ChatProcessor {
164
279
  return this.messageRepository.findByChatIdPaginated(chatId, options);
165
280
  }
166
281
  async deleteChat(chatId) {
282
+ // Collect all subagent descendants BEFORE removing the parent, otherwise the
283
+ // `parentChatId` link is severed and we'd orphan the subtree. Subagents can
284
+ // themselves spawn subagents, so traversal must be recursive.
285
+ const descendantIds = await this.collectDescendantChatIds(chatId);
167
286
  const deleted = await this.chatRepository.delete(chatId);
168
287
  if (!deleted) {
169
288
  throw new Error('Chat not found');
170
289
  }
290
+ // Delete descendant chat rows in parallel. Use `Promise.allSettled` so a
291
+ // missing/already-deleted descendant doesn't abort cleanup of the rest.
292
+ if (descendantIds.length > 0) {
293
+ await Promise.allSettled(descendantIds.map(id => this.chatRepository.delete(id)));
294
+ }
295
+ const allIds = [chatId, ...descendantIds];
171
296
  await Promise.all([
172
- this.messageRepository.deleteByChatId(chatId),
173
- this.activityRepository.deleteByGroupId(chatId), // todo: discuss if this is needed
297
+ ...allIds.map(id => this.messageRepository.deleteByChatId(id)),
298
+ ...allIds.map(id => this.activityRepository.deleteByGroupId(id)),
174
299
  ]);
175
- this.artifactStore.deleteArtifacts(chatId);
300
+ for (const id of allIds) {
301
+ this.artifactStore.deleteArtifacts(id);
302
+ }
303
+ }
304
+ /**
305
+ * Walk the subagent tree rooted at `rootChatId` and return every descendant
306
+ * chat id (excluding the root itself). BFS, with parallel fan-out per level
307
+ * so deeply nested trees don't serialize one DB roundtrip per depth.
308
+ */
309
+ async collectDescendantChatIds(rootChatId) {
310
+ const descendants = [];
311
+ let frontier = [rootChatId];
312
+ while (frontier.length > 0) {
313
+ const childLists = await Promise.all(frontier.map(id => this.chatRepository.find({ parentChatId: id })));
314
+ const nextFrontier = [];
315
+ for (const list of childLists) {
316
+ for (const child of list) {
317
+ descendants.push(child.id);
318
+ nextFrontier.push(child.id);
319
+ }
320
+ }
321
+ frontier = nextFrontier;
322
+ }
323
+ return descendants;
176
324
  }
177
325
  async upsertAndGetChat(payload, excludeSocketId) {
178
326
  const targetUserId = payload.userId ?? 'guest';
@@ -235,6 +383,7 @@ class ChatProcessor {
235
383
  title: this.getTemporaryTitle(contextKey),
236
384
  type: ai_agent_types_1.ChatType.Chat,
237
385
  status: ai_agent_types_1.AgentStatus.Streaming,
386
+ sessionKind: ai_agent_types_2.AgentSessionKind.ROOT,
238
387
  contextKey,
239
388
  userId: targetUserId,
240
389
  metadata,
@@ -306,8 +455,11 @@ class ChatProcessor {
306
455
  * Update selected fields (input/output/status) on a specific tool call.
307
456
  */
308
457
  async updateToolCall(params) {
309
- if (params.input === undefined && params.output === undefined && params.status === undefined) {
310
- throw new Error('At least one of input, output, or status must be provided');
458
+ if (params.input === undefined &&
459
+ params.output === undefined &&
460
+ params.status === undefined &&
461
+ params.subagentChatId === undefined) {
462
+ throw new Error('At least one tool call update field must be provided');
311
463
  }
312
464
  const chat = await this.chatRepository.findById(params.chatId);
313
465
  if (!chat) {
@@ -335,6 +487,7 @@ class ChatProcessor {
335
487
  ...(params.input !== undefined ? { input: params.input } : {}),
336
488
  ...(params.output !== undefined ? { output: params.output } : {}),
337
489
  ...(params.status !== undefined ? { status: params.status } : {}),
490
+ ...(params.subagentChatId !== undefined ? { subagentChatId: params.subagentChatId } : {}),
338
491
  });
339
492
  if (!updatedMessage) {
340
493
  throw new Error('Tool call not found in message');
@@ -344,8 +497,80 @@ class ChatProcessor {
344
497
  this.socketService.emitMessageUpdate(chat.userId, updatedMessage, params.excludeSocketId);
345
498
  return updatedMessage;
346
499
  }
500
+ async storeSubagentResponse(params) {
501
+ const { subagentChat, content, error, context, executionContext } = params;
502
+ if (!subagentChat.parentChatId || !subagentChat.parentMessageId) {
503
+ throw new Error(`Parent chat or message not found for subagent ${subagentChat.id}`);
504
+ }
505
+ const chat = await this.chatRepository.findById(subagentChat.parentChatId);
506
+ if (!chat) {
507
+ throw new Error(`Parent chat not found for subagent ${subagentChat.id}`);
508
+ }
509
+ const assistantMessage = await this.messageRepository.findById(subagentChat.parentMessageId);
510
+ if (!assistantMessage || assistantMessage.chat !== subagentChat.parentChatId) {
511
+ throw new Error(`Assistant message with id ${subagentChat.parentMessageId} not found`);
512
+ }
513
+ const toolCall = assistantMessage.toolCalls?.find(tc => tc.id === subagentChat.parentToolCallId);
514
+ if (!toolCall) {
515
+ throw new Error(`Tool call with id ${subagentChat.parentToolCallId} not found`);
516
+ }
517
+ if (error) {
518
+ toolCall.output = {
519
+ type: 'error',
520
+ message: error,
521
+ };
522
+ toolCall.status = ai_agent_types_1.AgentToolCallStatus.Failed;
523
+ }
524
+ else {
525
+ toolCall.output = content;
526
+ toolCall.status = ai_agent_types_1.AgentToolCallStatus.Succeeded;
527
+ }
528
+ await this.messageRepository.update(assistantMessage.id, assistantMessage);
529
+ const agentOptions = await this.aiHelper.getAgentOptions({
530
+ agentName: assistantMessage.agentName,
531
+ context: context,
532
+ executionContext: executionContext,
533
+ });
534
+ const executionTenants = this.getExecutionTenants(params);
535
+ const abortController = this.agentStore.registerAgentProcess(chat.id);
536
+ const existingMessages = await this.messageRepository.findByChatId(chat.id);
537
+ // Limit context to prevent exceeding token limits
538
+ const limitedMessages = helpers_1.ContextLimiter.limitContext(existingMessages, {
539
+ maxMessages: this.config.ai.maxContextMessages,
540
+ keepFirstUserMessage: true,
541
+ keepSystemMessages: true,
542
+ });
543
+ agentOptions.onStepFinish = (stepResult) => {
544
+ return this.storeStepActivity({
545
+ chat,
546
+ parentId: assistantMessage.activity,
547
+ stepResult,
548
+ name: ai_agent_types_1.ActivityOperationName.STEP_FINISHED,
549
+ sourceId: assistantMessage.id,
550
+ sourceType: 'AgentMessage',
551
+ tenants: executionTenants,
552
+ });
553
+ };
554
+ const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(chat.userId, agentOptions.name);
555
+ agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
556
+ await this.streamMessageStep({
557
+ chat,
558
+ existingMessages: limitedMessages,
559
+ assistantMessage,
560
+ agentOptions,
561
+ signal: abortController.signal,
562
+ tenants: executionTenants,
563
+ executionContext,
564
+ context,
565
+ });
566
+ }
347
567
  async streamMessageStep(params) {
348
568
  const { chat, assistantMessage, existingMessages, signal, excludeSocketId, agentOptions } = params;
569
+ const persistAndBroadcastToolCalls = async () => {
570
+ await this.messageRepository.update(assistantMessage.id, { toolCalls: assistantMessage.toolCalls ?? [] });
571
+ this.socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
572
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Update, data: assistantMessage });
573
+ };
349
574
  if (signal.aborted) {
350
575
  return;
351
576
  }
@@ -359,12 +584,21 @@ class ChatProcessor {
359
584
  await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Error, data: chunk.error });
360
585
  assistantMessage.content = chunk.error.message || 'Sorry, I cannot process you request due to the error';
361
586
  await this.messageRepository.update(assistantMessage.id, assistantMessage);
362
- await this.chatRepository.update(chat.id, { status: ai_agent_types_1.AgentStatus.Error });
587
+ await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.Error }, excludeSocketId);
588
+ if (chat.sessionKind === ai_agent_types_2.AgentSessionKind.SUBAGENT) {
589
+ await this.storeSubagentResponse({
590
+ subagentChat: chat,
591
+ content: undefined,
592
+ error: chunk.error,
593
+ context: params.context,
594
+ executionContext: params.executionContext,
595
+ });
596
+ }
363
597
  continue;
364
598
  }
365
599
  if (chunk.type === 'abort') {
366
600
  await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Aborted, data: undefined });
367
- await this.chatRepository.update(chat.id, { status: ai_agent_types_1.AgentStatus.Aborted });
601
+ await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.Aborted }, excludeSocketId);
368
602
  await this.messageRepository.update(assistantMessage.id, assistantMessage);
369
603
  this.socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
370
604
  continue;
@@ -383,8 +617,9 @@ class ChatProcessor {
383
617
  ((totalUsage.promptTokens ?? 0) + (totalUsage.completionTokens ?? 0));
384
618
  }
385
619
  await this.messageRepository.update(assistantMessage.id, { ...assistantMessage });
620
+ this.socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
386
621
  if (chunk.finishReason === 'stop') {
387
- await this.chatRepository.update(chat.id, { status: ai_agent_types_1.AgentStatus.Finished });
622
+ let nextChat = await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.Finished }, excludeSocketId);
388
623
  if (chat.title && this.isTemporaryTitle(chat.title)) {
389
624
  const title = await this.aiHelper.generateTitleForMessage(chat.contextKey, existingMessages.filter(m => m.role === ai_agent_types_1.MessageRole.User), (stepResult) => {
390
625
  return this.storeStepActivity({
@@ -396,16 +631,51 @@ class ChatProcessor {
396
631
  tenants: params.tenants || {},
397
632
  });
398
633
  }, params.executionContext);
399
- await this.chatRepository.update(chat.id, { title });
400
- this.socketService.emitChatUpdate(chat.userId, { ...chat, title });
634
+ nextChat = await this.updateChatAndEmit(nextChat, { title }, excludeSocketId);
635
+ }
636
+ if (chat.sessionKind === ai_agent_types_2.AgentSessionKind.SUBAGENT) {
637
+ await this.storeSubagentResponse({
638
+ subagentChat: chat,
639
+ content: assistantMessage.content,
640
+ error: undefined,
641
+ context: params.context,
642
+ executionContext: params.executionContext,
643
+ });
401
644
  }
402
645
  }
403
646
  if (chunk.finishReason === 'tool-calls') {
404
- if (assistantMessage.toolCalls?.some(toolCall => toolCall.requiresConfirmation && toolCall.approvalId && !toolCall.output)) {
405
- await this.chatRepository.update(chat.id, { status: ai_agent_types_1.AgentStatus.WaitingForUserAction });
647
+ const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.output === undefined && toolCall.status === ai_agent_types_1.AgentToolCallStatus.Running);
648
+ if (!toolCall) {
406
649
  await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Finished, data: assistantMessage });
650
+ await this.storeSubagentResponse({
651
+ subagentChat: chat,
652
+ content: JSON.stringify(assistantMessage.toolCalls),
653
+ error: undefined,
654
+ context: params.context,
655
+ executionContext: params.executionContext,
656
+ });
407
657
  continue;
408
658
  }
659
+ const toolConfig = store_1.ConfigStore.getInstance().getToolConfig(assistantMessage.agentName || '', toolCall.name);
660
+ if (toolCall.requiresConfirmation && toolCall.approvalId) {
661
+ await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.WaitingForUserAction }, excludeSocketId);
662
+ await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Finished, data: assistantMessage });
663
+ }
664
+ else if (toolConfig && toolConfig.type === ai_agent_types_1.AgentToolType.SUBAGENT) {
665
+ void this.startSubagentProcess({
666
+ parentChat: chat,
667
+ parentMessage: assistantMessage,
668
+ parentToolCallId: toolCall.id,
669
+ toolName: toolCall.name,
670
+ input: toolCall.input,
671
+ subAgentConfig: toolConfig.data.subAgent,
672
+ context: params.context,
673
+ executionContext: params.executionContext,
674
+ }).catch(error => {
675
+ logger_1.logger.error(error);
676
+ //todo: store error in response
677
+ });
678
+ }
409
679
  }
410
680
  //todo: Handle other finish reasons // length, content, error, other, undefined
411
681
  await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Finished, data: assistantMessage });
@@ -433,20 +703,43 @@ class ChatProcessor {
433
703
  if (chunk.type === 'tool-input-start') {
434
704
  if (!assistantMessage.toolCalls)
435
705
  assistantMessage.toolCalls = [];
436
- assistantMessage.toolCalls.push({
437
- id: chunk.id,
438
- name: chunk.toolName,
439
- status: ai_agent_types_1.AgentToolCallStatus.Pending,
440
- input: {},
441
- });
706
+ const existingToolCall = assistantMessage.toolCalls.find((toolCall) => toolCall.id === chunk.id);
707
+ if (existingToolCall) {
708
+ existingToolCall.name = chunk.toolName;
709
+ if (!existingToolCall.status) {
710
+ existingToolCall.status = ai_agent_types_1.AgentToolCallStatus.Pending;
711
+ }
712
+ existingToolCall.input = existingToolCall.input ?? {};
713
+ }
714
+ else {
715
+ assistantMessage.toolCalls.push({
716
+ id: chunk.id,
717
+ name: chunk.toolName,
718
+ status: ai_agent_types_1.AgentToolCallStatus.Pending,
719
+ input: {},
720
+ });
721
+ }
722
+ await persistAndBroadcastToolCalls();
442
723
  continue;
443
724
  }
444
725
  if (chunk.type === 'tool-call') {
445
- const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
446
- if (toolCall) {
726
+ if (!assistantMessage.toolCalls)
727
+ assistantMessage.toolCalls = [];
728
+ let toolCall = assistantMessage.toolCalls.find(toolCall => toolCall.id === chunk.toolCallId);
729
+ if (!toolCall) {
730
+ toolCall = {
731
+ id: chunk.toolCallId,
732
+ name: chunk.toolName,
733
+ status: ai_agent_types_1.AgentToolCallStatus.Running,
734
+ input: chunk.input,
735
+ };
736
+ assistantMessage.toolCalls.push(toolCall);
737
+ }
738
+ else {
447
739
  toolCall.status = ai_agent_types_1.AgentToolCallStatus.Running;
448
740
  toolCall.input = chunk.input;
449
741
  }
742
+ await persistAndBroadcastToolCalls();
450
743
  continue;
451
744
  }
452
745
  if (chunk.type === 'tool-error') {
@@ -454,29 +747,33 @@ class ChatProcessor {
454
747
  if (toolCall) {
455
748
  toolCall.status = ai_agent_types_1.AgentToolCallStatus.Failed;
456
749
  toolCall.error = chunk.error.message || JSON.stringify(chunk.error);
750
+ await persistAndBroadcastToolCalls();
457
751
  }
458
752
  continue;
459
753
  }
460
754
  if (chunk.type === 'tool-approval-request') {
461
755
  const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCall.toolCallId);
462
756
  if (toolCall) {
757
+ const toolConfig = store_1.ConfigStore.getInstance().getToolConfig(assistantMessage.agentName || '', toolCall.name);
758
+ if (toolConfig && toolConfig.type === ai_agent_types_1.AgentToolType.SUBAGENT) {
759
+ // TODO: allow needsApproval flow for the subagents too
760
+ continue;
761
+ }
463
762
  toolCall.requiresConfirmation = true;
464
763
  toolCall.approvalId = chunk.approvalId;
465
764
  if (toolCall.name === ai_agent_types_1.AgentToolType.REQUEST_CLARIFICATION) {
466
765
  toolCall.requiresUserAction = true;
467
766
  }
767
+ await persistAndBroadcastToolCalls();
468
768
  }
469
769
  continue;
470
770
  }
471
771
  if (chunk.type === 'tool-result') {
472
772
  const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
473
773
  if (toolCall) {
474
- if (chunk.output?.requiresApproval) {
475
- toolCall.requiresConfirmation = true;
476
- continue;
477
- }
478
774
  toolCall.status = ai_agent_types_1.AgentToolCallStatus.Succeeded;
479
775
  toolCall.output = chunk.output;
776
+ await persistAndBroadcastToolCalls();
480
777
  }
481
778
  continue;
482
779
  }
@@ -667,7 +964,7 @@ class ChatProcessor {
667
964
  let agentOptions;
668
965
  const existingMessages = await this.messageRepository.findByChatId(chat.id);
669
966
  // Limit context to prevent exceeding token limits
670
- const limitedMessages = helpers_2.ContextLimiter.limitContext(existingMessages, {
967
+ const limitedMessages = helpers_1.ContextLimiter.limitContext(existingMessages, {
671
968
  maxMessages: this.config.ai.maxContextMessages,
672
969
  keepFirstUserMessage: true,
673
970
  keepSystemMessages: true,
@@ -712,6 +1009,7 @@ class ChatProcessor {
712
1009
  signal: abortController.signal,
713
1010
  tenants: executionTenants,
714
1011
  executionContext: payload.executionContext,
1012
+ context: payload.context,
715
1013
  });
716
1014
  }
717
1015
  catch (error) {
@@ -721,9 +1019,9 @@ class ChatProcessor {
721
1019
  getAvailableTools(agentOptions, userPreferences) {
722
1020
  const toolNames = Object.keys(agentOptions.tools || {});
723
1021
  const availableToolsMap = Object.fromEntries(toolNames.map(key => [key, true]));
724
- const agentsConfiguration = store_2.ConfigStore.getInstance().getAgentConfigByName(agentOptions.name);
1022
+ const agentsConfiguration = store_1.ConfigStore.getInstance().getAgentConfigByName(agentOptions.name);
725
1023
  agentsConfiguration.tools.forEach((tool) => {
726
- const toolTitle = tool.data?.title || tool.type;
1024
+ const toolTitle = (0, utils_1.getAgentToolName)(tool);
727
1025
  if (tool.disabledByDefault) {
728
1026
  availableToolsMap[toolTitle] = false;
729
1027
  }