@push.rocks/smartagent 1.6.2 → 1.8.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.
package/readme.md CHANGED
@@ -37,20 +37,19 @@ flowchart TB
37
37
  end
38
38
 
39
39
  subgraph Orchestrator["DualAgentOrchestrator"]
40
+ Registry["ToolRegistry<br/><i>Visibility & Lifecycle</i>"]
40
41
  Driver["Driver Agent<br/><i>Reason + Plan</i>"]
41
42
  Guardian["Guardian Agent<br/><i>Evaluate against policy</i>"]
42
43
 
43
44
  Driver -->|"tool call proposal"| Guardian
44
45
  Guardian -->|"approve / reject + feedback"| Driver
46
+ Registry -->|"visible tools"| Driver
45
47
  end
46
48
 
47
- subgraph Tools["Standard Tools"]
48
- FS["Filesystem"]
49
- HTTP["HTTP"]
50
- Shell["Shell"]
51
- Browser["Browser"]
52
- Deno["Deno"]
53
- JSON["JSON Validator"]
49
+ subgraph Tools["Tools"]
50
+ Initial["Initial Tools<br/><i>Always visible</i>"]
51
+ OnDemand["On-Demand Tools<br/><i>Discoverable via search</i>"]
52
+ Experts["Expert SubAgents<br/><i>Specialized agents as tools</i>"]
54
53
  end
55
54
 
56
55
  Task --> Orchestrator
@@ -100,7 +99,7 @@ await orchestrator.stop();
100
99
 
101
100
  ## Standard Tools
102
101
 
103
- SmartAgent comes with six battle-tested tools out of the box:
102
+ SmartAgent comes with five battle-tested tools out of the box via `registerStandardTools()`:
104
103
 
105
104
  ### 🗂️ FilesystemTool
106
105
 
@@ -231,12 +230,21 @@ By default, code runs **fully sandboxed with no permissions**. Permissions must
231
230
  </tool_call>
232
231
  ```
233
232
 
233
+ ## Additional Tools
234
+
234
235
  ### 📋 JsonValidatorTool
235
236
 
236
237
  Validate and format JSON data. Perfect for agents to self-check their JSON output before completing tasks.
237
238
 
238
239
  **Actions**: `validate`, `format`
239
240
 
241
+ ```typescript
242
+ import { JsonValidatorTool } from '@push.rocks/smartagent';
243
+
244
+ // Register the JSON validator tool (not included in registerStandardTools)
245
+ orchestrator.registerTool(new JsonValidatorTool());
246
+ ```
247
+
240
248
  ```typescript
241
249
  // Validate JSON with required field checking
242
250
  <tool_call>
@@ -258,6 +266,157 @@ Validate and format JSON data. Perfect for agents to self-check their JSON outpu
258
266
  </tool_call>
259
267
  ```
260
268
 
269
+ ### 🔍 ToolSearchTool
270
+
271
+ Enable the Driver to discover and activate on-demand tools at runtime.
272
+
273
+ **Actions**: `search`, `list`, `activate`, `details`
274
+
275
+ ```typescript
276
+ // Enable tool search (adds the 'tools' tool)
277
+ orchestrator.enableToolSearch();
278
+ ```
279
+
280
+ ```typescript
281
+ // Search for tools by capability
282
+ <tool_call>
283
+ <tool>tools</tool>
284
+ <action>search</action>
285
+ <params>{"query": "database"}</params>
286
+ </tool_call>
287
+
288
+ // List all available tools
289
+ <tool_call>
290
+ <tool>tools</tool>
291
+ <action>list</action>
292
+ <params>{}</params>
293
+ </tool_call>
294
+
295
+ // Activate an on-demand tool
296
+ <tool_call>
297
+ <tool>tools</tool>
298
+ <action>activate</action>
299
+ <params>{"name": "database_expert"}</params>
300
+ </tool_call>
301
+
302
+ // Get detailed information about a tool
303
+ <tool_call>
304
+ <tool>tools</tool>
305
+ <action>details</action>
306
+ <params>{"name": "filesystem"}</params>
307
+ </tool_call>
308
+ ```
309
+
310
+ ### 🧠 ExpertTool (SubAgents)
311
+
312
+ Create specialized sub-agents that can be invoked as tools. Experts are complete `DualAgentOrchestrator` instances wrapped as tools, enabling hierarchical agent architectures.
313
+
314
+ **Actions**: `consult`
315
+
316
+ ```typescript
317
+ // Register an expert for code review
318
+ orchestrator.registerExpert({
319
+ name: 'code_reviewer',
320
+ description: 'Reviews code for quality, bugs, and best practices',
321
+ systemMessage: `You are an expert code reviewer. Analyze code for:
322
+ - Bugs and potential issues
323
+ - Code style and best practices
324
+ - Performance concerns
325
+ - Security vulnerabilities`,
326
+ guardianPolicy: 'Allow read-only file access within the workspace',
327
+ tools: [new FilesystemTool()],
328
+ visibility: 'on-demand', // Only available via tool search
329
+ tags: ['code', 'review', 'quality'],
330
+ category: 'expert',
331
+ });
332
+ ```
333
+
334
+ ```typescript
335
+ // Consult an expert
336
+ <tool_call>
337
+ <tool>code_reviewer</tool>
338
+ <action>consult</action>
339
+ <params>{
340
+ "task": "Review this function for potential issues",
341
+ "context": "This is a user authentication handler"
342
+ }</params>
343
+ </tool_call>
344
+ ```
345
+
346
+ ## 🎯 Tool Visibility System
347
+
348
+ SmartAgent supports **tool visibility modes** for scalable agent architectures:
349
+
350
+ - **`initial`** (default): Tool is visible to the Driver from the start, included in the system prompt
351
+ - **`on-demand`**: Tool is hidden until explicitly activated via `tools.activate()`
352
+
353
+ This enables you to have many specialized tools/experts without overwhelming the Driver's context.
354
+
355
+ ```typescript
356
+ // Register a tool with on-demand visibility
357
+ orchestrator.registerTool(new MySpecializedTool(), {
358
+ visibility: 'on-demand',
359
+ tags: ['specialized', 'database'],
360
+ category: 'data',
361
+ });
362
+
363
+ // Enable tool search so Driver can discover and activate on-demand tools
364
+ orchestrator.enableToolSearch();
365
+
366
+ // The Driver can now:
367
+ // 1. tools.search({"query": "database"}) -> finds MySpecializedTool
368
+ // 2. tools.activate({"name": "myspecialized"}) -> enables it
369
+ // 3. myspecialized.action({...}) -> use the tool
370
+ ```
371
+
372
+ ### Expert SubAgent Example
373
+
374
+ ```typescript
375
+ const orchestrator = new DualAgentOrchestrator({
376
+ openaiToken: 'sk-...',
377
+ defaultProvider: 'openai',
378
+ guardianPolicyPrompt: 'Allow safe operations...',
379
+ });
380
+
381
+ orchestrator.registerStandardTools();
382
+ orchestrator.enableToolSearch();
383
+
384
+ // Initial expert (always visible)
385
+ orchestrator.registerExpert({
386
+ name: 'code_assistant',
387
+ description: 'Helps with coding tasks and code generation',
388
+ systemMessage: 'You are a helpful coding assistant...',
389
+ guardianPolicy: 'Allow read-only file access',
390
+ tools: [new FilesystemTool()],
391
+ });
392
+
393
+ // On-demand experts (discoverable via search)
394
+ orchestrator.registerExpert({
395
+ name: 'database_expert',
396
+ description: 'Database design, optimization, and query analysis',
397
+ systemMessage: 'You are a database expert...',
398
+ guardianPolicy: 'Allow read-only operations',
399
+ visibility: 'on-demand',
400
+ tags: ['database', 'sql', 'optimization'],
401
+ });
402
+
403
+ orchestrator.registerExpert({
404
+ name: 'security_auditor',
405
+ description: 'Security vulnerability assessment and best practices',
406
+ systemMessage: 'You are a security expert...',
407
+ guardianPolicy: 'Allow read-only file access',
408
+ visibility: 'on-demand',
409
+ tags: ['security', 'audit', 'vulnerabilities'],
410
+ });
411
+
412
+ await orchestrator.start();
413
+
414
+ // Now the Driver can:
415
+ // - Use code_assistant directly
416
+ // - Search for "database" and activate database_expert when needed
417
+ // - Search for "security" and activate security_auditor when needed
418
+ ```
419
+
261
420
  ## 🎥 Streaming Support
262
421
 
263
422
  SmartAgent supports token-by-token streaming for real-time output during LLM generation:
@@ -330,6 +489,29 @@ const orchestrator = new DualAgentOrchestrator({
330
489
 
331
490
  **Event Types**: `task_started`, `iteration_started`, `tool_proposed`, `guardian_evaluating`, `tool_approved`, `tool_rejected`, `tool_executing`, `tool_completed`, `task_completed`, `clarification_needed`, `max_iterations`, `max_rejections`
332
491
 
492
+ ## 🔧 Native Tool Calling
493
+
494
+ For providers that support native tool calling (like Ollama with certain models), SmartAgent can use the provider's built-in tool calling API instead of XML parsing:
495
+
496
+ ```typescript
497
+ const orchestrator = new DualAgentOrchestrator({
498
+ ollamaToken: 'http://localhost:11434', // Ollama endpoint
499
+ defaultProvider: 'ollama',
500
+ guardianPolicyPrompt: '...',
501
+
502
+ // Enable native tool calling
503
+ useNativeToolCalling: true,
504
+ });
505
+ ```
506
+
507
+ When `useNativeToolCalling` is enabled:
508
+ - Tools are converted to JSON schema format automatically
509
+ - The provider handles tool call parsing natively
510
+ - Streaming still works with `[THINKING]` and `[OUTPUT]` markers for supported models
511
+ - Tool calls appear as `toolName_actionName` (e.g., `json_validate`)
512
+
513
+ This is more efficient for models that support it and avoids potential XML parsing issues.
514
+
333
515
  ## Guardian Policy Examples
334
516
 
335
517
  The Guardian's power comes from your policy. Here are battle-tested examples:
@@ -401,6 +583,7 @@ interface IDualAgentOptions {
401
583
  perplexityToken?: string;
402
584
  groqToken?: string;
403
585
  xaiToken?: string;
586
+ ollamaToken?: string; // URL for Ollama endpoint
404
587
 
405
588
  // Use existing SmartAi instance (optional - avoids duplicate providers)
406
589
  smartAiInstance?: SmartAi;
@@ -415,6 +598,9 @@ interface IDualAgentOptions {
415
598
  name?: string; // Agent system name
416
599
  verbose?: boolean; // Enable verbose logging
417
600
 
601
+ // Native tool calling
602
+ useNativeToolCalling?: boolean; // Use provider's native tool calling API (default: false)
603
+
418
604
  // Limits
419
605
  maxIterations?: number; // Max task iterations (default: 20)
420
606
  maxConsecutiveRejections?: number; // Abort after N rejections (default: 3)
@@ -573,12 +759,15 @@ const orchestrator = new DualAgentOrchestrator({
573
759
  | `stop()` | Cleanup all tools and resources |
574
760
  | `run(task, options?)` | Execute a task with optional images for vision |
575
761
  | `continueTask(input)` | Continue a task with user input |
576
- | `registerTool(tool)` | Register a custom tool |
577
- | `registerStandardTools()` | Register all built-in tools |
762
+ | `registerTool(tool, options?)` | Register a custom tool with optional visibility settings |
763
+ | `registerStandardTools()` | Register all built-in tools (Filesystem, HTTP, Shell, Browser, Deno) |
578
764
  | `registerScopedFilesystemTool(basePath, excludePatterns?)` | Register filesystem tool with path restriction |
765
+ | `registerExpert(config)` | Register a specialized sub-agent as a tool |
766
+ | `enableToolSearch()` | Enable tool discovery and activation for the Driver |
579
767
  | `setGuardianPolicy(policy)` | Update Guardian policy at runtime |
580
768
  | `getHistory()` | Get conversation history |
581
769
  | `getToolNames()` | Get list of registered tool names |
770
+ | `getRegistry()` | Get the ToolRegistry for advanced operations |
582
771
  | `isActive()` | Check if orchestrator is running |
583
772
 
584
773
  ### Exports
@@ -589,6 +778,9 @@ export { DualAgentOrchestrator } from '@push.rocks/smartagent';
589
778
  export { DriverAgent } from '@push.rocks/smartagent';
590
779
  export { GuardianAgent } from '@push.rocks/smartagent';
591
780
 
781
+ // Tool Registry
782
+ export { ToolRegistry } from '@push.rocks/smartagent';
783
+
592
784
  // Tools
593
785
  export { BaseToolWrapper } from '@push.rocks/smartagent';
594
786
  export { FilesystemTool, type IFilesystemToolOptions } from '@push.rocks/smartagent';
@@ -597,9 +789,11 @@ export { ShellTool } from '@push.rocks/smartagent';
597
789
  export { BrowserTool } from '@push.rocks/smartagent';
598
790
  export { DenoTool, type TDenoPermission } from '@push.rocks/smartagent';
599
791
  export { JsonValidatorTool } from '@push.rocks/smartagent';
792
+ export { ToolSearchTool } from '@push.rocks/smartagent';
793
+ export { ExpertTool } from '@push.rocks/smartagent';
600
794
 
601
795
  // Types and interfaces
602
- export * from '@push.rocks/smartagent'; // All interfaces
796
+ export * from '@push.rocks/smartagent'; // All interfaces (IExpertConfig, IToolMetadata, etc.)
603
797
 
604
798
  // Re-exported from @push.rocks/smartai
605
799
  export { type ISmartAiOptions, type TProvider, type ChatMessage, type ChatOptions, type ChatResponse };
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartagent',
6
- version: '1.6.2',
6
+ version: '1.8.0',
7
7
  description: 'an agentic framework built on top of @push.rocks/smartai'
8
8
  }
package/ts/index.ts CHANGED
@@ -7,6 +7,9 @@ export { DualAgentOrchestrator } from './smartagent.classes.dualagent.js';
7
7
  export { DriverAgent } from './smartagent.classes.driveragent.js';
8
8
  export { GuardianAgent } from './smartagent.classes.guardianagent.js';
9
9
 
10
+ // Export tool registry and related classes
11
+ export { ToolRegistry } from './smartagent.classes.toolregistry.js';
12
+
10
13
  // Export base tool class for custom tool creation
11
14
  export { BaseToolWrapper } from './smartagent.tools.base.js';
12
15
 
@@ -18,6 +21,10 @@ export { BrowserTool } from './smartagent.tools.browser.js';
18
21
  export { DenoTool, type TDenoPermission } from './smartagent.tools.deno.js';
19
22
  export { JsonValidatorTool } from './smartagent.tools.json.js';
20
23
 
24
+ // Export tool search and expert tools
25
+ export { ToolSearchTool } from './smartagent.tools.search.js';
26
+ export { ExpertTool } from './smartagent.tools.expert.js';
27
+
21
28
  // Export all interfaces
22
29
  export * from './smartagent.interfaces.js';
23
30
 
@@ -8,6 +8,9 @@ import { HttpTool } from './smartagent.tools.http.js';
8
8
  import { ShellTool } from './smartagent.tools.shell.js';
9
9
  import { BrowserTool } from './smartagent.tools.browser.js';
10
10
  import { DenoTool } from './smartagent.tools.deno.js';
11
+ import { ToolRegistry } from './smartagent.classes.toolregistry.js';
12
+ import { ToolSearchTool } from './smartagent.tools.search.js';
13
+ import { ExpertTool } from './smartagent.tools.expert.js';
11
14
 
12
15
  /**
13
16
  * DualAgentOrchestrator - Coordinates Driver and Guardian agents
@@ -20,7 +23,7 @@ export class DualAgentOrchestrator {
20
23
  private guardianProvider: plugins.smartai.MultiModalModel;
21
24
  private driver: DriverAgent;
22
25
  private guardian: GuardianAgent;
23
- private tools: Map<string, BaseToolWrapper> = new Map();
26
+ private registry: ToolRegistry = new ToolRegistry();
24
27
  private isRunning = false;
25
28
  private conversationHistory: interfaces.IAgentMessage[] = [];
26
29
  private ownsSmartAi = true; // true if we created the SmartAi instance, false if it was provided
@@ -125,19 +128,55 @@ export class DualAgentOrchestrator {
125
128
  }
126
129
 
127
130
  /**
128
- * Register a custom tool
131
+ * Register a custom tool with optional visibility settings
129
132
  */
130
- public registerTool(tool: BaseToolWrapper): void {
131
- this.tools.set(tool.name, tool);
132
- // Register with agents if they exist (they're created in start())
133
- if (this.driver) {
134
- this.driver.registerTool(tool);
135
- }
136
- if (this.guardian) {
137
- this.guardian.registerTool(tool);
133
+ public registerTool(
134
+ tool: BaseToolWrapper,
135
+ options?: interfaces.IToolRegistrationOptions
136
+ ): void {
137
+ this.registry.register(tool, options);
138
+
139
+ // If initial visibility and agents exist, register with them
140
+ const visibility = options?.visibility ?? 'initial';
141
+ if (visibility === 'initial') {
142
+ if (this.driver) {
143
+ this.driver.registerTool(tool);
144
+ }
145
+ if (this.guardian) {
146
+ this.guardian.registerTool(tool);
147
+ }
138
148
  }
139
149
  }
140
150
 
151
+ /**
152
+ * Register an expert (subagent) as a tool
153
+ */
154
+ public registerExpert(config: interfaces.IExpertConfig): void {
155
+ const expert = new ExpertTool(config, this.smartai);
156
+ this.registerTool(expert, {
157
+ visibility: config.visibility,
158
+ tags: config.tags,
159
+ category: config.category ?? 'expert',
160
+ });
161
+ }
162
+
163
+ /**
164
+ * Enable tool search functionality
165
+ * This adds a 'tools' tool that allows the Driver to discover and activate on-demand tools
166
+ */
167
+ public enableToolSearch(): void {
168
+ const searchTool = new ToolSearchTool(this.registry, (tool) => {
169
+ // Callback when an on-demand tool is activated
170
+ if (this.driver) {
171
+ this.driver.registerTool(tool);
172
+ }
173
+ if (this.guardian) {
174
+ this.guardian.registerTool(tool);
175
+ }
176
+ });
177
+ this.registerTool(searchTool); // Always initial visibility
178
+ }
179
+
141
180
  /**
142
181
  * Register all standard tools
143
182
  */
@@ -193,19 +232,14 @@ export class DualAgentOrchestrator {
193
232
  });
194
233
  this.guardian = new GuardianAgent(this.guardianProvider, this.options.guardianPolicyPrompt);
195
234
 
196
- // Register any tools that were added before start() with the agents
197
- for (const tool of this.tools.values()) {
235
+ // Register visible tools with agents
236
+ for (const tool of this.registry.getVisibleTools()) {
198
237
  this.driver.registerTool(tool);
199
238
  this.guardian.registerTool(tool);
200
239
  }
201
240
 
202
- // Initialize all tools
203
- const initPromises: Promise<void>[] = [];
204
- for (const tool of this.tools.values()) {
205
- initPromises.push(tool.initialize());
206
- }
207
-
208
- await Promise.all(initPromises);
241
+ // Initialize visible tools
242
+ await this.registry.initializeVisibleTools();
209
243
  this.isRunning = true;
210
244
  }
211
245
 
@@ -213,13 +247,7 @@ export class DualAgentOrchestrator {
213
247
  * Cleanup all tools
214
248
  */
215
249
  public async stop(): Promise<void> {
216
- const cleanupPromises: Promise<void>[] = [];
217
-
218
- for (const tool of this.tools.values()) {
219
- cleanupPromises.push(tool.cleanup());
220
- }
221
-
222
- await Promise.all(cleanupPromises);
250
+ await this.registry.cleanup();
223
251
 
224
252
  // Only stop smartai if we created it (don't stop external instances)
225
253
  if (this.ownsSmartAi) {
@@ -432,7 +460,7 @@ Please output the exact XML format above.`
432
460
  });
433
461
 
434
462
  // Execute the tool
435
- const tool = this.tools.get(proposal.toolName);
463
+ const tool = this.registry.getTool(proposal.toolName);
436
464
  if (!tool) {
437
465
  const errorMessage = `Tool "${proposal.toolName}" not found.`;
438
466
  driverResponse = await this.driver.continueWithMessage(
@@ -652,6 +680,13 @@ Please output the exact XML format above.`
652
680
  * Get registered tool names
653
681
  */
654
682
  public getToolNames(): string[] {
655
- return Array.from(this.tools.keys());
683
+ return this.registry.getAllMetadata().map((m) => m.name);
684
+ }
685
+
686
+ /**
687
+ * Get the tool registry for advanced operations
688
+ */
689
+ public getRegistry(): ToolRegistry {
690
+ return this.registry;
656
691
  }
657
692
  }
@@ -0,0 +1,188 @@
1
+ import * as interfaces from './smartagent.interfaces.js';
2
+ import { BaseToolWrapper } from './smartagent.tools.base.js';
3
+
4
+ /**
5
+ * ToolRegistry - Manages tool registration, visibility, and lifecycle
6
+ *
7
+ * Responsibilities:
8
+ * - Track all registered tools with their metadata
9
+ * - Manage visibility (initial vs on-demand)
10
+ * - Handle activation of on-demand tools
11
+ * - Provide search functionality
12
+ */
13
+ export class ToolRegistry {
14
+ private tools: Map<string, BaseToolWrapper> = new Map();
15
+ private metadata: Map<string, interfaces.IToolMetadata> = new Map();
16
+ private activated: Set<string> = new Set();
17
+
18
+ /**
19
+ * Register a tool with optional visibility settings
20
+ */
21
+ register(tool: BaseToolWrapper, options: interfaces.IToolRegistrationOptions = {}): void {
22
+ const visibility = options.visibility ?? 'initial';
23
+
24
+ this.tools.set(tool.name, tool);
25
+ this.metadata.set(tool.name, {
26
+ name: tool.name,
27
+ description: tool.description,
28
+ actions: tool.actions,
29
+ visibility,
30
+ isActivated: visibility === 'initial',
31
+ isInitialized: false,
32
+ tags: options.tags,
33
+ category: options.category,
34
+ });
35
+
36
+ if (visibility === 'initial') {
37
+ this.activated.add(tool.name);
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Get tools visible to the Driver (initial + activated on-demand)
43
+ */
44
+ getVisibleTools(): BaseToolWrapper[] {
45
+ return Array.from(this.tools.entries())
46
+ .filter(([name]) => this.activated.has(name))
47
+ .map(([, tool]) => tool);
48
+ }
49
+
50
+ /**
51
+ * Get all tools (for search results)
52
+ */
53
+ getAllTools(): BaseToolWrapper[] {
54
+ return Array.from(this.tools.values());
55
+ }
56
+
57
+ /**
58
+ * Get a specific tool by name
59
+ */
60
+ getTool(name: string): BaseToolWrapper | undefined {
61
+ return this.tools.get(name);
62
+ }
63
+
64
+ /**
65
+ * Get metadata for a tool
66
+ */
67
+ getMetadata(name: string): interfaces.IToolMetadata | undefined {
68
+ return this.metadata.get(name);
69
+ }
70
+
71
+ /**
72
+ * Get all metadata
73
+ */
74
+ getAllMetadata(): interfaces.IToolMetadata[] {
75
+ return Array.from(this.metadata.values());
76
+ }
77
+
78
+ /**
79
+ * Search tools by query (matches name, description, tags, action names)
80
+ */
81
+ search(query: string): interfaces.IToolMetadata[] {
82
+ const q = query.toLowerCase();
83
+ return this.getAllMetadata().filter((meta) => {
84
+ if (meta.name.toLowerCase().includes(q)) return true;
85
+ if (meta.description.toLowerCase().includes(q)) return true;
86
+ if (meta.tags?.some((t) => t.toLowerCase().includes(q))) return true;
87
+ if (meta.category?.toLowerCase().includes(q)) return true;
88
+ if (
89
+ meta.actions.some(
90
+ (a) => a.name.toLowerCase().includes(q) || a.description.toLowerCase().includes(q)
91
+ )
92
+ )
93
+ return true;
94
+ return false;
95
+ });
96
+ }
97
+
98
+ /**
99
+ * Activate an on-demand tool
100
+ */
101
+ async activate(name: string): Promise<{ success: boolean; error?: string }> {
102
+ const tool = this.tools.get(name);
103
+ const meta = this.metadata.get(name);
104
+
105
+ if (!tool || !meta) {
106
+ return { success: false, error: `Tool "${name}" not found` };
107
+ }
108
+
109
+ if (this.activated.has(name)) {
110
+ return { success: true }; // Already activated
111
+ }
112
+
113
+ // Initialize if not already initialized
114
+ if (!meta.isInitialized) {
115
+ await tool.initialize();
116
+ meta.isInitialized = true;
117
+ }
118
+
119
+ this.activated.add(name);
120
+ meta.isActivated = true;
121
+
122
+ return { success: true };
123
+ }
124
+
125
+ /**
126
+ * Check if a tool is activated
127
+ */
128
+ isActivated(name: string): boolean {
129
+ return this.activated.has(name);
130
+ }
131
+
132
+ /**
133
+ * Initialize all initial (visible) tools
134
+ */
135
+ async initializeVisibleTools(): Promise<void> {
136
+ const promises: Promise<void>[] = [];
137
+
138
+ for (const [name, tool] of this.tools) {
139
+ const meta = this.metadata.get(name);
140
+ if (meta && this.activated.has(name) && !meta.isInitialized) {
141
+ promises.push(
142
+ tool.initialize().then(() => {
143
+ meta.isInitialized = true;
144
+ })
145
+ );
146
+ }
147
+ }
148
+
149
+ await Promise.all(promises);
150
+ }
151
+
152
+ /**
153
+ * Cleanup all initialized tools
154
+ */
155
+ async cleanup(): Promise<void> {
156
+ const promises: Promise<void>[] = [];
157
+
158
+ for (const [name, tool] of this.tools) {
159
+ const meta = this.metadata.get(name);
160
+ if (meta?.isInitialized) {
161
+ promises.push(tool.cleanup());
162
+ }
163
+ }
164
+
165
+ await Promise.all(promises);
166
+ }
167
+
168
+ /**
169
+ * Check if a tool exists in the registry
170
+ */
171
+ has(name: string): boolean {
172
+ return this.tools.has(name);
173
+ }
174
+
175
+ /**
176
+ * Get the number of registered tools
177
+ */
178
+ get size(): number {
179
+ return this.tools.size;
180
+ }
181
+
182
+ /**
183
+ * Get the number of activated tools
184
+ */
185
+ get activatedCount(): number {
186
+ return this.activated.size;
187
+ }
188
+ }