@compilr-dev/sdk 0.1.27 → 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 (40) hide show
  1. package/dist/index.d.ts +6 -2
  2. package/dist/index.js +27 -1
  3. package/dist/meta-tools/registry.js +4 -2
  4. package/dist/team/activity.d.ts +21 -0
  5. package/dist/team/activity.js +34 -0
  6. package/dist/team/agent-selection.d.ts +53 -0
  7. package/dist/team/agent-selection.js +88 -0
  8. package/dist/team/artifacts.d.ts +175 -0
  9. package/dist/team/artifacts.js +279 -0
  10. package/dist/team/collision-utils.d.ts +16 -0
  11. package/dist/team/collision-utils.js +28 -0
  12. package/dist/team/context-resolver.d.ts +97 -0
  13. package/dist/team/context-resolver.js +322 -0
  14. package/dist/team/custom-agents.d.ts +68 -0
  15. package/dist/team/custom-agents.js +150 -0
  16. package/dist/team/delegation-tracker.d.ts +147 -0
  17. package/dist/team/delegation-tracker.js +215 -0
  18. package/dist/team/index.d.ts +34 -0
  19. package/dist/team/index.js +30 -0
  20. package/dist/team/interfaces.d.ts +36 -0
  21. package/dist/team/interfaces.js +7 -0
  22. package/dist/team/mention-parser.d.ts +64 -0
  23. package/dist/team/mention-parser.js +138 -0
  24. package/dist/team/shared-context.d.ts +293 -0
  25. package/dist/team/shared-context.js +673 -0
  26. package/dist/team/skill-requirements.d.ts +66 -0
  27. package/dist/team/skill-requirements.js +178 -0
  28. package/dist/team/task-assignment.d.ts +69 -0
  29. package/dist/team/task-assignment.js +123 -0
  30. package/dist/team/task-suggestion.d.ts +31 -0
  31. package/dist/team/task-suggestion.js +84 -0
  32. package/dist/team/team-agent.d.ts +201 -0
  33. package/dist/team/team-agent.js +492 -0
  34. package/dist/team/team.d.ts +297 -0
  35. package/dist/team/team.js +615 -0
  36. package/dist/team/tool-config.d.ts +110 -0
  37. package/dist/team/tool-config.js +739 -0
  38. package/dist/team/types.d.ts +211 -0
  39. package/dist/team/types.js +638 -0
  40. package/package.json +1 -1
@@ -0,0 +1,201 @@
1
+ /**
2
+ * TeamAgent - Wrapper for persistent team agents
3
+ *
4
+ * Each team agent has:
5
+ * - Unique identity (id, name, mascot)
6
+ * - Isolated conversation history
7
+ * - Role-specific system prompt additions
8
+ * - Optional tool filtering
9
+ */
10
+ import type { Agent, AgentConfig, ContextManager } from '@compilr-dev/agents';
11
+ import type { TeamAgentConfig, SerializedTeamAgent, MascotExpression, AgentRole } from './types.js';
12
+ import type { ModelTier } from '../models/index.js';
13
+ import type { ProviderType } from '../config.js';
14
+ import type { SharedContextManager } from './shared-context.js';
15
+ import type { CustomAgentDefinition } from './custom-agents.js';
16
+ /**
17
+ * TeamAgent wraps an Agent instance with team-specific metadata
18
+ */
19
+ export declare class TeamAgent {
20
+ /**
21
+ * Unique identifier (e.g., 'pm', 'arch', 'qa')
22
+ */
23
+ readonly id: string;
24
+ /**
25
+ * Display name shown in UI
26
+ */
27
+ readonly displayName: string;
28
+ /**
29
+ * Mascot expression for visual differentiation
30
+ */
31
+ readonly mascot: MascotExpression;
32
+ /**
33
+ * Role type
34
+ */
35
+ readonly role: AgentRole;
36
+ /**
37
+ * Optional description
38
+ */
39
+ readonly description?: string;
40
+ /**
41
+ * System prompt addition specific to this agent
42
+ */
43
+ readonly systemPromptAddition?: string;
44
+ /**
45
+ * Use minimal system prompt - skip all modules
46
+ */
47
+ readonly useMinimalSystemPrompt?: boolean;
48
+ /**
49
+ * Skip all tool registration - agent has no tools
50
+ */
51
+ readonly noTools?: boolean;
52
+ /**
53
+ * Tool filter (tool names this agent can use)
54
+ */
55
+ readonly toolFilter?: string[];
56
+ /**
57
+ * Tool profile name (for display)
58
+ */
59
+ readonly toolProfile?: TeamAgentConfig['toolProfile'];
60
+ /**
61
+ * Enabled skills (empty = all skills)
62
+ */
63
+ readonly enabledSkills?: string[];
64
+ /**
65
+ * Auto-approve handoffs from this agent without user confirmation
66
+ */
67
+ readonly autoApproveHandoff?: boolean;
68
+ /**
69
+ * Model tier (fast/balanced/powerful)
70
+ * Determines which model to use based on provider
71
+ */
72
+ private _modelTier?;
73
+ /**
74
+ * The underlying agent instance
75
+ */
76
+ private _agent;
77
+ /**
78
+ * Agent configuration for lazy initialization
79
+ */
80
+ private readonly agentConfig;
81
+ /**
82
+ * Stored agent state (for persistence before agent is created)
83
+ */
84
+ private storedState?;
85
+ /**
86
+ * Token usage tracking
87
+ */
88
+ private _tokenUsage;
89
+ /**
90
+ * Last activity timestamp
91
+ */
92
+ private _lastActivity;
93
+ constructor(config: TeamAgentConfig);
94
+ /**
95
+ * Get the underlying agent instance
96
+ */
97
+ get agent(): Agent | null;
98
+ /**
99
+ * Check if agent is initialized
100
+ */
101
+ get isInitialized(): boolean;
102
+ /**
103
+ * Get token usage
104
+ */
105
+ get tokenUsage(): {
106
+ input: number;
107
+ output: number;
108
+ };
109
+ /**
110
+ * Get last activity timestamp
111
+ */
112
+ get lastActivity(): Date;
113
+ /**
114
+ * Get model tier
115
+ */
116
+ get modelTier(): ModelTier | undefined;
117
+ /**
118
+ * Change the model tier
119
+ * This preserves conversation history but strips thinking blocks
120
+ * (which contain provider-specific signatures that don't transfer).
121
+ *
122
+ * @param newTier - The new model tier to use
123
+ */
124
+ setModelTier(newTier: ModelTier): void;
125
+ /**
126
+ * Initialize the agent with the given factory
127
+ * This allows the team to control agent creation
128
+ *
129
+ * @param agentFactory Factory function to create the agent
130
+ * @param sharedContext Optional shared context to inject into system prompt
131
+ */
132
+ initialize(agentFactory: (config: Partial<AgentConfig>, systemPromptAddition?: string, useMinimalSystemPrompt?: boolean, noTools?: boolean, toolFilter?: string[], modelTier?: ModelTier) => Promise<Agent>, sharedContext?: SharedContextManager): Promise<void>;
133
+ /**
134
+ * Set the agent instance directly
135
+ * Used for the default agent when an agent is created before the team
136
+ */
137
+ setAgent(agent: Agent): void;
138
+ /**
139
+ * Update token usage after a turn
140
+ */
141
+ updateTokenUsage(input: number, output: number): void;
142
+ /**
143
+ * Reset token usage (e.g., after compaction)
144
+ */
145
+ resetTokenUsage(): void;
146
+ /**
147
+ * Clear conversation history (for /reset command)
148
+ * Clears both the underlying agent and stored state
149
+ */
150
+ clearHistory(): void;
151
+ /**
152
+ * Get context manager from the agent (if available)
153
+ */
154
+ getContextManager(): ContextManager | undefined;
155
+ /**
156
+ * Get context utilization percentage
157
+ */
158
+ getContextUtilization(): number;
159
+ /**
160
+ * Get the max context window for this agent's model.
161
+ * Resolves: modelTier → modelId → contextWindow from MODEL_REGISTRY.
162
+ */
163
+ getMaxContextTokens(provider: ProviderType): number;
164
+ /**
165
+ * Get context stats (works even when agent is not initialized)
166
+ * Returns data from either the live agent or stored state.
167
+ * Includes maxTokens for per-agent context window display.
168
+ */
169
+ getContextStats(provider?: ProviderType): {
170
+ messageCount: number;
171
+ estimatedTokens: number;
172
+ maxTokens: number;
173
+ };
174
+ /**
175
+ * Serialize the team agent for persistence
176
+ */
177
+ serialize(): SerializedTeamAgent;
178
+ /**
179
+ * Create a TeamAgent from serialized state
180
+ */
181
+ static fromSerialized(data: SerializedTeamAgent): TeamAgent;
182
+ /**
183
+ * Create a TeamAgent from a predefined role
184
+ * @param role - The predefined role
185
+ * @param id - Optional custom ID (defaults to role name)
186
+ * @param modelTier - Optional model tier override (defaults to role's default tier)
187
+ */
188
+ static fromRole(role: AgentRole, id?: string, modelTier?: ModelTier): TeamAgent;
189
+ /**
190
+ * Create a TeamAgent from a custom agent definition
191
+ */
192
+ static fromCustomDefinition(def: CustomAgentDefinition): TeamAgent;
193
+ /**
194
+ * Get a formatted label for display (e.g., "[◈_◈] $arch")
195
+ */
196
+ getLabel(): string;
197
+ /**
198
+ * Get a full label with display name (e.g., "[◈_◈] $arch (Solution Architect)")
199
+ */
200
+ getFullLabel(): string;
201
+ }
@@ -0,0 +1,492 @@
1
+ /**
2
+ * TeamAgent - Wrapper for persistent team agents
3
+ *
4
+ * Each team agent has:
5
+ * - Unique identity (id, name, mascot)
6
+ * - Isolated conversation history
7
+ * - Role-specific system prompt additions
8
+ * - Optional tool filtering
9
+ */
10
+ import { ROLE_METADATA } from './types.js';
11
+ import { getModelForTier, getModelContextWindow } from '../models/index.js';
12
+ import { generateCustomAgentSystemPrompt, getCustomAgentToolFilter } from './custom-agents.js';
13
+ import { getToolsForProfile, generateToolAwarenessPrompt, generateCoordinatorGuidance, generateSpecialistGuidance, } from './tool-config.js';
14
+ /**
15
+ * Estimate token count from text (approximate: ~4 chars per token)
16
+ */
17
+ function estimateTokens(text) {
18
+ return Math.ceil(text.length / 4);
19
+ }
20
+ /**
21
+ * TeamAgent wraps an Agent instance with team-specific metadata
22
+ */
23
+ export class TeamAgent {
24
+ /**
25
+ * Unique identifier (e.g., 'pm', 'arch', 'qa')
26
+ */
27
+ id;
28
+ /**
29
+ * Display name shown in UI
30
+ */
31
+ displayName;
32
+ /**
33
+ * Mascot expression for visual differentiation
34
+ */
35
+ mascot;
36
+ /**
37
+ * Role type
38
+ */
39
+ role;
40
+ /**
41
+ * Optional description
42
+ */
43
+ description;
44
+ /**
45
+ * System prompt addition specific to this agent
46
+ */
47
+ systemPromptAddition;
48
+ /**
49
+ * Use minimal system prompt - skip all modules
50
+ */
51
+ useMinimalSystemPrompt;
52
+ /**
53
+ * Skip all tool registration - agent has no tools
54
+ */
55
+ noTools;
56
+ /**
57
+ * Tool filter (tool names this agent can use)
58
+ */
59
+ toolFilter;
60
+ /**
61
+ * Tool profile name (for display)
62
+ */
63
+ toolProfile;
64
+ /**
65
+ * Enabled skills (empty = all skills)
66
+ */
67
+ enabledSkills;
68
+ /**
69
+ * Auto-approve handoffs from this agent without user confirmation
70
+ */
71
+ autoApproveHandoff;
72
+ /**
73
+ * Model tier (fast/balanced/powerful)
74
+ * Determines which model to use based on provider
75
+ */
76
+ _modelTier;
77
+ /**
78
+ * The underlying agent instance
79
+ */
80
+ _agent = null;
81
+ /**
82
+ * Agent configuration for lazy initialization
83
+ */
84
+ agentConfig;
85
+ /**
86
+ * Stored agent state (for persistence before agent is created)
87
+ */
88
+ storedState;
89
+ /**
90
+ * Token usage tracking
91
+ */
92
+ _tokenUsage = {
93
+ input: 0,
94
+ output: 0,
95
+ };
96
+ /**
97
+ * Last activity timestamp
98
+ */
99
+ _lastActivity = new Date();
100
+ constructor(config) {
101
+ this.id = config.id;
102
+ this.displayName = config.displayName;
103
+ this.mascot = config.mascot;
104
+ this.role = config.role;
105
+ this.description = config.description;
106
+ this.systemPromptAddition = config.systemPromptAddition;
107
+ this.useMinimalSystemPrompt = config.useMinimalSystemPrompt;
108
+ this.noTools = config.noTools;
109
+ this.toolFilter = config.toolFilter;
110
+ this.toolProfile = config.toolProfile;
111
+ this.enabledSkills = config.enabledSkills;
112
+ this.autoApproveHandoff = config.autoApproveHandoff;
113
+ this._modelTier = config.modelTier;
114
+ this.agentConfig = config.agentConfig ?? {};
115
+ }
116
+ /**
117
+ * Get the underlying agent instance
118
+ */
119
+ get agent() {
120
+ return this._agent;
121
+ }
122
+ /**
123
+ * Check if agent is initialized
124
+ */
125
+ get isInitialized() {
126
+ return this._agent !== null;
127
+ }
128
+ /**
129
+ * Get token usage
130
+ */
131
+ get tokenUsage() {
132
+ return { ...this._tokenUsage };
133
+ }
134
+ /**
135
+ * Get last activity timestamp
136
+ */
137
+ get lastActivity() {
138
+ return this._lastActivity;
139
+ }
140
+ /**
141
+ * Get model tier
142
+ */
143
+ get modelTier() {
144
+ return this._modelTier;
145
+ }
146
+ /**
147
+ * Change the model tier
148
+ * This preserves conversation history but strips thinking blocks
149
+ * (which contain provider-specific signatures that don't transfer).
150
+ *
151
+ * @param newTier - The new model tier to use
152
+ */
153
+ setModelTier(newTier) {
154
+ if (newTier === this._modelTier) {
155
+ return; // No change needed
156
+ }
157
+ // Store current state if agent is initialized
158
+ if (this._agent) {
159
+ const state = this._agent.serialize();
160
+ // Strip thinking blocks from messages - they contain provider-specific
161
+ // signatures (e.g., Gemini's thought_signature) that don't transfer
162
+ // between sessions or model changes
163
+ state.messages = state.messages.map((msg) => {
164
+ // Content can be string or ContentBlock[]
165
+ if (typeof msg.content === 'string') {
166
+ return msg;
167
+ }
168
+ return {
169
+ ...msg,
170
+ content: msg.content.filter((block) => block.type !== 'thinking'),
171
+ };
172
+ });
173
+ this.storedState = state;
174
+ this._agent = null; // Clear agent so it will be reinitialized
175
+ }
176
+ this._modelTier = newTier;
177
+ }
178
+ /**
179
+ * Initialize the agent with the given factory
180
+ * This allows the team to control agent creation
181
+ *
182
+ * @param agentFactory Factory function to create the agent
183
+ * @param sharedContext Optional shared context to inject into system prompt
184
+ */
185
+ async initialize(agentFactory, sharedContext) {
186
+ if (this._agent) {
187
+ return; // Already initialized
188
+ }
189
+ // Build system prompt addition with shared context
190
+ let finalSystemPromptAddition = this.systemPromptAddition ?? '';
191
+ // Inject shared context if provided (excluding roster — that goes via anchor for live updates)
192
+ if (sharedContext) {
193
+ const sharedContextBlock = sharedContext.format({ excludeRoster: true });
194
+ if (sharedContextBlock) {
195
+ // Prepend shared context to role-specific prompt
196
+ finalSystemPromptAddition = finalSystemPromptAddition
197
+ ? `${sharedContextBlock}\n\n${finalSystemPromptAddition}`
198
+ : sharedContextBlock;
199
+ }
200
+ }
201
+ this._agent = await agentFactory(this.agentConfig, finalSystemPromptAddition || undefined, this.useMinimalSystemPrompt, this.noTools, this.toolFilter, // Pass tool filter to factory
202
+ this._modelTier // Pass model tier to factory
203
+ );
204
+ // Set the team roster as an anchor (dynamically re-injected on every LLM call).
205
+ // This ensures roster updates (agent added/removed) are always visible.
206
+ if (sharedContext && sharedContext.hasTeamRoster() && this._agent.hasAnchors()) {
207
+ this._agent.addAnchor({
208
+ id: 'team-roster',
209
+ content: sharedContext.formatTeamRoster(),
210
+ priority: 'info',
211
+ scope: 'session',
212
+ });
213
+ }
214
+ // Restore state if we have stored state
215
+ if (this.storedState && this.storedState.messages.length > 0) {
216
+ // Strip thinking blocks - they contain provider-specific signatures
217
+ // (e.g., Gemini's thought_signature) that don't transfer between sessions
218
+ const messagesWithoutThinking = this.storedState.messages.map((msg) => {
219
+ if (typeof msg.content === 'string') {
220
+ return msg;
221
+ }
222
+ return {
223
+ ...msg,
224
+ content: msg.content.filter((block) => block.type !== 'thinking'),
225
+ };
226
+ });
227
+ await this._agent.setHistory(messagesWithoutThinking, {
228
+ turnCount: this.storedState.turnCount,
229
+ });
230
+ }
231
+ }
232
+ /**
233
+ * Set the agent instance directly
234
+ * Used for the default agent when an agent is created before the team
235
+ */
236
+ setAgent(agent) {
237
+ this._agent = agent;
238
+ }
239
+ /**
240
+ * Update token usage after a turn
241
+ */
242
+ updateTokenUsage(input, output) {
243
+ this._tokenUsage.input += input;
244
+ this._tokenUsage.output += output;
245
+ this._lastActivity = new Date();
246
+ }
247
+ /**
248
+ * Reset token usage (e.g., after compaction)
249
+ */
250
+ resetTokenUsage() {
251
+ this._tokenUsage = { input: 0, output: 0 };
252
+ }
253
+ /**
254
+ * Clear conversation history (for /reset command)
255
+ * Clears both the underlying agent and stored state
256
+ */
257
+ clearHistory() {
258
+ // Clear underlying agent if initialized
259
+ if (this._agent) {
260
+ this._agent.clearHistory();
261
+ }
262
+ // Clear stored state messages
263
+ if (this.storedState) {
264
+ this.storedState.messages = [];
265
+ this.storedState.todos = [];
266
+ this.storedState.turnCount = 0;
267
+ }
268
+ // Reset token usage
269
+ this._tokenUsage = { input: 0, output: 0 };
270
+ this._lastActivity = new Date();
271
+ }
272
+ /**
273
+ * Get context manager from the agent (if available)
274
+ */
275
+ getContextManager() {
276
+ return this._agent?.getContextManager();
277
+ }
278
+ /**
279
+ * Get context utilization percentage
280
+ */
281
+ getContextUtilization() {
282
+ const cm = this.getContextManager();
283
+ if (!cm)
284
+ return 0;
285
+ const messageCount = this._agent?.serialize().messages.length ?? 0;
286
+ const stats = cm.getStats(messageCount);
287
+ return Math.round((stats.currentTokens / stats.maxTokens) * 100);
288
+ }
289
+ /**
290
+ * Get the max context window for this agent's model.
291
+ * Resolves: modelTier → modelId → contextWindow from MODEL_REGISTRY.
292
+ */
293
+ getMaxContextTokens(provider) {
294
+ // If agent is initialized, get from ContextManager
295
+ if (this._agent) {
296
+ const cm = this.getContextManager();
297
+ if (cm) {
298
+ const state = this._agent.serialize();
299
+ const stats = cm.getStats(state.messages.length);
300
+ return stats.maxTokens;
301
+ }
302
+ }
303
+ // Fall back to model-based lookup
304
+ if (this._modelTier) {
305
+ const modelId = getModelForTier(provider, this._modelTier);
306
+ return getModelContextWindow(modelId, provider);
307
+ }
308
+ return getModelContextWindow('', provider); // provider fallback
309
+ }
310
+ /**
311
+ * Get context stats (works even when agent is not initialized)
312
+ * Returns data from either the live agent or stored state.
313
+ * Includes maxTokens for per-agent context window display.
314
+ */
315
+ getContextStats(provider) {
316
+ // Try live agent first — use ContextManager stats (includes overhead)
317
+ if (this._agent) {
318
+ const cm = this.getContextManager();
319
+ if (cm) {
320
+ const state = this._agent.serialize();
321
+ const stats = cm.getStats(state.messages.length);
322
+ return {
323
+ messageCount: state.messages.length,
324
+ estimatedTokens: stats.currentTokens,
325
+ maxTokens: stats.maxTokens,
326
+ };
327
+ }
328
+ // Agent exists but no context manager — estimate
329
+ const state = this._agent.serialize();
330
+ const messagesStr = JSON.stringify(state.messages);
331
+ return {
332
+ messageCount: state.messages.length,
333
+ estimatedTokens: estimateTokens(messagesStr),
334
+ maxTokens: provider ? this.getMaxContextTokens(provider) : 200000,
335
+ };
336
+ }
337
+ // Fall back to stored state
338
+ if (this.storedState && this.storedState.messages.length > 0) {
339
+ const messagesStr = JSON.stringify(this.storedState.messages);
340
+ return {
341
+ messageCount: this.storedState.messages.length,
342
+ estimatedTokens: estimateTokens(messagesStr),
343
+ maxTokens: provider ? this.getMaxContextTokens(provider) : 200000,
344
+ };
345
+ }
346
+ return {
347
+ messageCount: 0,
348
+ estimatedTokens: 0,
349
+ maxTokens: provider ? this.getMaxContextTokens(provider) : 200000,
350
+ };
351
+ }
352
+ /**
353
+ * Serialize the team agent for persistence
354
+ */
355
+ serialize() {
356
+ const now = new Date().toISOString();
357
+ const agentState = this._agent?.serialize() ??
358
+ this.storedState ?? {
359
+ sessionId: '',
360
+ messages: [],
361
+ systemPrompt: '',
362
+ todos: [],
363
+ currentIteration: 0,
364
+ turnCount: 0,
365
+ totalTokensUsed: 0,
366
+ createdAt: now,
367
+ updatedAt: now,
368
+ version: 1,
369
+ };
370
+ return {
371
+ id: this.id,
372
+ displayName: this.displayName,
373
+ mascot: this.mascot,
374
+ role: this.role,
375
+ description: this.description,
376
+ systemPromptAddition: this.systemPromptAddition,
377
+ useMinimalSystemPrompt: this.useMinimalSystemPrompt,
378
+ noTools: this.noTools,
379
+ toolFilter: this.toolFilter,
380
+ toolProfile: this.toolProfile,
381
+ enabledSkills: this.enabledSkills,
382
+ modelTier: this.modelTier,
383
+ autoApproveHandoff: this.autoApproveHandoff,
384
+ agentState,
385
+ };
386
+ }
387
+ /**
388
+ * Create a TeamAgent from serialized state
389
+ */
390
+ static fromSerialized(data) {
391
+ // Refresh tool filter from profile if available (ensures new tools are picked up)
392
+ // This handles the case where tools were added after the agent was created
393
+ const toolFilter = data.toolProfile ? getToolsForProfile(data.toolProfile) : data.toolFilter;
394
+ const agent = new TeamAgent({
395
+ id: data.id,
396
+ displayName: data.displayName,
397
+ mascot: data.mascot,
398
+ role: data.role,
399
+ description: data.description,
400
+ systemPromptAddition: data.systemPromptAddition,
401
+ useMinimalSystemPrompt: data.useMinimalSystemPrompt,
402
+ noTools: data.noTools,
403
+ toolFilter,
404
+ toolProfile: data.toolProfile,
405
+ enabledSkills: data.enabledSkills,
406
+ modelTier: data.modelTier,
407
+ autoApproveHandoff: data.autoApproveHandoff,
408
+ });
409
+ // Store the state for restoration when agent is initialized
410
+ agent.storedState = data.agentState;
411
+ return agent;
412
+ }
413
+ /**
414
+ * Create a TeamAgent from a predefined role
415
+ * @param role - The predefined role
416
+ * @param id - Optional custom ID (defaults to role name)
417
+ * @param modelTier - Optional model tier override (defaults to role's default tier)
418
+ */
419
+ static fromRole(role, id, modelTier) {
420
+ const metadata = ROLE_METADATA[role];
421
+ // Get tool filter from default profile (if specified)
422
+ const profile = metadata.defaultToolProfile;
423
+ const toolFilter = profile ? getToolsForProfile(profile) : undefined;
424
+ // Build system prompt with tool awareness (for non-full profiles)
425
+ let systemPromptAddition = metadata.defaultSystemPromptAddition ?? '';
426
+ if (profile && profile !== 'full') {
427
+ const toolAwareness = generateToolAwarenessPrompt({ profile });
428
+ systemPromptAddition = systemPromptAddition
429
+ ? `${systemPromptAddition}\n\n${toolAwareness}`
430
+ : toolAwareness;
431
+ }
432
+ // Add delegation guidance based on role
433
+ if (role === 'default') {
434
+ // Coordinator gets delegation tool guidance
435
+ const coordinatorGuidance = generateCoordinatorGuidance();
436
+ systemPromptAddition = systemPromptAddition
437
+ ? `${systemPromptAddition}\n\n${coordinatorGuidance}`
438
+ : coordinatorGuidance;
439
+ }
440
+ else {
441
+ // Specialists get collaboration guidance
442
+ const specialistGuidance = generateSpecialistGuidance(role);
443
+ systemPromptAddition = systemPromptAddition
444
+ ? `${systemPromptAddition}\n\n${specialistGuidance}`
445
+ : specialistGuidance;
446
+ }
447
+ return new TeamAgent({
448
+ id: id ?? role,
449
+ displayName: metadata.displayName,
450
+ mascot: metadata.mascot,
451
+ role,
452
+ description: metadata.description,
453
+ systemPromptAddition: systemPromptAddition || undefined,
454
+ useMinimalSystemPrompt: metadata.useMinimalSystemPrompt,
455
+ noTools: metadata.noTools,
456
+ toolFilter,
457
+ toolProfile: profile,
458
+ modelTier: modelTier ?? metadata.defaultModelTier,
459
+ });
460
+ }
461
+ /**
462
+ * Create a TeamAgent from a custom agent definition
463
+ */
464
+ static fromCustomDefinition(def) {
465
+ // Get tool filter from custom agent config
466
+ const toolFilter = getCustomAgentToolFilter(def);
467
+ return new TeamAgent({
468
+ id: def.id,
469
+ displayName: def.displayName,
470
+ mascot: def.mascot,
471
+ role: 'custom',
472
+ description: def.specialty,
473
+ systemPromptAddition: generateCustomAgentSystemPrompt(def),
474
+ toolFilter, // Pass tool filter from custom agent config
475
+ toolProfile: def.toolConfig?.profile, // Store profile for display
476
+ enabledSkills: def.enabledSkills, // Store skills for display
477
+ modelTier: def.modelTier, // Store model tier
478
+ });
479
+ }
480
+ /**
481
+ * Get a formatted label for display (e.g., "[◈_◈] $arch")
482
+ */
483
+ getLabel() {
484
+ return `${this.mascot} $${this.id}`;
485
+ }
486
+ /**
487
+ * Get a full label with display name (e.g., "[◈_◈] $arch (Solution Architect)")
488
+ */
489
+ getFullLabel() {
490
+ return `${this.mascot} $${this.id} (${this.displayName})`;
491
+ }
492
+ }