@xagent-ai/cli 1.3.7 → 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.
Files changed (65) hide show
  1. package/README.md +9 -0
  2. package/README_CN.md +9 -0
  3. package/dist/cli.js +26 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/mcp.d.ts +8 -1
  6. package/dist/mcp.d.ts.map +1 -1
  7. package/dist/mcp.js +53 -20
  8. package/dist/mcp.js.map +1 -1
  9. package/dist/sdk-output-adapter.d.ts +79 -0
  10. package/dist/sdk-output-adapter.d.ts.map +1 -1
  11. package/dist/sdk-output-adapter.js +118 -0
  12. package/dist/sdk-output-adapter.js.map +1 -1
  13. package/dist/session.d.ts +88 -1
  14. package/dist/session.d.ts.map +1 -1
  15. package/dist/session.js +351 -5
  16. package/dist/session.js.map +1 -1
  17. package/dist/smart-approval.d.ts.map +1 -1
  18. package/dist/smart-approval.js +1 -0
  19. package/dist/smart-approval.js.map +1 -1
  20. package/dist/system-prompt-generator.d.ts +15 -1
  21. package/dist/system-prompt-generator.d.ts.map +1 -1
  22. package/dist/system-prompt-generator.js +36 -27
  23. package/dist/system-prompt-generator.js.map +1 -1
  24. package/dist/team-manager/index.d.ts +6 -0
  25. package/dist/team-manager/index.d.ts.map +1 -0
  26. package/dist/team-manager/index.js +6 -0
  27. package/dist/team-manager/index.js.map +1 -0
  28. package/dist/team-manager/message-broker.d.ts +128 -0
  29. package/dist/team-manager/message-broker.d.ts.map +1 -0
  30. package/dist/team-manager/message-broker.js +638 -0
  31. package/dist/team-manager/message-broker.js.map +1 -0
  32. package/dist/team-manager/team-coordinator.d.ts +45 -0
  33. package/dist/team-manager/team-coordinator.d.ts.map +1 -0
  34. package/dist/team-manager/team-coordinator.js +887 -0
  35. package/dist/team-manager/team-coordinator.js.map +1 -0
  36. package/dist/team-manager/team-store.d.ts +49 -0
  37. package/dist/team-manager/team-store.d.ts.map +1 -0
  38. package/dist/team-manager/team-store.js +436 -0
  39. package/dist/team-manager/team-store.js.map +1 -0
  40. package/dist/team-manager/teammate-spawner.d.ts +86 -0
  41. package/dist/team-manager/teammate-spawner.d.ts.map +1 -0
  42. package/dist/team-manager/teammate-spawner.js +605 -0
  43. package/dist/team-manager/teammate-spawner.js.map +1 -0
  44. package/dist/team-manager/types.d.ts +164 -0
  45. package/dist/team-manager/types.d.ts.map +1 -0
  46. package/dist/team-manager/types.js +27 -0
  47. package/dist/team-manager/types.js.map +1 -0
  48. package/dist/tools.d.ts +41 -1
  49. package/dist/tools.d.ts.map +1 -1
  50. package/dist/tools.js +288 -32
  51. package/dist/tools.js.map +1 -1
  52. package/package.json +1 -1
  53. package/src/cli.ts +20 -0
  54. package/src/mcp.ts +64 -25
  55. package/src/sdk-output-adapter.ts +177 -0
  56. package/src/session.ts +423 -15
  57. package/src/smart-approval.ts +1 -0
  58. package/src/system-prompt-generator.ts +59 -26
  59. package/src/team-manager/index.ts +5 -0
  60. package/src/team-manager/message-broker.ts +751 -0
  61. package/src/team-manager/team-coordinator.ts +1117 -0
  62. package/src/team-manager/team-store.ts +558 -0
  63. package/src/team-manager/teammate-spawner.ts +800 -0
  64. package/src/team-manager/types.ts +206 -0
  65. package/src/tools.ts +316 -33
package/src/session.ts CHANGED
@@ -93,11 +93,33 @@ export class InteractiveSession {
93
93
  private heartbeatTimeout: NodeJS.Timeout | null = null;
94
94
  private heartbeatTimeoutMs: number = 300000; // 5 minutes timeout for long AI responses
95
95
  private lastActivityTime: number = Date.now();
96
+ private teammateMessageQueue: ChatMessage[] = [];
96
97
 
97
98
  // SDK response handling for approvals and questions
98
99
  private approvalPromises: Map<string, { resolve: (approved: boolean) => void; reject: (err: Error) => void }> = new Map();
99
100
  private questionPromises: Map<string, { resolve: (answers: string[]) => void; reject: (err: Error) => void }> = new Map();
100
101
 
102
+ // Team mode properties
103
+ private isTeamMode: boolean = false;
104
+ private teamId: string | null = null;
105
+ private memberId: string | null = null;
106
+ private memberName: string | null = null;
107
+ private memberRole: string | null = null;
108
+ private leadId: string | null = null;
109
+ private teamStore: any = null;
110
+ private messageClient: any = null;
111
+ private spawnPrompt: string | null = null;
112
+ private brokerPort: number | null = null;
113
+ private initialTaskId: string | null = null;
114
+
115
+ // Operation lock for preventing concurrent operations
116
+ private _isOperationInProgress: boolean = false;
117
+ private _shutdownResolver: ((value: void) => void) | null = null;
118
+
119
+ // Queue processing lock to prevent race conditions
120
+ private isProcessingQueue: boolean = false;
121
+ private queueProcessingPromise: Promise<void> | null = null;
122
+
101
123
  constructor(indentLevel: number = 0) {
102
124
  this.rl = readline.createInterface({
103
125
  input: process.stdin,
@@ -113,6 +135,59 @@ export class InteractiveSession {
113
135
  this.sessionManager = getSessionManager(process.cwd());
114
136
  this.slashCommandHandler = new SlashCommandHandler();
115
137
 
138
+ // Check if running in Team Mode
139
+ if (process.env.XAGENT_TEAM_MODE === 'true') {
140
+ this.isTeamMode = true;
141
+ this.teamId = process.env.XAGENT_TEAM_ID || null;
142
+ this.memberId = process.env.XAGENT_MEMBER_ID || null;
143
+ this.memberName = process.env.XAGENT_MEMBER_NAME || null;
144
+ this.memberRole = 'teammate'; // Role is now determined by tool response, not env var
145
+ this.leadId = process.env.XAGENT_LEAD_ID || null;
146
+ this.spawnPrompt = process.env.XAGENT_SPAWN_PROMPT || null;
147
+ this.brokerPort = process.env.XAGENT_BROKER_PORT ? parseInt(process.env.XAGENT_BROKER_PORT, 10) : null;
148
+ this.initialTaskId = process.env.XAGENT_INITIAL_TASK_ID || null;
149
+
150
+ // Show team role info in welcome message (compact format)
151
+ console.log(colors.textMuted(`[Team] ${this.memberName} (${this.memberRole})`));
152
+
153
+ // Import and initialize team components
154
+ import('./team-manager/index.js').then(async ({ getTeamStore, MessageClient, setTeammateClient }) => {
155
+ this.teamStore = getTeamStore();
156
+
157
+ // Connect to message broker if port is available
158
+ if (this.brokerPort && this.teamId && this.memberId) {
159
+ this.messageClient = new MessageClient(
160
+ this.teamId,
161
+ this.memberId,
162
+ this.brokerPort
163
+ );
164
+
165
+ // Save to global singleton for TeamCoordinator to use
166
+ setTeammateClient(this.messageClient);
167
+
168
+ this.messageClient.on('message', (msg: any) => {
169
+ this.handleTeamMessage(msg);
170
+ });
171
+
172
+ this.messageClient.on('connected', () => {
173
+ // Silently connected - no output needed
174
+ });
175
+
176
+ this.messageClient.on('disconnected', () => {
177
+ // Silently disconnected - no output needed
178
+ });
179
+
180
+ try {
181
+ await this.messageClient.connect();
182
+ } catch (err) {
183
+ console.error('[Team] Failed to connect to message broker:', err);
184
+ }
185
+ }
186
+ }).catch(() => {
187
+ // Ignore import errors
188
+ });
189
+ }
190
+
116
191
  // Register /clear callback, clear local conversation when clearing dialogue
117
192
  this.slashCommandHandler.setClearCallback(() => {
118
193
  this.conversation = [];
@@ -224,6 +299,233 @@ export class InteractiveSession {
224
299
  await toolRegistry.setSdkMode(true, adapter);
225
300
  }
226
301
 
302
+ /**
303
+ * Handle incoming team message from socket.
304
+ * Messages are queued and must be explicitly processed by the agent.
305
+ */
306
+ private handleTeamMessage(msg: any): void {
307
+ if (!msg || !msg.content) return;
308
+
309
+ let teamMessage: ChatMessage;
310
+
311
+ if (msg.type === 'task_update') {
312
+ let taskInfo: string;
313
+ try {
314
+ const content = typeof msg.content === 'string' ? JSON.parse(msg.content) : msg.content;
315
+ taskInfo = `Task ${content.action}: ${content.title || content.taskId}`;
316
+ if (content.assignee) {
317
+ taskInfo += ` (assignee: ${content.assignee})`;
318
+ }
319
+ if (content.result) {
320
+ taskInfo += ` - Result: ${content.result.substring(0, 100)}...`;
321
+ }
322
+ } catch {
323
+ taskInfo = `Task update: ${msg.content}`;
324
+ }
325
+
326
+ teamMessage = {
327
+ role: 'user',
328
+ content: `<system-notification type="task_update">${taskInfo}</system-notification>`,
329
+ timestamp: msg.timestamp || Date.now()
330
+ };
331
+ } else {
332
+ teamMessage = {
333
+ role: 'user',
334
+ content: `<teammate-message from="${msg.fromMemberId}" type="${msg.type}">${msg.content}</teammate-message>`,
335
+ timestamp: msg.timestamp || Date.now()
336
+ };
337
+ }
338
+
339
+ this.teammateMessageQueue.push(teamMessage);
340
+
341
+ // Only start processing if not already processing
342
+ if (!this.isProcessingQueue && !this._isOperationInProgress) {
343
+ this.startQueueProcessing();
344
+ }
345
+ }
346
+
347
+ /**
348
+ * Start queue processing with proper locking.
349
+ */
350
+ private startQueueProcessing(): void {
351
+ // Prevent multiple queue processing coroutines
352
+ if (this.isProcessingQueue) {
353
+ return;
354
+ }
355
+
356
+ this.isProcessingQueue = true;
357
+ this.queueProcessingPromise = this.processMessageQueue();
358
+
359
+ // Handle completion and errors
360
+ this.queueProcessingPromise
361
+ .catch((error) => {
362
+ console.error('[Team] Queue processing error:', error);
363
+ })
364
+ .finally(() => {
365
+ this.isProcessingQueue = false;
366
+ this.queueProcessingPromise = null;
367
+ });
368
+ }
369
+
370
+ /**
371
+ * Process queued teammate messages one by one.
372
+ * This method should only be called from startQueueProcessing.
373
+ */
374
+ private async processMessageQueue(): Promise<void> {
375
+ while (this.teammateMessageQueue.length > 0) {
376
+ // Check if operation is in progress (e.g., user is typing or another operation)
377
+ if (this._isOperationInProgress) {
378
+ // Wait a bit and retry
379
+ await new Promise(resolve => setTimeout(resolve, 100));
380
+ continue;
381
+ }
382
+
383
+ const message = this.teammateMessageQueue.shift()!;
384
+ this.conversation.push(message);
385
+
386
+ try {
387
+ if (this.remoteAIClient) {
388
+ await this.generateRemoteResponse(0);
389
+ } else {
390
+ await this.generateResponse(0);
391
+ }
392
+ } catch (error) {
393
+ console.error('[Team] Failed to process teammate message:', error);
394
+ // Continue processing remaining messages even if one fails
395
+ }
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Get team mode status.
401
+ */
402
+ getIsTeamMode(): boolean {
403
+ return this.isTeamMode;
404
+ }
405
+
406
+ /**
407
+ * Get team ID.
408
+ */
409
+ getTeamId(): string | null {
410
+ return this.teamId;
411
+ }
412
+
413
+ /**
414
+ * Get member ID.
415
+ */
416
+ getMemberId(): string | null {
417
+ return this.memberId;
418
+ }
419
+
420
+ /**
421
+ * Get member name.
422
+ */
423
+ getMemberName(): string | null {
424
+ return this.memberName;
425
+ }
426
+
427
+ /**
428
+ * Get lead ID.
429
+ */
430
+ getLeadId(): string | null {
431
+ return this.leadId;
432
+ }
433
+
434
+ /**
435
+ * Get spawn prompt (for team mode).
436
+ */
437
+ getSpawnPrompt(): string | null {
438
+ return this.spawnPrompt;
439
+ }
440
+
441
+ /**
442
+ * Get teammate message queue info.
443
+ */
444
+ getTeammateMessageQueueInfo(): { length: number } {
445
+ return {
446
+ length: this.teammateMessageQueue.length,
447
+ };
448
+ }
449
+
450
+ /**
451
+ * Get and clear all teammate messages from queue.
452
+ * Returns all pending messages and clears the queue.
453
+ */
454
+ popTeammateMessages(): ChatMessage[] {
455
+ const messages = [...this.teammateMessageQueue];
456
+ this.teammateMessageQueue = [];
457
+ return messages;
458
+ }
459
+
460
+ /**
461
+ * Connect to team message broker (for lead agent after creating a team).
462
+ * This allows the lead agent to receive real-time messages from teammates.
463
+ */
464
+ async connectToTeamBroker(teamId: string, memberId: string, brokerPort: number): Promise<void> {
465
+ // Skip if already connected
466
+ if (this.messageClient) {
467
+ return;
468
+ }
469
+
470
+ this.isTeamMode = true;
471
+ this.teamId = teamId;
472
+ this.memberId = memberId;
473
+ this.memberRole = 'lead';
474
+ this.brokerPort = brokerPort;
475
+
476
+ try {
477
+ const { getTeamStore, MessageClient } = await import('./team-manager/index.js');
478
+ this.teamStore = getTeamStore();
479
+
480
+ this.messageClient = new MessageClient(teamId, memberId, brokerPort);
481
+
482
+ this.messageClient.on('message', (msg: any) => {
483
+ this.handleTeamMessage(msg);
484
+ });
485
+
486
+ this.messageClient.on('connected', () => {
487
+ // Silently connected - no output needed
488
+ });
489
+
490
+ this.messageClient.on('disconnected', () => {
491
+ // Silently disconnected - no output needed
492
+ });
493
+
494
+ await this.messageClient.connect();
495
+ } catch (err) {
496
+ console.error(colors.error('[Team] Failed to connect to message broker:'), err);
497
+ throw err; // Re-throw to let caller handle it
498
+ }
499
+ }
500
+
501
+ /**
502
+ * Cleanup team mode resources.
503
+ */
504
+ async cleanupTeamMode(): Promise<void> {
505
+ // Clear teammate's persistent MessageClient if exists
506
+ const { clearTeammateClient } = await import('./team-manager/index.js');
507
+ clearTeammateClient();
508
+
509
+ if (this.messageClient) {
510
+ await this.messageClient.disconnect();
511
+ this.messageClient = null;
512
+ }
513
+
514
+ if (this.teamStore && this.teamId && this.memberId) {
515
+ try {
516
+ await this.teamStore.updateMember(this.teamId, this.memberId, {
517
+ status: 'shutdown',
518
+ lastActivity: Date.now()
519
+ });
520
+ } catch {
521
+ // Ignore cleanup errors
522
+ }
523
+ }
524
+
525
+ // Reset team mode flag to restore thinking spinner
526
+ this.isTeamMode = false;
527
+ }
528
+
227
529
  /**
228
530
  * Get SDK mode status.
229
531
  */
@@ -268,11 +570,24 @@ export class InteractiveSession {
268
570
  await skillInvoker.reload();
269
571
 
270
572
  const toolRegistry = getToolRegistry();
573
+
574
+ // Build team context if running in team mode
575
+ const teamContext = this.isTeamMode ? {
576
+ teamId: this.teamId!,
577
+ memberId: this.memberId!,
578
+ memberName: this.memberName!,
579
+ memberRole: this.memberRole!,
580
+ leadId: this.leadId || undefined,
581
+ brokerPort: this.brokerPort || undefined,
582
+ initialTaskId: this.initialTaskId || undefined,
583
+ } : undefined;
584
+
271
585
  const promptGenerator = new SystemPromptGenerator(
272
586
  toolRegistry,
273
587
  this.executionMode,
274
588
  undefined,
275
- this.mcpManager
589
+ this.mcpManager,
590
+ teamContext
276
591
  );
277
592
 
278
593
  // Use the current agent's original system prompt as base
@@ -409,7 +724,7 @@ export class InteractiveSession {
409
724
  let _escCleanup: (() => void) | undefined;
410
725
  if (process.stdin.isTTY) {
411
726
  _escCleanup = setupEscKeyHandler(() => {
412
- if ((this as any)._isOperationInProgress) {
727
+ if (this._isOperationInProgress) {
413
728
  // An operation is running, let it be cancelled
414
729
  this.cancellationManager.cancel();
415
730
  }
@@ -418,16 +733,42 @@ export class InteractiveSession {
418
733
  }
419
734
 
420
735
  // Track if an operation is in progress
421
- (this as any)._isOperationInProgress = false;
736
+ this._isOperationInProgress = false;
422
737
 
423
738
  this.promptLoop();
424
739
 
425
740
  // Keep the promise pending until shutdown
426
741
  return new Promise((resolve) => {
427
- (this as any)._shutdownResolver = resolve;
742
+ this._shutdownResolver = resolve;
428
743
  });
429
744
  }
430
745
 
746
+ private async sendInitialPrompt(prompt: string): Promise<void> {
747
+ console.log(colors.textMuted(`\n📋 Initial prompt: ${prompt}\n`));
748
+
749
+ const userMessage: ChatMessage = {
750
+ role: 'user',
751
+ content: prompt,
752
+ timestamp: Date.now(),
753
+ };
754
+
755
+ this.conversation.push(userMessage);
756
+ await this.conversationManager.addMessage(userMessage);
757
+
758
+ const thinkingConfig = this.configManager.getThinkingConfig();
759
+ let thinkingTokens = 0;
760
+ if (thinkingConfig.enabled) {
761
+ const thinkingMode = detectThinkingKeywords(prompt);
762
+ thinkingTokens = getThinkingTokens(thinkingMode);
763
+ }
764
+
765
+ if (this.remoteAIClient) {
766
+ await this.generateRemoteResponse(thinkingTokens);
767
+ } else {
768
+ await this.generateResponse(thinkingTokens);
769
+ }
770
+ }
771
+
431
772
  private async initialize(): Promise<void> {
432
773
  logger.debug('\n[SESSION] ========== initialize() 开始 ==========\n');
433
774
 
@@ -957,6 +1298,20 @@ export class InteractiveSession {
957
1298
  return;
958
1299
  }
959
1300
 
1301
+ // Check teammate message queue when agent becomes idle
1302
+ if (this.teammateMessageQueue.length > 0) {
1303
+ await this.processMessageQueue();
1304
+ }
1305
+
1306
+ // If running in team mode with initial prompt, send it immediately and skip user input
1307
+ if (this.spawnPrompt) {
1308
+ const prompt = this.spawnPrompt;
1309
+ this.spawnPrompt = null;
1310
+ await this.sendInitialPrompt(prompt);
1311
+ this.promptLoop();
1312
+ return;
1313
+ }
1314
+
960
1315
  // In SDK mode, use a different input loop
961
1316
  if (this.isSdkMode) {
962
1317
  await this.sdkPromptLoop();
@@ -1579,7 +1934,25 @@ export class InteractiveSession {
1579
1934
 
1580
1935
  const toolRegistry = getToolRegistry();
1581
1936
  const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
1582
- const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
1937
+
1938
+ // Build team context if running in team mode
1939
+ const teamContext = this.isTeamMode ? {
1940
+ teamId: this.teamId!,
1941
+ memberId: this.memberId!,
1942
+ memberName: this.memberName!,
1943
+ memberRole: this.memberRole!,
1944
+ leadId: this.leadId || undefined,
1945
+ brokerPort: this.brokerPort || undefined,
1946
+ initialTaskId: this.initialTaskId || undefined,
1947
+ } : undefined;
1948
+
1949
+ const systemPromptGenerator = new SystemPromptGenerator(
1950
+ toolRegistry,
1951
+ this.executionMode,
1952
+ undefined,
1953
+ undefined,
1954
+ teamContext
1955
+ );
1583
1956
  const enhancedSystemPrompt =
1584
1957
  await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
1585
1958
 
@@ -1802,15 +2175,15 @@ export class InteractiveSession {
1802
2175
  }
1803
2176
 
1804
2177
  // Mark that an operation is in progress
1805
- (this as any)._isOperationInProgress = true;
2178
+ this._isOperationInProgress = true;
1806
2179
 
1807
2180
  const thinkingText = colors.textMuted(`Thinking... (Press ESC to cancel)`);
1808
2181
  const icon = colors.primary(icons.brain);
1809
2182
  const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
1810
2183
  let frameIndex = 0;
1811
2184
 
1812
- // SDK 模式下不显示 spinner
1813
- const showThinkingSpinner = !this.isSdkMode;
2185
+ // SDK 模式和 Team 模式下不显示 spinner (team 模式下 teammate 输出会与 spinner 混淆)
2186
+ const showThinkingSpinner = !this.isSdkMode && !this.isTeamMode;
1814
2187
  let spinnerInterval: NodeJS.Timeout | null = null;
1815
2188
 
1816
2189
  // Custom spinner: only icon rotates, text stays static
@@ -1846,11 +2219,24 @@ export class InteractiveSession {
1846
2219
  : toolDefinitions;
1847
2220
 
1848
2221
  const baseSystemPrompt = this.currentAgent?.systemPrompt ?? '';
2222
+
2223
+ // Build team context if running in team mode
2224
+ const teamContext = this.isTeamMode ? {
2225
+ teamId: this.teamId!,
2226
+ memberId: this.memberId!,
2227
+ memberName: this.memberName!,
2228
+ memberRole: this.memberRole!,
2229
+ leadId: this.leadId || undefined,
2230
+ brokerPort: this.brokerPort || undefined,
2231
+ initialTaskId: this.initialTaskId || undefined,
2232
+ } : undefined;
2233
+
1849
2234
  const systemPromptGenerator = new SystemPromptGenerator(
1850
2235
  toolRegistry,
1851
2236
  this.executionMode,
1852
2237
  undefined,
1853
- this.mcpManager
2238
+ this.mcpManager,
2239
+ teamContext
1854
2240
  );
1855
2241
  const enhancedSystemPrompt =
1856
2242
  await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
@@ -1927,13 +2313,13 @@ export class InteractiveSession {
1927
2313
  }
1928
2314
 
1929
2315
  // Operation completed successfully, clear the flag
1930
- (this as any)._isOperationInProgress = false;
2316
+ this._isOperationInProgress = false;
1931
2317
  } catch (error: any) {
1932
2318
  if (spinnerInterval) clearInterval(spinnerInterval);
1933
2319
  process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
1934
2320
 
1935
2321
  // Clear the operation flag
1936
- (this as any)._isOperationInProgress = false;
2322
+ this._isOperationInProgress = false;
1937
2323
 
1938
2324
  // Signal request completion to SDK
1939
2325
  if (this.isSdkMode && this.sdkOutputAdapter && this._currentRequestId) {
@@ -2009,7 +2395,7 @@ export class InteractiveSession {
2009
2395
  }
2010
2396
  } catch (error: any) {
2011
2397
  // Clear the operation flag
2012
- (this as any)._isOperationInProgress = false;
2398
+ this._isOperationInProgress = false;
2013
2399
 
2014
2400
  if (error.message === 'Operation cancelled by user') {
2015
2401
  // Notify backend to cancel the task
@@ -2098,7 +2484,7 @@ export class InteractiveSession {
2098
2484
  onComplete?: () => Promise<void>
2099
2485
  ): Promise<void> {
2100
2486
  // Mark that tool execution is in progress
2101
- (this as any)._isOperationInProgress = true;
2487
+ this._isOperationInProgress = true;
2102
2488
 
2103
2489
  const toolRegistry = getToolRegistry();
2104
2490
  const showToolDetails = this.configManager.get('showToolDetails') || false;
@@ -2177,7 +2563,7 @@ export class InteractiveSession {
2177
2563
  // 清理 conversation 中未完成的 tool_call
2178
2564
  this.cleanupIncompleteToolCalls();
2179
2565
 
2180
- (this as any)._isOperationInProgress = false;
2566
+ this._isOperationInProgress = false;
2181
2567
  return;
2182
2568
  }
2183
2569
 
@@ -2450,7 +2836,7 @@ export class InteractiveSession {
2450
2836
  // If GUI agent was cancelled by user, don't continue generating response
2451
2837
  // This avoids wasting API calls and tokens on cancelled tasks
2452
2838
  if (guiSubagentCancelled) {
2453
- (this as any)._isOperationInProgress = false;
2839
+ this._isOperationInProgress = false;
2454
2840
  return;
2455
2841
  }
2456
2842
 
@@ -2934,3 +3320,25 @@ export function setSingletonSession(session: InteractiveSession): void {
2934
3320
  export function getSingletonSession(): InteractiveSession | null {
2935
3321
  return singletonSession;
2936
3322
  }
3323
+
3324
+ /**
3325
+ * Get teammate message queue info from the singleton session.
3326
+ * Returns queue length.
3327
+ */
3328
+ export function getTeammateMessageQueueInfo(): { length: number } | null {
3329
+ if (!singletonSession) {
3330
+ return null;
3331
+ }
3332
+ return singletonSession.getTeammateMessageQueueInfo();
3333
+ }
3334
+
3335
+ /**
3336
+ * Get and clear all teammate messages from the singleton session.
3337
+ * Returns all pending messages and clears the queue.
3338
+ */
3339
+ export function popTeammateMessages(): ChatMessage[] | null {
3340
+ if (!singletonSession) {
3341
+ return null;
3342
+ }
3343
+ return singletonSession.popTeammateMessages();
3344
+ }
@@ -82,6 +82,7 @@ export class WhitelistChecker {
82
82
  'xml_escape',
83
83
  'InvokeSkill',
84
84
  'task',
85
+ 'Team',
85
86
  ]);
86
87
 
87
88
  /**
@@ -23,18 +23,36 @@ export interface ToolSchema {
23
23
  bestPractices: string[];
24
24
  }
25
25
 
26
+ export interface TeamContext {
27
+ teamId: string;
28
+ memberId: string;
29
+ memberName: string;
30
+ memberRole: string;
31
+ leadId?: string;
32
+ brokerPort?: number;
33
+ initialTaskId?: string;
34
+ }
35
+
26
36
  export class SystemPromptGenerator {
27
37
  private toolRegistry: ToolRegistry;
28
38
  private executionMode: ExecutionMode;
29
39
  private agentConfig?: AgentConfig;
30
40
  private mcpManager?: MCPManager;
41
+ private teamContext?: TeamContext;
31
42
  private cachedEnvironmentInfo: string;
32
43
 
33
- constructor(toolRegistry: ToolRegistry, executionMode: ExecutionMode, agentConfig?: AgentConfig, mcpManager?: MCPManager) {
44
+ constructor(
45
+ toolRegistry: ToolRegistry,
46
+ executionMode: ExecutionMode,
47
+ agentConfig?: AgentConfig,
48
+ mcpManager?: MCPManager,
49
+ teamContext?: TeamContext
50
+ ) {
34
51
  this.toolRegistry = toolRegistry;
35
52
  this.executionMode = executionMode;
36
53
  this.agentConfig = agentConfig;
37
54
  this.mcpManager = mcpManager;
55
+ this.teamContext = teamContext;
38
56
  this.cachedEnvironmentInfo = this.generateEnvironmentInfo();
39
57
  }
40
58
 
@@ -92,6 +110,41 @@ export class SystemPromptGenerator {
92
110
  - **Current Date**: ${currentDate} (Timezone: ${timeZone})${psSyntaxRules}`;
93
111
  }
94
112
 
113
+ /**
114
+ * Generate team context information for teammate agents
115
+ */
116
+ private generateTeamContextInfo(): string {
117
+ if (!this.teamContext) return '';
118
+
119
+ const { teamId, memberId, memberName, memberRole, leadId, initialTaskId } = this.teamContext;
120
+
121
+ let teamInfo = `
122
+
123
+ ## Team Context Information
124
+
125
+ You are running as a **${memberRole}** in a team.
126
+
127
+ ### Your Identity
128
+ - **Team ID**: ${teamId}
129
+ - **Your Member ID**: ${memberId}
130
+ - **Your Name**: ${memberName}
131
+ - **Your Role**: ${memberRole}`;
132
+
133
+ if (leadId) {
134
+ teamInfo += `
135
+ - **Lead Member ID**: ${leadId}`;
136
+ }
137
+
138
+ if (initialTaskId) {
139
+ teamInfo += `
140
+ - **Initial Task ID**: ${initialTaskId}`;
141
+ }
142
+
143
+ teamInfo += `**Important**: Use \`team(team_action="get_status", team_id="${teamId}")\` to get the complete team status including lead_id and all other members before starting your work.`;
144
+
145
+ return teamInfo;
146
+ }
147
+
95
148
  async generateEnhancedSystemPrompt(baseSystemPrompt: string): Promise<string> {
96
149
  let localTools = this.toolRegistry.getAll().filter(
97
150
  tool => tool.allowedModes.includes(this.executionMode)
@@ -111,6 +164,11 @@ export class SystemPromptGenerator {
111
164
  // Add system environment information (cached in constructor)
112
165
  enhancedPrompt += this.cachedEnvironmentInfo;
113
166
 
167
+ // Add team context information if running in team mode
168
+ if (this.teamContext) {
169
+ enhancedPrompt += this.generateTeamContextInfo();
170
+ }
171
+
114
172
  // Only add tool-related content if tools are available
115
173
  if (allAvailableTools.length > 0) {
116
174
  // Pre-load skills for dynamic generation
@@ -140,31 +198,6 @@ ${executionStrategy}
140
198
  - Use tools efficiently - avoid redundant calls
141
199
  - When in doubt, ask the user for clarification
142
200
  - Maintain context across tool calls to build a coherent solution`;
143
- } else {
144
- // No tools available - explicitly tell the AI not to use tools
145
- enhancedPrompt += `
146
-
147
- ## IMPORTANT: READ-ONLY MODE
148
-
149
- You are in DEFAULT mode (read-only mode). You CANNOT use any tools or functions.
150
-
151
- STRICT PROHIBITIONS:
152
- - DO NOT attempt to call any functions, tools, or commands
153
- - DO NOT output any tool call syntax, such as:
154
- - <function_calls>...</function_calls>
155
- - ToolName(params)
156
- - Function call format
157
- - Any similar syntax
158
- - DO NOT simulate tool calls or pretend to use tools
159
- - DO NOT output code that would execute tools
160
-
161
- REQUIRED BEHAVIOR:
162
- - Respond ONLY with plain text
163
- - Answer questions based on your knowledge
164
- - If you need to read files or perform actions, ask the user to switch modes
165
- - Tell the user they can use "/mode yolo" or "/mode accept_edits" to enable tools
166
-
167
- Remember: You are in a conversational mode, not a tool-execution mode. Just talk to the user!`;
168
201
  }
169
202
 
170
203
  return enhancedPrompt;