@compilr-dev/sdk 0.1.28 → 0.2.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 (39) hide show
  1. package/dist/index.d.ts +6 -2
  2. package/dist/index.js +27 -1
  3. package/dist/team/activity.d.ts +21 -0
  4. package/dist/team/activity.js +34 -0
  5. package/dist/team/agent-selection.d.ts +53 -0
  6. package/dist/team/agent-selection.js +88 -0
  7. package/dist/team/artifacts.d.ts +175 -0
  8. package/dist/team/artifacts.js +279 -0
  9. package/dist/team/collision-utils.d.ts +16 -0
  10. package/dist/team/collision-utils.js +28 -0
  11. package/dist/team/context-resolver.d.ts +97 -0
  12. package/dist/team/context-resolver.js +322 -0
  13. package/dist/team/custom-agents.d.ts +68 -0
  14. package/dist/team/custom-agents.js +150 -0
  15. package/dist/team/delegation-tracker.d.ts +147 -0
  16. package/dist/team/delegation-tracker.js +215 -0
  17. package/dist/team/index.d.ts +34 -0
  18. package/dist/team/index.js +30 -0
  19. package/dist/team/interfaces.d.ts +36 -0
  20. package/dist/team/interfaces.js +7 -0
  21. package/dist/team/mention-parser.d.ts +64 -0
  22. package/dist/team/mention-parser.js +138 -0
  23. package/dist/team/shared-context.d.ts +293 -0
  24. package/dist/team/shared-context.js +673 -0
  25. package/dist/team/skill-requirements.d.ts +66 -0
  26. package/dist/team/skill-requirements.js +178 -0
  27. package/dist/team/task-assignment.d.ts +69 -0
  28. package/dist/team/task-assignment.js +123 -0
  29. package/dist/team/task-suggestion.d.ts +31 -0
  30. package/dist/team/task-suggestion.js +84 -0
  31. package/dist/team/team-agent.d.ts +201 -0
  32. package/dist/team/team-agent.js +492 -0
  33. package/dist/team/team.d.ts +297 -0
  34. package/dist/team/team.js +615 -0
  35. package/dist/team/tool-config.d.ts +110 -0
  36. package/dist/team/tool-config.js +739 -0
  37. package/dist/team/types.d.ts +211 -0
  38. package/dist/team/types.js +638 -0
  39. package/package.json +1 -1
@@ -0,0 +1,615 @@
1
+ /**
2
+ * AgentTeam - Multi-agent orchestration
3
+ *
4
+ * Manages a team of persistent agents:
5
+ * - Agent switching ($name prefix)
6
+ * - Isolated contexts per agent
7
+ * - Team-level persistence
8
+ * - Event notifications
9
+ */
10
+ import { TeamAgent } from './team-agent.js';
11
+ import { ROLE_EXPERTISE } from './types.js';
12
+ import { SharedContextManager } from './shared-context.js';
13
+ import { ArtifactStore } from './artifacts.js';
14
+ import { resolveAgentIdCollision } from './collision-utils.js';
15
+ /**
16
+ * AgentTeam orchestrates multiple persistent agents
17
+ */
18
+ export class AgentTeam {
19
+ /**
20
+ * Team name
21
+ */
22
+ name;
23
+ /**
24
+ * Map of agent ID to TeamAgent
25
+ */
26
+ agents = new Map();
27
+ /**
28
+ * Currently active agent ID
29
+ */
30
+ _activeAgentId = 'default';
31
+ /**
32
+ * Coordinator agent ID (optional)
33
+ */
34
+ _coordinatorId;
35
+ /**
36
+ * Factory for creating agents
37
+ */
38
+ agentFactory;
39
+ /**
40
+ * Event handler
41
+ */
42
+ onEvent;
43
+ /**
44
+ * Shared context manager for cross-agent knowledge sharing
45
+ */
46
+ _sharedContext;
47
+ /**
48
+ * Artifact store for team artifacts
49
+ */
50
+ _artifactStore;
51
+ /**
52
+ * Optional session registry for collision detection
53
+ */
54
+ _sessionRegistry;
55
+ /**
56
+ * Track handoff source: targetAgentId → sourceAgentId
57
+ * Used for one-hop prevention (agents that were handed a task
58
+ * cannot re-hand it off, except back to the coordinator)
59
+ */
60
+ _handedFrom = new Map();
61
+ /**
62
+ * Creation timestamp
63
+ */
64
+ createdAt = new Date();
65
+ /**
66
+ * Last updated timestamp
67
+ */
68
+ updatedAt = new Date();
69
+ constructor(config) {
70
+ this.name = config.name ?? 'Team';
71
+ this.agentFactory = config.agentFactory;
72
+ this.onEvent = config.onEvent;
73
+ this._sessionRegistry = config.sessionRegistry;
74
+ // Initialize shared context (use provided or create new)
75
+ this._sharedContext = config.sharedContext ?? new SharedContextManager();
76
+ // Initialize artifact store (use provided or create in-memory)
77
+ this._artifactStore = config.artifactStore ?? new ArtifactStore();
78
+ // Create default agent
79
+ const defaultAgent = TeamAgent.fromRole('default', 'default');
80
+ this.agents.set('default', defaultAgent);
81
+ // Add any initial agents
82
+ if (config.initialAgents) {
83
+ for (const agentConfig of config.initialAgents) {
84
+ if (agentConfig.id !== 'default') {
85
+ this.agents.set(agentConfig.id, new TeamAgent(agentConfig));
86
+ }
87
+ }
88
+ }
89
+ // Update shared context with team roster (simple list)
90
+ this._sharedContext.setTeam(Array.from(this.agents.keys()), this._activeAgentId);
91
+ // Build full team roster for team awareness
92
+ this.updateTeamRoster();
93
+ }
94
+ /**
95
+ * Get the currently active agent ID
96
+ */
97
+ get activeAgentId() {
98
+ return this._activeAgentId;
99
+ }
100
+ /**
101
+ * Get the coordinator agent ID (if set)
102
+ */
103
+ get coordinatorId() {
104
+ return this._coordinatorId;
105
+ }
106
+ /**
107
+ * Get the shared context manager
108
+ */
109
+ get sharedContext() {
110
+ return this._sharedContext;
111
+ }
112
+ /**
113
+ * Get the artifact store
114
+ */
115
+ get artifactStore() {
116
+ return this._artifactStore;
117
+ }
118
+ /**
119
+ * Get the currently active TeamAgent
120
+ */
121
+ getActive() {
122
+ const agent = this.agents.get(this._activeAgentId);
123
+ if (!agent) {
124
+ // Fallback to default
125
+ const defaultAgent = this.agents.get('default');
126
+ if (!defaultAgent) {
127
+ throw new Error('Default agent not found - this should never happen');
128
+ }
129
+ return defaultAgent;
130
+ }
131
+ return agent;
132
+ }
133
+ /**
134
+ * Get the currently active Agent instance
135
+ * Returns null if not initialized
136
+ */
137
+ getActiveAgent() {
138
+ return this.getActive().agent;
139
+ }
140
+ /**
141
+ * Get a specific TeamAgent by ID
142
+ */
143
+ get(id) {
144
+ return this.agents.get(id);
145
+ }
146
+ /**
147
+ * Set the agent instance for the default TeamAgent
148
+ * Used when the agent is created before the team
149
+ */
150
+ setDefaultAgent(agent) {
151
+ const defaultTeamAgent = this.agents.get('default');
152
+ if (defaultTeamAgent) {
153
+ defaultTeamAgent.setAgent(agent);
154
+ }
155
+ }
156
+ /**
157
+ * Check if an agent exists in the team
158
+ */
159
+ has(id) {
160
+ return this.agents.has(id);
161
+ }
162
+ /**
163
+ * Get all agent IDs
164
+ */
165
+ getAgentIds() {
166
+ return Array.from(this.agents.keys());
167
+ }
168
+ /**
169
+ * Get all TeamAgents
170
+ */
171
+ getAll() {
172
+ return Array.from(this.agents.values());
173
+ }
174
+ /**
175
+ * Get team size (number of agents)
176
+ */
177
+ get size() {
178
+ return this.agents.size;
179
+ }
180
+ /**
181
+ * Add a new agent to the team
182
+ */
183
+ async addAgent(config) {
184
+ if (this.agents.has(config.id)) {
185
+ throw new Error(`Agent '${config.id}' already exists in team`);
186
+ }
187
+ const teamAgent = new TeamAgent(config);
188
+ this.agents.set(config.id, teamAgent);
189
+ // Initialize the agent with shared context
190
+ await teamAgent.initialize(this.agentFactory, this._sharedContext);
191
+ // Update shared context roster
192
+ this._sharedContext.addAgent(config.id);
193
+ // Update full team roster for team awareness
194
+ this.updateTeamRoster();
195
+ this.updatedAt = new Date();
196
+ this.emit({ type: 'agent:added', agentId: config.id });
197
+ return teamAgent;
198
+ }
199
+ /**
200
+ * Add an agent from a predefined role
201
+ * @param role - The predefined role
202
+ * @param id - Optional custom ID (defaults to role name)
203
+ * @param modelTier - Optional model tier override (defaults to role's default tier)
204
+ */
205
+ async addAgentFromRole(role, id, modelTier) {
206
+ let agentId = id ?? role;
207
+ // Cross-terminal collision check (if registry provided)
208
+ if (this._sessionRegistry) {
209
+ const globalIds = this._sessionRegistry.getGlobalAgentIds();
210
+ const localIds = Array.from(this.agents.keys());
211
+ const resolved = resolveAgentIdCollision(agentId, localIds, globalIds);
212
+ if (resolved.wasCollision) {
213
+ const originalId = agentId;
214
+ agentId = resolved.id;
215
+ this.emit({ type: 'agent:collision', originalId, agentId: resolved.id });
216
+ }
217
+ }
218
+ if (this.agents.has(agentId)) {
219
+ throw new Error(`Agent '${agentId}' already exists in team`);
220
+ }
221
+ const teamAgent = TeamAgent.fromRole(role, agentId, modelTier);
222
+ this.agents.set(agentId, teamAgent);
223
+ // Initialize the agent with shared context
224
+ await teamAgent.initialize(this.agentFactory, this._sharedContext);
225
+ // Update shared context roster
226
+ this._sharedContext.addAgent(agentId);
227
+ // Update full team roster for team awareness
228
+ this.updateTeamRoster();
229
+ this.updatedAt = new Date();
230
+ this.emit({ type: 'agent:added', agentId });
231
+ return teamAgent;
232
+ }
233
+ /**
234
+ * Add a custom agent from a CustomAgentDefinition
235
+ */
236
+ async addCustomAgent(def) {
237
+ let agentId = def.id;
238
+ // Cross-terminal collision check (if registry provided)
239
+ if (this._sessionRegistry) {
240
+ const globalIds = this._sessionRegistry.getGlobalAgentIds();
241
+ const localIds = Array.from(this.agents.keys());
242
+ const resolved = resolveAgentIdCollision(agentId, localIds, globalIds);
243
+ if (resolved.wasCollision) {
244
+ const originalId = agentId;
245
+ agentId = resolved.id;
246
+ def = { ...def, id: agentId };
247
+ this.emit({ type: 'agent:collision', originalId, agentId: resolved.id });
248
+ }
249
+ }
250
+ if (this.agents.has(agentId)) {
251
+ throw new Error(`Agent '${agentId}' already exists in team`);
252
+ }
253
+ const teamAgent = TeamAgent.fromCustomDefinition(def);
254
+ this.agents.set(agentId, teamAgent);
255
+ // Initialize the agent with shared context
256
+ await teamAgent.initialize(this.agentFactory, this._sharedContext);
257
+ // Update shared context roster
258
+ this._sharedContext.addAgent(def.id);
259
+ // Update full team roster for team awareness
260
+ this.updateTeamRoster();
261
+ this.updatedAt = new Date();
262
+ this.emit({ type: 'agent:added', agentId: def.id });
263
+ return teamAgent;
264
+ }
265
+ /**
266
+ * Remove an agent from the team
267
+ */
268
+ removeAgent(id) {
269
+ if (id === 'default') {
270
+ throw new Error('Cannot remove the default agent');
271
+ }
272
+ if (!this.agents.has(id)) {
273
+ return false;
274
+ }
275
+ // If removing active agent, switch to default
276
+ if (this._activeAgentId === id) {
277
+ this._activeAgentId = 'default';
278
+ this._sharedContext.setActiveAgent('default');
279
+ }
280
+ // If removing coordinator, clear coordinator
281
+ if (this._coordinatorId === id) {
282
+ this._coordinatorId = undefined;
283
+ }
284
+ this.agents.delete(id);
285
+ // Update shared context roster
286
+ this._sharedContext.removeAgent(id);
287
+ // Update full team roster for team awareness
288
+ this.updateTeamRoster();
289
+ this.updatedAt = new Date();
290
+ this.emit({ type: 'agent:removed', agentId: id });
291
+ return true;
292
+ }
293
+ /**
294
+ * Clear conversation history for all agents in the team
295
+ * Used by /reset command to start fresh
296
+ */
297
+ clearAllHistories() {
298
+ for (const agent of this.agents.values()) {
299
+ agent.clearHistory();
300
+ }
301
+ // Clear shared context activity feed
302
+ this._sharedContext.clearActivity();
303
+ this.updatedAt = new Date();
304
+ this.emit({ type: 'team:reset' });
305
+ }
306
+ /**
307
+ * Callback for checking in-progress tasks when switching agents
308
+ * Set by the consumer to provide task counts for warnings
309
+ */
310
+ _getInProgressTasksCallback;
311
+ /**
312
+ * Set the callback for getting in-progress tasks
313
+ * Used by consumer to integrate with todo list and work items
314
+ */
315
+ setInProgressTasksCallback(callback) {
316
+ this._getInProgressTasksCallback = callback;
317
+ }
318
+ /**
319
+ * Switch to a different agent
320
+ */
321
+ async switchTo(id) {
322
+ const agent = this.agents.get(id);
323
+ if (!agent) {
324
+ throw new Error(`Agent '${id}' not found in team`);
325
+ }
326
+ // Initialize if needed
327
+ if (!agent.isInitialized) {
328
+ await agent.initialize(this.agentFactory, this._sharedContext);
329
+ }
330
+ const previousId = this._activeAgentId;
331
+ // Check for in-progress tasks owned by the outgoing agent
332
+ if (previousId !== id && this._getInProgressTasksCallback) {
333
+ const inProgressTasks = this._getInProgressTasksCallback(previousId);
334
+ const hasTodos = inProgressTasks.todos.length > 0;
335
+ const hasWorkItems = inProgressTasks.workItems.length > 0;
336
+ if (hasTodos || hasWorkItems) {
337
+ this.emit({
338
+ type: 'agent:switch_warning',
339
+ agentId: id,
340
+ previousAgentId: previousId,
341
+ message: `Agent "${previousId}" has in-progress tasks: ${hasTodos ? `${String(inProgressTasks.todos.length)} todo(s)` : ''}${hasTodos && hasWorkItems ? ', ' : ''}${hasWorkItems ? `${String(inProgressTasks.workItems.length)} work item(s)` : ''}`,
342
+ });
343
+ }
344
+ }
345
+ this._activeAgentId = id;
346
+ this.updatedAt = new Date();
347
+ // Update shared context active agent
348
+ this._sharedContext.setActiveAgent(id);
349
+ // Update roster active agent for team awareness
350
+ this._sharedContext.setRosterActiveAgent(id);
351
+ this.emit({
352
+ type: 'agent:switched',
353
+ agentId: id,
354
+ previousAgentId: previousId,
355
+ });
356
+ return agent;
357
+ }
358
+ /**
359
+ * Set the coordinator agent
360
+ */
361
+ setCoordinator(id) {
362
+ if (id !== undefined && !this.agents.has(id)) {
363
+ throw new Error(`Agent '${id}' not found in team`);
364
+ }
365
+ this._coordinatorId = id;
366
+ this.updatedAt = new Date();
367
+ }
368
+ /**
369
+ * Check if an agent is the coordinator
370
+ * The default agent is considered coordinator if no explicit coordinator is set.
371
+ */
372
+ isCoordinator(id) {
373
+ if (this._coordinatorId !== undefined) {
374
+ return this._coordinatorId === id;
375
+ }
376
+ // If no coordinator explicitly set, default agent is the coordinator
377
+ return id === 'default';
378
+ }
379
+ /**
380
+ * Check if the active agent is the coordinator
381
+ * The default agent is considered coordinator if no explicit coordinator is set.
382
+ */
383
+ isActiveCoordinator() {
384
+ if (this._coordinatorId !== undefined) {
385
+ return this._coordinatorId === this._activeAgentId;
386
+ }
387
+ // If no coordinator explicitly set, default agent is the coordinator
388
+ return this._activeAgentId === 'default';
389
+ }
390
+ /**
391
+ * Change an agent's model tier
392
+ * This updates the tier and reinitializes the agent with the new model.
393
+ * Conversation history is preserved.
394
+ *
395
+ * @param id - The agent ID
396
+ * @param newTier - The new model tier
397
+ * @returns The updated TeamAgent
398
+ */
399
+ async changeAgentTier(id, newTier) {
400
+ const agent = this.agents.get(id);
401
+ if (!agent) {
402
+ throw new Error(`Agent '${id}' not found in team`);
403
+ }
404
+ // Set the new tier (this preserves state and clears the agent instance)
405
+ agent.setModelTier(newTier);
406
+ // Reinitialize with the new tier
407
+ await agent.initialize(this.agentFactory, this._sharedContext);
408
+ this.updatedAt = new Date();
409
+ this.emit({ type: 'agent:switched', agentId: id, previousAgentId: id });
410
+ return agent;
411
+ }
412
+ /**
413
+ * Initialize all agents (lazy initialization)
414
+ */
415
+ async initializeAll() {
416
+ for (const agent of this.agents.values()) {
417
+ if (!agent.isInitialized) {
418
+ await agent.initialize(this.agentFactory, this._sharedContext);
419
+ }
420
+ }
421
+ }
422
+ /**
423
+ * Initialize only the active agent
424
+ */
425
+ async initializeActive() {
426
+ const teamAgent = this.getActive();
427
+ if (!teamAgent.isInitialized) {
428
+ await teamAgent.initialize(this.agentFactory, this._sharedContext);
429
+ }
430
+ const agent = teamAgent.agent;
431
+ if (!agent) {
432
+ throw new Error('Agent not initialized after initialize() call');
433
+ }
434
+ return agent;
435
+ }
436
+ /**
437
+ * Parse a message for $agent prefix and switch if needed
438
+ * Returns the message without the prefix and whether a switch occurred
439
+ */
440
+ async parseAndSwitch(message) {
441
+ const match = message.match(/^\$(\w+)\s*(.*)/s);
442
+ if (!match) {
443
+ return { message, switched: false, agentId: this._activeAgentId };
444
+ }
445
+ const [, agentId, remainingMessage] = match;
446
+ // Check if agent exists
447
+ if (!this.agents.has(agentId)) {
448
+ // Return original message - let the agent handle invalid prefix
449
+ return { message, switched: false, agentId: this._activeAgentId };
450
+ }
451
+ // Switch to the agent
452
+ await this.switchTo(agentId);
453
+ return {
454
+ message: remainingMessage.trim() || message, // Keep original if no remaining message
455
+ switched: true,
456
+ agentId,
457
+ };
458
+ }
459
+ /**
460
+ * Get autocomplete suggestions for $agent prefix
461
+ */
462
+ getAgentSuggestions(prefix) {
463
+ const results = [];
464
+ const searchPrefix = prefix.toLowerCase();
465
+ for (const [id, agent] of this.agents) {
466
+ if (id.toLowerCase().startsWith(searchPrefix)) {
467
+ results.push({
468
+ id,
469
+ label: agent.getFullLabel(),
470
+ });
471
+ }
472
+ }
473
+ return results;
474
+ }
475
+ // ---------------------------------------------------------------------------
476
+ // Handoff Tracking (One-Hop Prevention)
477
+ // ---------------------------------------------------------------------------
478
+ /**
479
+ * Record that sourceAgent handed off to targetAgent.
480
+ * Used for one-hop prevention.
481
+ */
482
+ recordHandoff(targetAgentId, sourceAgentId) {
483
+ this._handedFrom.set(targetAgentId, sourceAgentId);
484
+ }
485
+ /**
486
+ * Check if an agent was handed a task (i.e., is a handoff target).
487
+ * If true, this agent cannot re-hand off (except back to coordinator).
488
+ */
489
+ wasHandedTo(agentId) {
490
+ return this._handedFrom.has(agentId);
491
+ }
492
+ /**
493
+ * Get the source agent that handed off to the given agent.
494
+ */
495
+ getHandoffSource(agentId) {
496
+ return this._handedFrom.get(agentId);
497
+ }
498
+ /**
499
+ * Clear handoff tracking for an agent (after it completes its task).
500
+ */
501
+ clearHandoffRecord(agentId) {
502
+ this._handedFrom.delete(agentId);
503
+ }
504
+ /**
505
+ * Serialize the team for persistence
506
+ */
507
+ serialize() {
508
+ const agents = [];
509
+ for (const agent of this.agents.values()) {
510
+ agents.push(agent.serialize());
511
+ }
512
+ return {
513
+ metadata: {
514
+ name: this.name,
515
+ activeAgentId: this._activeAgentId,
516
+ coordinatorId: this._coordinatorId,
517
+ agentIds: Array.from(this.agents.keys()),
518
+ createdAt: this.createdAt.toISOString(),
519
+ updatedAt: this.updatedAt.toISOString(),
520
+ },
521
+ agents,
522
+ };
523
+ }
524
+ /**
525
+ * Restore team state from serialized data
526
+ */
527
+ static restore(data, agentFactory, onEvent, options) {
528
+ const team = new AgentTeam({
529
+ name: data.metadata.name,
530
+ agentFactory,
531
+ onEvent,
532
+ sharedContext: options?.sharedContext,
533
+ artifactStore: options?.artifactStore,
534
+ sessionRegistry: options?.sessionRegistry,
535
+ });
536
+ // Clear default agent if not in serialized data
537
+ if (!data.metadata.agentIds.includes('default')) {
538
+ team.agents.delete('default');
539
+ }
540
+ // Restore agents
541
+ for (const agentData of data.agents) {
542
+ const teamAgent = TeamAgent.fromSerialized(agentData);
543
+ team.agents.set(agentData.id, teamAgent);
544
+ }
545
+ // Restore metadata
546
+ team._activeAgentId = data.metadata.activeAgentId;
547
+ team._coordinatorId = data.metadata.coordinatorId;
548
+ // Update shared context with restored team roster (simple list)
549
+ team._sharedContext.setTeam(Array.from(team.agents.keys()), team._activeAgentId);
550
+ // Build full team roster for team awareness
551
+ team.updateTeamRoster();
552
+ team.emit({ type: 'team:restored' });
553
+ return team;
554
+ }
555
+ /**
556
+ * Emit a team event
557
+ */
558
+ emit(event) {
559
+ this.onEvent?.(event);
560
+ }
561
+ // ---------------------------------------------------------------------------
562
+ // Team Awareness - Roster Management
563
+ // ---------------------------------------------------------------------------
564
+ /**
565
+ * Build and update the team roster in shared context
566
+ * Called when team composition or active agent changes
567
+ */
568
+ updateTeamRoster() {
569
+ const roster = this.buildTeamRoster();
570
+ this._sharedContext.updateTeamRoster(roster);
571
+ // Push updated roster to all initialized agents via anchors.
572
+ // Anchors are re-injected on every LLM call, so agents always see the latest roster.
573
+ if (this._sharedContext.hasTeamRoster()) {
574
+ const rosterContent = this._sharedContext.formatTeamRoster();
575
+ for (const [, teamAgent] of this.agents) {
576
+ const agent = teamAgent.agent;
577
+ if (agent && agent.hasAnchors()) {
578
+ agent.addAnchor({
579
+ id: 'team-roster',
580
+ content: rosterContent,
581
+ priority: 'info',
582
+ scope: 'session',
583
+ });
584
+ }
585
+ }
586
+ }
587
+ }
588
+ /**
589
+ * Build the team roster from current agents
590
+ */
591
+ buildTeamRoster() {
592
+ const roster = [];
593
+ for (const [id, agent] of this.agents) {
594
+ const role = agent.role;
595
+ // For custom agents, derive expertise from their specialty/description
596
+ // since ROLE_EXPERTISE['custom'] is empty
597
+ let expertise = ROLE_EXPERTISE[role];
598
+ if (role === 'custom' && expertise.length === 0 && agent.description) {
599
+ expertise = agent.description
600
+ .split(',')
601
+ .map((s) => s.trim())
602
+ .filter(Boolean);
603
+ }
604
+ roster.push({
605
+ id,
606
+ displayName: agent.displayName,
607
+ mascot: agent.mascot,
608
+ role,
609
+ expertise,
610
+ isActive: id === this._activeAgentId,
611
+ });
612
+ }
613
+ return roster;
614
+ }
615
+ }