@wundr.io/autogen-orchestrator 1.0.3

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,795 @@
1
+ /**
2
+ * Nested Chat Handling for AutoGen-style Group Chat
3
+ *
4
+ * Implements support for sub-discussions within a main conversation,
5
+ * allowing focused interactions between subsets of participants.
6
+ */
7
+
8
+ import { EventEmitter } from 'eventemitter3';
9
+ import { v4 as uuidv4 } from 'uuid';
10
+
11
+ import type {
12
+ Message,
13
+ ChatParticipant,
14
+ ChatContext,
15
+ ChatResult,
16
+ ChatStatus,
17
+ NestedChatConfig,
18
+ NestedChatTrigger,
19
+ NestedChatTriggerValue,
20
+ NestedChatConditionFn,
21
+ NestedChatResult,
22
+ SummaryMethod,
23
+ CreateMessageOptions,
24
+ } from './types';
25
+
26
+ // Re-export NestedChatResult from types
27
+ export type { NestedChatResult } from './types';
28
+
29
+ /**
30
+ * Events emitted by the nested chat manager
31
+ */
32
+ export interface NestedChatEvents {
33
+ 'nested:started': (data: {
34
+ nestedChatId: string;
35
+ config: NestedChatConfig;
36
+ parentMessageId: string;
37
+ }) => void;
38
+ 'nested:message': (data: { nestedChatId: string; message: Message }) => void;
39
+ 'nested:completed': (data: {
40
+ nestedChatId: string;
41
+ result: ChatResult;
42
+ }) => void;
43
+ 'nested:error': (data: { nestedChatId: string; error: Error }) => void;
44
+ }
45
+
46
+ /**
47
+ * State of a nested chat session
48
+ */
49
+ interface NestedChatState {
50
+ id: string;
51
+ config: NestedChatConfig;
52
+ parentChatId: string;
53
+ parentMessageId: string;
54
+ participants: ChatParticipant[];
55
+ messages: Message[];
56
+ status: ChatStatus;
57
+ startedAt: Date;
58
+ context: ChatContext;
59
+ }
60
+
61
+ /**
62
+ * Manager for nested chat sessions within a group chat
63
+ */
64
+ export class NestedChatManager extends EventEmitter<NestedChatEvents> {
65
+ private configs: Map<string, NestedChatConfig> = new Map();
66
+ private activeChats: Map<string, NestedChatState> = new Map();
67
+ private completedChats: Map<string, NestedChatResult> = new Map();
68
+
69
+ /**
70
+ * Create a nested chat manager
71
+ * @param configs - Initial nested chat configurations
72
+ */
73
+ constructor(configs: NestedChatConfig[] = []) {
74
+ super();
75
+ for (const config of configs) {
76
+ this.addConfig(config);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Add a nested chat configuration
82
+ * @param config - Configuration to add
83
+ */
84
+ addConfig(config: NestedChatConfig): void {
85
+ this.configs.set(config.id, config);
86
+ }
87
+
88
+ /**
89
+ * Remove a nested chat configuration
90
+ * @param configId - Configuration ID to remove
91
+ */
92
+ removeConfig(configId: string): void {
93
+ this.configs.delete(configId);
94
+ }
95
+
96
+ /**
97
+ * Get all configurations
98
+ * @returns Array of nested chat configurations
99
+ */
100
+ getConfigs(): NestedChatConfig[] {
101
+ return Array.from(this.configs.values());
102
+ }
103
+
104
+ /**
105
+ * Check if a message should trigger a nested chat
106
+ * @param message - Message to check
107
+ * @param participants - Current participants
108
+ * @param context - Current chat context
109
+ * @returns Triggered configuration or null
110
+ */
111
+ checkTrigger(
112
+ message: Message,
113
+ participants: ChatParticipant[],
114
+ context: ChatContext,
115
+ ): NestedChatConfig | null {
116
+ for (const config of this.configs.values()) {
117
+ if (
118
+ this.evaluateTrigger(config.trigger, message, participants, context)
119
+ ) {
120
+ return config;
121
+ }
122
+ }
123
+ return null;
124
+ }
125
+
126
+ /**
127
+ * Evaluate if a trigger condition is met
128
+ * @param trigger - Trigger configuration
129
+ * @param message - Current message
130
+ * @param participants - Available participants
131
+ * @param context - Chat context
132
+ * @returns Whether the trigger is activated
133
+ */
134
+ private evaluateTrigger(
135
+ trigger: NestedChatTrigger,
136
+ message: Message,
137
+ participants: ChatParticipant[],
138
+ context: ChatContext,
139
+ ): boolean {
140
+ switch (trigger.type) {
141
+ case 'keyword':
142
+ return this.evaluateKeywordTrigger(trigger.value, message);
143
+
144
+ case 'participant':
145
+ return this.evaluateParticipantTrigger(
146
+ trigger.value,
147
+ message,
148
+ participants,
149
+ );
150
+
151
+ case 'condition':
152
+ return this.evaluateConditionTrigger(trigger.value, message, context);
153
+
154
+ case 'manual':
155
+ return this.evaluateManualTrigger(trigger.value, context);
156
+
157
+ default:
158
+ return false;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Evaluate keyword-based trigger
164
+ * @param value - Keyword(s) to check
165
+ * @param message - Message to check
166
+ * @returns Whether keyword is found
167
+ */
168
+ private evaluateKeywordTrigger(
169
+ value: NestedChatTriggerValue,
170
+ message: Message,
171
+ ): boolean {
172
+ const keywords: string[] = Array.isArray(value)
173
+ ? value
174
+ : typeof value === 'string'
175
+ ? [value]
176
+ : [];
177
+ const contentLower = message.content.toLowerCase();
178
+
179
+ return keywords.some(keyword =>
180
+ contentLower.includes(keyword.toLowerCase()),
181
+ );
182
+ }
183
+
184
+ /**
185
+ * Evaluate participant-based trigger
186
+ * @param value - Participant name(s)
187
+ * @param message - Current message
188
+ * @param participants - Available participants
189
+ * @returns Whether participant condition is met
190
+ */
191
+ private evaluateParticipantTrigger(
192
+ value: NestedChatTriggerValue,
193
+ message: Message,
194
+ participants: ChatParticipant[],
195
+ ): boolean {
196
+ const targetParticipants: string[] = Array.isArray(value)
197
+ ? value
198
+ : typeof value === 'string'
199
+ ? [value]
200
+ : [];
201
+
202
+ // Check if message is from one of the target participants
203
+ if (targetParticipants.includes(message.name)) {
204
+ return true;
205
+ }
206
+
207
+ // Check if message mentions target participants
208
+ for (const targetName of targetParticipants) {
209
+ const participant = participants.find(p => p.name === targetName);
210
+ if (participant && message.content.includes(`@${targetName}`)) {
211
+ return true;
212
+ }
213
+ }
214
+
215
+ return false;
216
+ }
217
+
218
+ /**
219
+ * Evaluate condition-based trigger
220
+ * @param value - Condition expression or function
221
+ * @param message - Current message
222
+ * @param context - Chat context
223
+ * @returns Whether condition is met
224
+ */
225
+ private evaluateConditionTrigger(
226
+ value: NestedChatTriggerValue,
227
+ message: Message,
228
+ context: ChatContext,
229
+ ): boolean {
230
+ // Check if value is a function (NestedChatConditionFn)
231
+ if (typeof value === 'function') {
232
+ return (value as NestedChatConditionFn)(message, context);
233
+ }
234
+
235
+ if (typeof value === 'string') {
236
+ // Simple condition parsing
237
+ const condition = value.toLowerCase();
238
+
239
+ if (condition.includes('round >')) {
240
+ const parts = condition.split('>');
241
+ const threshold = parseInt(parts[1]?.trim() || '0', 10);
242
+ return context.currentRound > threshold;
243
+ }
244
+
245
+ if (condition.includes('messages >')) {
246
+ const parts = condition.split('>');
247
+ const threshold = parseInt(parts[1]?.trim() || '0', 10);
248
+ return context.messageCount > threshold;
249
+ }
250
+ }
251
+
252
+ return false;
253
+ }
254
+
255
+ /**
256
+ * Evaluate manual trigger
257
+ * @param value - Manual trigger value (state key to check)
258
+ * @param context - Chat context
259
+ * @returns Whether manual trigger is set
260
+ */
261
+ private evaluateManualTrigger(
262
+ value: NestedChatTriggerValue,
263
+ context: ChatContext,
264
+ ): boolean {
265
+ // For manual triggers, value should be a string representing the state key
266
+ const triggerKey = typeof value === 'string' ? value : String(value);
267
+ return context.state[triggerKey] === true;
268
+ }
269
+
270
+ /**
271
+ * Start a nested chat session
272
+ * @param config - Nested chat configuration
273
+ * @param parentChatId - Parent chat ID
274
+ * @param parentMessageId - Message that triggered the nested chat
275
+ * @param allParticipants - All available participants
276
+ * @param parentContext - Parent chat context
277
+ * @returns Nested chat ID
278
+ */
279
+ startNestedChat(
280
+ config: NestedChatConfig,
281
+ parentChatId: string,
282
+ parentMessageId: string,
283
+ allParticipants: ChatParticipant[],
284
+ parentContext: ChatContext,
285
+ ): string {
286
+ const nestedChatId = uuidv4();
287
+
288
+ // Select participants for nested chat
289
+ const nestedParticipants = allParticipants.filter(p =>
290
+ config.participants.includes(p.name),
291
+ );
292
+
293
+ if (nestedParticipants.length < 2) {
294
+ throw new Error(
295
+ `Nested chat requires at least 2 participants, found ${nestedParticipants.length}`,
296
+ );
297
+ }
298
+
299
+ // Create nested context
300
+ const nestedContext: ChatContext = {
301
+ chatId: nestedChatId,
302
+ currentRound: 0,
303
+ messageCount: 0,
304
+ activeParticipants: nestedParticipants.map(p => p.name),
305
+ startTime: new Date(),
306
+ state: config.shareContext ? { ...parentContext.state } : {},
307
+ parentContext: parentContext,
308
+ };
309
+
310
+ // Create nested chat state
311
+ const state: NestedChatState = {
312
+ id: nestedChatId,
313
+ config,
314
+ parentChatId,
315
+ parentMessageId,
316
+ participants: nestedParticipants,
317
+ messages: [],
318
+ status: 'active',
319
+ startedAt: new Date(),
320
+ context: nestedContext,
321
+ };
322
+
323
+ this.activeChats.set(nestedChatId, state);
324
+
325
+ // Add initial prompt message if configured
326
+ if (config.prompt) {
327
+ const systemMessage = this.createMessage({
328
+ role: 'system',
329
+ content: config.prompt,
330
+ name: 'system',
331
+ });
332
+ state.messages.push(systemMessage);
333
+ }
334
+
335
+ this.emit('nested:started', {
336
+ nestedChatId,
337
+ config,
338
+ parentMessageId,
339
+ });
340
+
341
+ return nestedChatId;
342
+ }
343
+
344
+ /**
345
+ * Add a message to a nested chat
346
+ * @param nestedChatId - Nested chat ID
347
+ * @param message - Message to add
348
+ */
349
+ addMessage(nestedChatId: string, message: Message): void {
350
+ const state = this.activeChats.get(nestedChatId);
351
+ if (!state) {
352
+ throw new Error(`Nested chat not found: ${nestedChatId}`);
353
+ }
354
+
355
+ if (state.status !== 'active') {
356
+ throw new Error(`Nested chat is not active: ${state.status}`);
357
+ }
358
+
359
+ state.messages.push(message);
360
+ state.context.messageCount = state.messages.length;
361
+
362
+ this.emit('nested:message', { nestedChatId, message });
363
+ }
364
+
365
+ /**
366
+ * End a nested chat session
367
+ * @param nestedChatId - Nested chat ID
368
+ * @param status - Final status
369
+ * @param terminationReason - Reason for ending
370
+ * @returns Nested chat result
371
+ */
372
+ async endNestedChat(
373
+ nestedChatId: string,
374
+ status: ChatStatus = 'completed',
375
+ terminationReason?: string,
376
+ ): Promise<NestedChatResult> {
377
+ const state = this.activeChats.get(nestedChatId);
378
+ if (!state) {
379
+ throw new Error(`Nested chat not found: ${nestedChatId}`);
380
+ }
381
+
382
+ state.status = status;
383
+
384
+ // Generate summary
385
+ const summary = await this.generateSummary(
386
+ state.messages,
387
+ state.config.summaryMethod || 'last',
388
+ );
389
+
390
+ // Create chat result
391
+ const endedAt = new Date();
392
+ const chatResult: ChatResult = {
393
+ chatId: nestedChatId,
394
+ status,
395
+ messages: state.messages,
396
+ summary,
397
+ terminationReason,
398
+ totalRounds: state.context.currentRound,
399
+ totalMessages: state.messages.length,
400
+ participants: state.participants.map(p => p.name),
401
+ durationMs: endedAt.getTime() - state.startedAt.getTime(),
402
+ startedAt: state.startedAt,
403
+ endedAt,
404
+ };
405
+
406
+ const result: NestedChatResult = {
407
+ nestedChatId,
408
+ configId: state.config.id,
409
+ result: chatResult,
410
+ summary,
411
+ parentMessageId: state.parentMessageId,
412
+ };
413
+
414
+ // Move from active to completed
415
+ this.activeChats.delete(nestedChatId);
416
+ this.completedChats.set(nestedChatId, result);
417
+
418
+ this.emit('nested:completed', { nestedChatId, result: chatResult });
419
+
420
+ return result;
421
+ }
422
+
423
+ /**
424
+ * Generate a summary of the nested chat
425
+ * @param messages - Chat messages
426
+ * @param method - Summary method
427
+ * @returns Generated summary
428
+ */
429
+ private async generateSummary(
430
+ messages: Message[],
431
+ method: SummaryMethod,
432
+ ): Promise<string> {
433
+ switch (method) {
434
+ case 'last':
435
+ return this.generateLastMessageSummary(messages);
436
+
437
+ case 'llm':
438
+ return this.generateLLMSummary(messages);
439
+
440
+ case 'reflection':
441
+ return this.generateReflectionSummary(messages);
442
+
443
+ case 'custom':
444
+ return this.generateCustomSummary(messages);
445
+
446
+ default:
447
+ return this.generateLastMessageSummary(messages);
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Generate summary from the last message
453
+ * @param messages - Chat messages
454
+ * @returns Last message content as summary
455
+ */
456
+ private generateLastMessageSummary(messages: Message[]): string {
457
+ const lastNonSystemMessage = [...messages]
458
+ .reverse()
459
+ .find(m => m.role !== 'system');
460
+
461
+ if (!lastNonSystemMessage) {
462
+ return 'No substantive messages in nested chat.';
463
+ }
464
+
465
+ return `${lastNonSystemMessage.name}: ${lastNonSystemMessage.content}`;
466
+ }
467
+
468
+ /**
469
+ * Generate summary using LLM (placeholder)
470
+ * @param messages - Chat messages
471
+ * @returns LLM-generated summary
472
+ */
473
+ private async generateLLMSummary(messages: Message[]): Promise<string> {
474
+ // In a real implementation, this would call an LLM
475
+ // For now, generate a structured summary
476
+
477
+ const participantMessages = new Map<string, number>();
478
+ const topics: string[] = [];
479
+
480
+ for (const message of messages) {
481
+ if (message.role !== 'system') {
482
+ participantMessages.set(
483
+ message.name,
484
+ (participantMessages.get(message.name) || 0) + 1,
485
+ );
486
+
487
+ // Extract potential topics (simplified)
488
+ const words = message.content.split(/\s+/).filter(w => w.length > 5);
489
+ topics.push(...words.slice(0, 3));
490
+ }
491
+ }
492
+
493
+ const participantSummary = Array.from(participantMessages.entries())
494
+ .map(([name, count]) => `${name} (${count} messages)`)
495
+ .join(', ');
496
+
497
+ const uniqueTopics = [...new Set(topics)].slice(0, 5).join(', ');
498
+
499
+ return `Nested discussion with ${participantMessages.size} participants (${participantSummary}). Key topics: ${uniqueTopics || 'general discussion'}. Total ${messages.length} messages exchanged.`;
500
+ }
501
+
502
+ /**
503
+ * Generate a reflection-style summary
504
+ * @param messages - Chat messages
505
+ * @returns Reflection summary
506
+ */
507
+ private generateReflectionSummary(messages: Message[]): string {
508
+ const nonSystemMessages = messages.filter(m => m.role !== 'system');
509
+
510
+ if (nonSystemMessages.length === 0) {
511
+ return 'No discussion occurred.';
512
+ }
513
+
514
+ const firstMessage = nonSystemMessages[0]!;
515
+ const lastMessage = nonSystemMessages[nonSystemMessages.length - 1]!;
516
+
517
+ // Check for resolution indicators
518
+ const resolutionKeywords = [
519
+ 'agree',
520
+ 'resolved',
521
+ 'decided',
522
+ 'conclusion',
523
+ 'done',
524
+ ];
525
+ const hasResolution = nonSystemMessages.some(m =>
526
+ resolutionKeywords.some(k => m.content.toLowerCase().includes(k)),
527
+ );
528
+
529
+ const participants = [...new Set(nonSystemMessages.map(m => m.name))];
530
+
531
+ return (
532
+ `Discussion started with ${firstMessage.name}: "${firstMessage.content.slice(0, 50)}...". ` +
533
+ `${participants.length} participants contributed ${nonSystemMessages.length} messages. ` +
534
+ `${hasResolution ? 'A resolution was reached.' : 'Discussion ongoing.'} ` +
535
+ `Final message from ${lastMessage.name}: "${lastMessage.content.slice(0, 50)}..."`
536
+ );
537
+ }
538
+
539
+ /**
540
+ * Generate a custom summary (placeholder)
541
+ * @param messages - Chat messages
542
+ * @returns Custom summary
543
+ */
544
+ private generateCustomSummary(messages: Message[]): string {
545
+ // Default to last message summary
546
+ return this.generateLastMessageSummary(messages);
547
+ }
548
+
549
+ /**
550
+ * Get the state of an active nested chat
551
+ * @param nestedChatId - Nested chat ID
552
+ * @returns Nested chat state or undefined
553
+ */
554
+ getActiveChat(nestedChatId: string): NestedChatState | undefined {
555
+ return this.activeChats.get(nestedChatId);
556
+ }
557
+
558
+ /**
559
+ * Get a completed nested chat result
560
+ * @param nestedChatId - Nested chat ID
561
+ * @returns Nested chat result or undefined
562
+ */
563
+ getCompletedChat(nestedChatId: string): NestedChatResult | undefined {
564
+ return this.completedChats.get(nestedChatId);
565
+ }
566
+
567
+ /**
568
+ * Get all active nested chat IDs
569
+ * @returns Array of active nested chat IDs
570
+ */
571
+ getActiveChats(): string[] {
572
+ return Array.from(this.activeChats.keys());
573
+ }
574
+
575
+ /**
576
+ * Get all completed nested chat results
577
+ * @returns Array of nested chat results
578
+ */
579
+ getCompletedChats(): NestedChatResult[] {
580
+ return Array.from(this.completedChats.values());
581
+ }
582
+
583
+ /**
584
+ * Check if there are any active nested chats
585
+ * @returns Whether there are active nested chats
586
+ */
587
+ hasActiveChats(): boolean {
588
+ return this.activeChats.size > 0;
589
+ }
590
+
591
+ /**
592
+ * Increment the round counter for a nested chat
593
+ * @param nestedChatId - Nested chat ID
594
+ */
595
+ incrementRound(nestedChatId: string): void {
596
+ const state = this.activeChats.get(nestedChatId);
597
+ if (state) {
598
+ state.context.currentRound++;
599
+ }
600
+ }
601
+
602
+ /**
603
+ * Get the current context for a nested chat
604
+ * @param nestedChatId - Nested chat ID
605
+ * @returns Chat context or undefined
606
+ */
607
+ getContext(nestedChatId: string): ChatContext | undefined {
608
+ return this.activeChats.get(nestedChatId)?.context;
609
+ }
610
+
611
+ /**
612
+ * Update state in a nested chat context
613
+ * @param nestedChatId - Nested chat ID
614
+ * @param key - State key
615
+ * @param value - State value
616
+ */
617
+ updateState(nestedChatId: string, key: string, value: unknown): void {
618
+ const state = this.activeChats.get(nestedChatId);
619
+ if (state) {
620
+ state.context.state[key] = value;
621
+ }
622
+ }
623
+
624
+ /**
625
+ * Create a message object
626
+ * @param options - Message creation options
627
+ * @returns Created message
628
+ */
629
+ private createMessage(options: CreateMessageOptions): Message {
630
+ return {
631
+ id: uuidv4(),
632
+ role: options.role,
633
+ content: options.content,
634
+ name: options.name,
635
+ timestamp: new Date(),
636
+ contentType: options.contentType || 'text',
637
+ functionCall: options.functionCall,
638
+ metadata: options.metadata,
639
+ status: 'delivered',
640
+ };
641
+ }
642
+
643
+ /**
644
+ * Clear all nested chat data
645
+ */
646
+ clear(): void {
647
+ this.configs.clear();
648
+ this.activeChats.clear();
649
+ this.completedChats.clear();
650
+ }
651
+ }
652
+
653
+ /**
654
+ * Builder for creating nested chat configurations
655
+ */
656
+ export class NestedChatConfigBuilder {
657
+ private config: Partial<NestedChatConfig> = {};
658
+
659
+ /**
660
+ * Set the config ID
661
+ * @param id - Configuration ID
662
+ */
663
+ withId(id: string): this {
664
+ this.config.id = id;
665
+ return this;
666
+ }
667
+
668
+ /**
669
+ * Set the config name
670
+ * @param name - Configuration name
671
+ */
672
+ withName(name: string): this {
673
+ this.config.name = name;
674
+ return this;
675
+ }
676
+
677
+ /**
678
+ * Set a keyword trigger
679
+ * @param keywords - Keywords to trigger the nested chat
680
+ */
681
+ withKeywordTrigger(keywords: string | string[]): this {
682
+ this.config.trigger = {
683
+ type: 'keyword',
684
+ value: keywords,
685
+ description: `Triggered by keyword(s): ${Array.isArray(keywords) ? keywords.join(', ') : keywords}`,
686
+ };
687
+ return this;
688
+ }
689
+
690
+ /**
691
+ * Set a participant trigger
692
+ * @param participants - Participants that trigger the nested chat
693
+ */
694
+ withParticipantTrigger(participants: string | string[]): this {
695
+ this.config.trigger = {
696
+ type: 'participant',
697
+ value: participants,
698
+ description: `Triggered by participant(s): ${Array.isArray(participants) ? participants.join(', ') : participants}`,
699
+ };
700
+ return this;
701
+ }
702
+
703
+ /**
704
+ * Set a condition trigger
705
+ * @param condition - Condition expression or function
706
+ */
707
+ withConditionTrigger(
708
+ condition: string | ((message: Message, context: ChatContext) => boolean),
709
+ ): this {
710
+ this.config.trigger = {
711
+ type: 'condition',
712
+ value: condition,
713
+ description: 'Triggered by condition',
714
+ };
715
+ return this;
716
+ }
717
+
718
+ /**
719
+ * Set a manual trigger
720
+ * @param stateKey - State key to check for manual trigger
721
+ */
722
+ withManualTrigger(stateKey: string): this {
723
+ this.config.trigger = {
724
+ type: 'manual',
725
+ value: stateKey,
726
+ description: `Manually triggered via state key: ${stateKey}`,
727
+ };
728
+ return this;
729
+ }
730
+
731
+ /**
732
+ * Set the participants for the nested chat
733
+ * @param participants - Participant names
734
+ */
735
+ withParticipants(participants: string[]): this {
736
+ this.config.participants = participants;
737
+ return this;
738
+ }
739
+
740
+ /**
741
+ * Set the maximum rounds
742
+ * @param maxRounds - Maximum number of rounds
743
+ */
744
+ withMaxRounds(maxRounds: number): this {
745
+ this.config.maxRounds = maxRounds;
746
+ return this;
747
+ }
748
+
749
+ /**
750
+ * Set the summary method
751
+ * @param method - Summary method
752
+ */
753
+ withSummaryMethod(method: SummaryMethod): this {
754
+ this.config.summaryMethod = method;
755
+ return this;
756
+ }
757
+
758
+ /**
759
+ * Set the initial prompt
760
+ * @param prompt - Prompt for the nested chat
761
+ */
762
+ withPrompt(prompt: string): this {
763
+ this.config.prompt = prompt;
764
+ return this;
765
+ }
766
+
767
+ /**
768
+ * Enable context sharing with parent
769
+ */
770
+ withSharedContext(): this {
771
+ this.config.shareContext = true;
772
+ return this;
773
+ }
774
+
775
+ /**
776
+ * Build the configuration
777
+ * @returns Built nested chat configuration
778
+ */
779
+ build(): NestedChatConfig {
780
+ if (!this.config.id) {
781
+ this.config.id = uuidv4();
782
+ }
783
+ if (!this.config.name) {
784
+ this.config.name = `nested-chat-${this.config.id}`;
785
+ }
786
+ if (!this.config.trigger) {
787
+ throw new Error('Nested chat config requires a trigger');
788
+ }
789
+ if (!this.config.participants || this.config.participants.length < 2) {
790
+ throw new Error('Nested chat requires at least 2 participants');
791
+ }
792
+
793
+ return this.config as NestedChatConfig;
794
+ }
795
+ }