confused-ai-core 0.1.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 (114) hide show
  1. package/FEATURES.md +169 -0
  2. package/package.json +119 -0
  3. package/src/agent.ts +187 -0
  4. package/src/agentic/index.ts +87 -0
  5. package/src/agentic/runner.ts +386 -0
  6. package/src/agentic/types.ts +91 -0
  7. package/src/artifacts/artifact.ts +417 -0
  8. package/src/artifacts/index.ts +42 -0
  9. package/src/artifacts/media.ts +304 -0
  10. package/src/cli/index.ts +122 -0
  11. package/src/core/base-agent.ts +151 -0
  12. package/src/core/context-builder.ts +106 -0
  13. package/src/core/index.ts +8 -0
  14. package/src/core/schemas.ts +17 -0
  15. package/src/core/types.ts +158 -0
  16. package/src/create-agent.ts +309 -0
  17. package/src/debug-logger.ts +188 -0
  18. package/src/dx/agent.ts +88 -0
  19. package/src/dx/define-agent.ts +183 -0
  20. package/src/dx/dev-logger.ts +57 -0
  21. package/src/dx/index.ts +11 -0
  22. package/src/errors.ts +175 -0
  23. package/src/execution/engine.ts +522 -0
  24. package/src/execution/graph-builder.ts +362 -0
  25. package/src/execution/index.ts +8 -0
  26. package/src/execution/types.ts +257 -0
  27. package/src/execution/worker-pool.ts +308 -0
  28. package/src/extensions/index.ts +123 -0
  29. package/src/guardrails/allowlist.ts +155 -0
  30. package/src/guardrails/index.ts +17 -0
  31. package/src/guardrails/types.ts +159 -0
  32. package/src/guardrails/validator.ts +265 -0
  33. package/src/index.ts +74 -0
  34. package/src/knowledge/index.ts +5 -0
  35. package/src/knowledge/types.ts +52 -0
  36. package/src/learning/in-memory-store.ts +72 -0
  37. package/src/learning/index.ts +6 -0
  38. package/src/learning/types.ts +42 -0
  39. package/src/llm/cache.ts +300 -0
  40. package/src/llm/index.ts +22 -0
  41. package/src/llm/model-resolver.ts +81 -0
  42. package/src/llm/openai-provider.ts +313 -0
  43. package/src/llm/openrouter-provider.ts +29 -0
  44. package/src/llm/types.ts +131 -0
  45. package/src/memory/in-memory-store.ts +255 -0
  46. package/src/memory/index.ts +7 -0
  47. package/src/memory/types.ts +193 -0
  48. package/src/memory/vector-store.ts +251 -0
  49. package/src/observability/console-logger.ts +123 -0
  50. package/src/observability/index.ts +12 -0
  51. package/src/observability/metrics.ts +85 -0
  52. package/src/observability/otlp-exporter.ts +417 -0
  53. package/src/observability/tracer.ts +105 -0
  54. package/src/observability/types.ts +341 -0
  55. package/src/orchestration/agent-adapter.ts +33 -0
  56. package/src/orchestration/index.ts +34 -0
  57. package/src/orchestration/load-balancer.ts +151 -0
  58. package/src/orchestration/mcp-types.ts +59 -0
  59. package/src/orchestration/message-bus.ts +192 -0
  60. package/src/orchestration/orchestrator.ts +349 -0
  61. package/src/orchestration/pipeline.ts +66 -0
  62. package/src/orchestration/supervisor.ts +107 -0
  63. package/src/orchestration/swarm.ts +1099 -0
  64. package/src/orchestration/toolkit.ts +47 -0
  65. package/src/orchestration/types.ts +339 -0
  66. package/src/planner/classical-planner.ts +383 -0
  67. package/src/planner/index.ts +8 -0
  68. package/src/planner/llm-planner.ts +353 -0
  69. package/src/planner/types.ts +227 -0
  70. package/src/planner/validator.ts +297 -0
  71. package/src/production/circuit-breaker.ts +290 -0
  72. package/src/production/graceful-shutdown.ts +251 -0
  73. package/src/production/health.ts +333 -0
  74. package/src/production/index.ts +57 -0
  75. package/src/production/latency-eval.ts +62 -0
  76. package/src/production/rate-limiter.ts +287 -0
  77. package/src/production/resumable-stream.ts +289 -0
  78. package/src/production/types.ts +81 -0
  79. package/src/sdk/index.ts +374 -0
  80. package/src/session/db-driver.ts +50 -0
  81. package/src/session/in-memory-store.ts +235 -0
  82. package/src/session/index.ts +12 -0
  83. package/src/session/sql-store.ts +315 -0
  84. package/src/session/sqlite-store.ts +61 -0
  85. package/src/session/types.ts +153 -0
  86. package/src/tools/base-tool.ts +223 -0
  87. package/src/tools/browser-tool.ts +123 -0
  88. package/src/tools/calculator-tool.ts +265 -0
  89. package/src/tools/file-tools.ts +394 -0
  90. package/src/tools/github-tool.ts +432 -0
  91. package/src/tools/hackernews-tool.ts +187 -0
  92. package/src/tools/http-tool.ts +118 -0
  93. package/src/tools/index.ts +99 -0
  94. package/src/tools/jira-tool.ts +373 -0
  95. package/src/tools/notion-tool.ts +322 -0
  96. package/src/tools/openai-tool.ts +236 -0
  97. package/src/tools/registry.ts +131 -0
  98. package/src/tools/serpapi-tool.ts +234 -0
  99. package/src/tools/shell-tool.ts +118 -0
  100. package/src/tools/slack-tool.ts +327 -0
  101. package/src/tools/telegram-tool.ts +127 -0
  102. package/src/tools/types.ts +229 -0
  103. package/src/tools/websearch-tool.ts +335 -0
  104. package/src/tools/wikipedia-tool.ts +177 -0
  105. package/src/tools/yfinance-tool.ts +33 -0
  106. package/src/voice/index.ts +17 -0
  107. package/src/voice/voice-provider.ts +228 -0
  108. package/tests/artifact.test.ts +241 -0
  109. package/tests/circuit-breaker.test.ts +171 -0
  110. package/tests/health.test.ts +192 -0
  111. package/tests/llm-cache.test.ts +186 -0
  112. package/tests/rate-limiter.test.ts +161 -0
  113. package/tsconfig.json +29 -0
  114. package/vitest.config.ts +47 -0
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Notion tool implementation - TypeScript NotionTools
3
+ */
4
+
5
+ import { z } from 'zod';
6
+ import { BaseTool, BaseToolConfig } from './base-tool.js';
7
+ import { ToolContext, ToolCategory } from './types.js';
8
+
9
+ /**
10
+ * Notion API types
11
+ */
12
+ interface NotionPage {
13
+ id: string;
14
+ url: string;
15
+ properties: Record<string, unknown>;
16
+ }
17
+
18
+ interface NotionSearchResult {
19
+ results: NotionPage[];
20
+ }
21
+
22
+ interface NotionResult {
23
+ data?: unknown;
24
+ error?: string;
25
+ }
26
+
27
+ /**
28
+ * Base Notion tool with common authentication
29
+ */
30
+ abstract class BaseNotionTool<TParams extends z.ZodObject<Record<string, z.ZodType>>> extends BaseTool<TParams, NotionResult> {
31
+ protected token: string;
32
+ protected databaseId?: string;
33
+ protected baseUrl = 'https://api.notion.com/v1';
34
+
35
+ constructor(
36
+ config: Partial<Omit<BaseToolConfig<TParams>, 'parameters'>> & {
37
+ token?: string;
38
+ databaseId?: string;
39
+ },
40
+ params: TParams
41
+ ) {
42
+ super({
43
+ name: config.name || 'notion.tool',
44
+ description: config.description || 'Notion tool',
45
+ parameters: params,
46
+ category: config.category || ToolCategory.API,
47
+ permissions: {
48
+ allowNetwork: true,
49
+ maxExecutionTimeMs: 30000,
50
+ ...config.permissions,
51
+ },
52
+ ...config,
53
+ });
54
+
55
+ this.token = config.token || process.env.NOTION_API_TOKEN || '';
56
+ this.databaseId = config.databaseId || process.env.NOTION_DATABASE_ID;
57
+
58
+ if (!this.token) {
59
+ throw new Error('Notion API token is required. Set NOTION_API_TOKEN environment variable or pass token in config.');
60
+ }
61
+ }
62
+
63
+ protected async notionRequest(endpoint: string, options: RequestInit = {}): Promise<Response> {
64
+ return fetch(`${this.baseUrl}${endpoint}`, {
65
+ ...options,
66
+ headers: {
67
+ Authorization: `Bearer ${this.token}`,
68
+ 'Notion-Version': '2022-06-28',
69
+ 'Content-Type': 'application/json',
70
+ ...(options.headers || {}),
71
+ },
72
+ });
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Create page tool
78
+ */
79
+ const NotionCreatePageParameters = z.object({
80
+ parent_database_id: z.string().optional().describe('Database ID to create page in (overrides default)'),
81
+ title: z.string().describe('The page title'),
82
+ content: z.string().describe('The page content'),
83
+ properties: z.record(z.string(), z.unknown()).optional().describe('Additional properties for the page'),
84
+ });
85
+
86
+ export class NotionCreatePageTool extends BaseNotionTool<typeof NotionCreatePageParameters> {
87
+ constructor(
88
+ config?: Partial<Omit<BaseToolConfig<typeof NotionCreatePageParameters>, 'parameters'>> & {
89
+ token?: string;
90
+ databaseId?: string;
91
+ }
92
+ ) {
93
+ super(
94
+ {
95
+ name: config?.name ?? 'notion.create_page',
96
+ description: config?.description ?? 'Create a new page in Notion',
97
+ ...config,
98
+ },
99
+ NotionCreatePageParameters
100
+ );
101
+ }
102
+
103
+ protected async performExecute(
104
+ params: z.infer<typeof NotionCreatePageParameters>,
105
+ _context: ToolContext
106
+ ): Promise<NotionResult> {
107
+ const databaseId = params.parent_database_id || this.databaseId;
108
+
109
+ if (!databaseId) {
110
+ return {
111
+ error: 'Database ID is required. Set NOTION_DATABASE_ID environment variable, pass databaseId in config, or provide parent_database_id in parameters.',
112
+ };
113
+ }
114
+
115
+ try {
116
+ const body: Record<string, unknown> = {
117
+ parent: { database_id: databaseId },
118
+ properties: {
119
+ Name: {
120
+ title: [{ text: { content: params.title } }],
121
+ },
122
+ ...params.properties,
123
+ },
124
+ children: [
125
+ {
126
+ object: 'block',
127
+ type: 'paragraph',
128
+ paragraph: {
129
+ rich_text: [{ type: 'text', text: { content: params.content } }],
130
+ },
131
+ },
132
+ ],
133
+ };
134
+
135
+ const response = await this.notionRequest('/pages', {
136
+ method: 'POST',
137
+ body: JSON.stringify(body),
138
+ });
139
+
140
+ if (!response.ok) {
141
+ const errorData = (await response.json()) as { message?: string };
142
+ throw new Error(errorData.message || `Notion API error: ${response.status}`);
143
+ }
144
+
145
+ const data = (await response.json()) as NotionPage;
146
+
147
+ return {
148
+ data: {
149
+ id: data.id,
150
+ url: data.url,
151
+ title: params.title,
152
+ },
153
+ };
154
+ } catch (error) {
155
+ return {
156
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
157
+ };
158
+ }
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Search pages tool
164
+ */
165
+ const NotionSearchParameters = z.object({
166
+ query: z.string().describe('Search query string'),
167
+ filter: z.enum(['page', 'database']).optional().describe('Filter by type'),
168
+ });
169
+
170
+ export class NotionSearchTool extends BaseNotionTool<typeof NotionSearchParameters> {
171
+ constructor(
172
+ config?: Partial<Omit<BaseToolConfig<typeof NotionSearchParameters>, 'parameters'>> & {
173
+ token?: string;
174
+ }
175
+ ) {
176
+ super(
177
+ {
178
+ name: config?.name ?? 'notion.search',
179
+ description: config?.description ?? 'Search for pages and databases in Notion',
180
+ ...config,
181
+ },
182
+ NotionSearchParameters
183
+ );
184
+ }
185
+
186
+ protected async performExecute(
187
+ params: z.infer<typeof NotionSearchParameters>,
188
+ _context: ToolContext
189
+ ): Promise<NotionResult> {
190
+ try {
191
+ const body: Record<string, unknown> = {
192
+ query: params.query,
193
+ };
194
+
195
+ if (params.filter) {
196
+ body.filter = {
197
+ value: params.filter,
198
+ property: 'object',
199
+ };
200
+ }
201
+
202
+ const response = await this.notionRequest('/search', {
203
+ method: 'POST',
204
+ body: JSON.stringify(body),
205
+ });
206
+
207
+ if (!response.ok) {
208
+ const errorData = (await response.json()) as { message?: string };
209
+ throw new Error(errorData.message || `Notion API error: ${response.status}`);
210
+ }
211
+
212
+ const data = (await response.json()) as NotionSearchResult;
213
+
214
+ const results = data.results.map((page) => ({
215
+ id: page.id,
216
+ url: page.url,
217
+ object: page.properties,
218
+ }));
219
+
220
+ return {
221
+ data: {
222
+ count: results.length,
223
+ results,
224
+ },
225
+ };
226
+ } catch (error) {
227
+ return {
228
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
229
+ };
230
+ }
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Update page tool
236
+ */
237
+ const NotionUpdatePageParameters = z.object({
238
+ page_id: z.string().describe('The page ID to update'),
239
+ content: z.string().describe('Content to append to the page'),
240
+ });
241
+
242
+ export class NotionUpdatePageTool extends BaseNotionTool<typeof NotionUpdatePageParameters> {
243
+ constructor(
244
+ config?: Partial<Omit<BaseToolConfig<typeof NotionUpdatePageParameters>, 'parameters'>> & {
245
+ token?: string;
246
+ }
247
+ ) {
248
+ super(
249
+ {
250
+ name: config?.name ?? 'notion.update_page',
251
+ description: config?.description ?? 'Add content to an existing Notion page',
252
+ ...config,
253
+ },
254
+ NotionUpdatePageParameters
255
+ );
256
+ }
257
+
258
+ protected async performExecute(
259
+ params: z.infer<typeof NotionUpdatePageParameters>,
260
+ _context: ToolContext
261
+ ): Promise<NotionResult> {
262
+ try {
263
+ const response = await this.notionRequest(`/blocks/${params.page_id}/children`, {
264
+ method: 'PATCH',
265
+ body: JSON.stringify({
266
+ children: [
267
+ {
268
+ object: 'block',
269
+ type: 'paragraph',
270
+ paragraph: {
271
+ rich_text: [{ type: 'text', text: { content: params.content } }],
272
+ },
273
+ },
274
+ ],
275
+ }),
276
+ });
277
+
278
+ if (!response.ok) {
279
+ const errorData = (await response.json()) as { message?: string };
280
+ throw new Error(errorData.message || `Notion API error: ${response.status}`);
281
+ }
282
+
283
+ return {
284
+ data: {
285
+ pageId: params.page_id,
286
+ message: 'Content added successfully',
287
+ },
288
+ };
289
+ } catch (error) {
290
+ return {
291
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
292
+ };
293
+ }
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Notion toolkit
299
+ */
300
+ export class NotionToolkit {
301
+ static create(options?: {
302
+ token?: string;
303
+ databaseId?: string;
304
+ enableCreatePage?: boolean;
305
+ enableSearch?: boolean;
306
+ enableUpdatePage?: boolean;
307
+ }): Array<NotionCreatePageTool | NotionSearchTool | NotionUpdatePageTool> {
308
+ const tools: Array<NotionCreatePageTool | NotionSearchTool | NotionUpdatePageTool> = [];
309
+
310
+ if (options?.enableCreatePage !== false) {
311
+ tools.push(new NotionCreatePageTool({ token: options?.token, databaseId: options?.databaseId }));
312
+ }
313
+ if (options?.enableSearch !== false) {
314
+ tools.push(new NotionSearchTool({ token: options?.token }));
315
+ }
316
+ if (options?.enableUpdatePage !== false) {
317
+ tools.push(new NotionUpdatePageTool({ token: options?.token }));
318
+ }
319
+
320
+ return tools;
321
+ }
322
+ }
@@ -0,0 +1,236 @@
1
+ /**
2
+ * OpenAI tool implementation - TypeScript OpenAITools
3
+ */
4
+
5
+ import { z } from 'zod';
6
+ import { BaseTool, BaseToolConfig } from './base-tool.js';
7
+ import { ToolContext, ToolCategory } from './types.js';
8
+
9
+ /**
10
+ * OpenAI API types
11
+ */
12
+ interface OpenAIImageResponse {
13
+ data: Array<{
14
+ url?: string;
15
+ b64_json?: string;
16
+ }>;
17
+ }
18
+
19
+ interface OpenAITranscriptionResponse {
20
+ text: string;
21
+ }
22
+
23
+ interface OpenAIResult {
24
+ data?: unknown;
25
+ error?: string;
26
+ }
27
+
28
+ /**
29
+ * Base OpenAI tool with common authentication
30
+ */
31
+ abstract class BaseOpenAITool<TParams extends z.ZodObject<Record<string, z.ZodType>>> extends BaseTool<TParams, OpenAIResult> {
32
+ protected apiKey: string;
33
+ protected baseUrl = 'https://api.openai.com/v1';
34
+
35
+ constructor(
36
+ config: Partial<Omit<BaseToolConfig<TParams>, 'parameters'>> & {
37
+ apiKey?: string;
38
+ },
39
+ params: TParams
40
+ ) {
41
+ super({
42
+ name: config.name || 'openai.tool',
43
+ description: config.description || 'OpenAI tool',
44
+ parameters: params,
45
+ category: config.category || ToolCategory.AI,
46
+ permissions: {
47
+ allowNetwork: true,
48
+ maxExecutionTimeMs: 60000,
49
+ ...config.permissions,
50
+ },
51
+ ...config,
52
+ });
53
+
54
+ this.apiKey = config.apiKey || process.env.OPENAI_API_KEY || '';
55
+
56
+ if (!this.apiKey) {
57
+ throw new Error('OpenAI API key is required. Set OPENAI_API_KEY environment variable or pass apiKey in config.');
58
+ }
59
+ }
60
+
61
+ protected async openAIRequest(endpoint: string, options: RequestInit = {}): Promise<Response> {
62
+ return fetch(`${this.baseUrl}${endpoint}`, {
63
+ ...options,
64
+ headers: {
65
+ Authorization: `Bearer ${this.apiKey}`,
66
+ ...(options.headers || {}),
67
+ },
68
+ });
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Generate image tool
74
+ */
75
+ const OpenAIGenerateImageParameters = z.object({
76
+ prompt: z.string().describe('Text description of the image to generate'),
77
+ size: z.enum(['256x256', '512x512', '1024x1024', '1792x1024', '1024x1792']).optional().default('1024x1024'),
78
+ quality: z.enum(['standard', 'hd']).optional().default('standard'),
79
+ style: z.enum(['vivid', 'natural']).optional().default('vivid'),
80
+ model: z.enum(['dall-e-2', 'dall-e-3', 'gpt-image-1']).optional().default('dall-e-3'),
81
+ });
82
+
83
+ export class OpenAIGenerateImageTool extends BaseOpenAITool<typeof OpenAIGenerateImageParameters> {
84
+ constructor(
85
+ config?: Partial<Omit<BaseToolConfig<typeof OpenAIGenerateImageParameters>, 'parameters'>> & {
86
+ apiKey?: string;
87
+ }
88
+ ) {
89
+ super(
90
+ {
91
+ name: config?.name ?? 'openai.generate_image',
92
+ description: config?.description ?? 'Generate an image using OpenAI DALL-E',
93
+ ...config,
94
+ },
95
+ OpenAIGenerateImageParameters
96
+ );
97
+ }
98
+
99
+ protected async performExecute(
100
+ params: z.infer<typeof OpenAIGenerateImageParameters>,
101
+ _context: ToolContext
102
+ ): Promise<OpenAIResult> {
103
+ try {
104
+ const body: Record<string, unknown> = {
105
+ model: params.model,
106
+ prompt: params.prompt,
107
+ n: 1,
108
+ size: params.size,
109
+ };
110
+
111
+ if (params.model === 'dall-e-3') {
112
+ body.quality = params.quality;
113
+ body.style = params.style;
114
+ }
115
+
116
+ const response = await this.openAIRequest('/images/generations', {
117
+ method: 'POST',
118
+ headers: {
119
+ 'Content-Type': 'application/json',
120
+ },
121
+ body: JSON.stringify(body),
122
+ });
123
+
124
+ if (!response.ok) {
125
+ const errorData = (await response.json()) as { error?: { message?: string } };
126
+ throw new Error(errorData.error?.message || `OpenAI API error: ${response.status}`);
127
+ }
128
+
129
+ const data = (await response.json()) as OpenAIImageResponse;
130
+ const imageData = data.data[0];
131
+
132
+ return {
133
+ data: {
134
+ url: imageData.url,
135
+ b64_json: imageData.b64_json,
136
+ prompt: params.prompt,
137
+ },
138
+ };
139
+ } catch (error) {
140
+ return {
141
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
142
+ };
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Transcribe audio tool
149
+ */
150
+ const OpenAITranscribeAudioParameters = z.object({
151
+ audio_url: z.string().describe('URL or path to the audio file'),
152
+ model: z.enum(['whisper-1']).optional().default('whisper-1'),
153
+ language: z.string().optional().describe('Language code (e.g., en, es)'),
154
+ });
155
+
156
+ export class OpenAITranscribeAudioTool extends BaseOpenAITool<typeof OpenAITranscribeAudioParameters> {
157
+ constructor(
158
+ config?: Partial<Omit<BaseToolConfig<typeof OpenAITranscribeAudioParameters>, 'parameters'>> & {
159
+ apiKey?: string;
160
+ }
161
+ ) {
162
+ super(
163
+ {
164
+ name: config?.name ?? 'openai.transcribe_audio',
165
+ description: config?.description ?? 'Transcribe audio using OpenAI Whisper',
166
+ ...config,
167
+ },
168
+ OpenAITranscribeAudioParameters
169
+ );
170
+ }
171
+
172
+ protected async performExecute(
173
+ params: z.infer<typeof OpenAITranscribeAudioParameters>,
174
+ _context: ToolContext
175
+ ): Promise<OpenAIResult> {
176
+ try {
177
+ // For URL-based audio, we need to fetch it first
178
+ const audioResponse = await fetch(params.audio_url);
179
+ if (!audioResponse.ok) {
180
+ throw new Error(`Failed to fetch audio: ${audioResponse.status}`);
181
+ }
182
+
183
+ const audioBlob = await audioResponse.blob();
184
+ const formData = new FormData();
185
+ formData.append('file', audioBlob, 'audio.mp3');
186
+ formData.append('model', params.model);
187
+ if (params.language) {
188
+ formData.append('language', params.language);
189
+ }
190
+
191
+ const response = await this.openAIRequest('/audio/transcriptions', {
192
+ method: 'POST',
193
+ body: formData,
194
+ });
195
+
196
+ if (!response.ok) {
197
+ const errorData = (await response.json()) as { error?: { message?: string } };
198
+ throw new Error(errorData.error?.message || `OpenAI API error: ${response.status}`);
199
+ }
200
+
201
+ const data = (await response.json()) as OpenAITranscriptionResponse;
202
+
203
+ return {
204
+ data: {
205
+ text: data.text,
206
+ },
207
+ };
208
+ } catch (error) {
209
+ return {
210
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
211
+ };
212
+ }
213
+ }
214
+ }
215
+
216
+ /**
217
+ * OpenAI toolkit
218
+ */
219
+ export class OpenAIToolkit {
220
+ static create(options?: {
221
+ apiKey?: string;
222
+ enableImageGeneration?: boolean;
223
+ enableTranscription?: boolean;
224
+ }): Array<OpenAIGenerateImageTool | OpenAITranscribeAudioTool> {
225
+ const tools: Array<OpenAIGenerateImageTool | OpenAITranscribeAudioTool> = [];
226
+
227
+ if (options?.enableImageGeneration !== false) {
228
+ tools.push(new OpenAIGenerateImageTool({ apiKey: options?.apiKey }));
229
+ }
230
+ if (options?.enableTranscription !== false) {
231
+ tools.push(new OpenAITranscribeAudioTool({ apiKey: options?.apiKey }));
232
+ }
233
+
234
+ return tools;
235
+ }
236
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Tool registry implementation and tool provider helpers.
3
+ */
4
+
5
+ import { ToolRegistry, Tool, ToolCategory } from './types.js';
6
+ import type { EntityId } from '../core/types.js';
7
+
8
+ /** Pass an array or registry when configuring agents. */
9
+ export type ToolProvider = Tool[] | ToolRegistry;
10
+
11
+ /** Normalize tools to a ToolRegistry (extensible: plug any tools). */
12
+ export function toToolRegistry(tools: ToolProvider): ToolRegistry {
13
+ if (Array.isArray(tools)) {
14
+ const reg = new ToolRegistryImpl();
15
+ for (const t of tools) reg.register(t);
16
+ return reg;
17
+ }
18
+ return tools;
19
+ }
20
+
21
+ /**
22
+ * Implementation of ToolRegistry
23
+ */
24
+ export class ToolRegistryImpl implements ToolRegistry {
25
+ private tools: Map<EntityId, Tool> = new Map();
26
+ private nameIndex: Map<string, EntityId> = new Map();
27
+
28
+ /**
29
+ * Register a tool
30
+ */
31
+ register(tool: Tool): void {
32
+ if (this.tools.has(tool.id)) {
33
+ throw new Error(`Tool with ID ${tool.id} is already registered`);
34
+ }
35
+
36
+ if (this.nameIndex.has(tool.name)) {
37
+ throw new Error(`Tool with name ${tool.name} is already registered`);
38
+ }
39
+
40
+ this.tools.set(tool.id, tool);
41
+ this.nameIndex.set(tool.name, tool.id);
42
+ }
43
+
44
+ /**
45
+ * Unregister a tool by ID
46
+ */
47
+ unregister(toolId: EntityId): boolean {
48
+ const tool = this.tools.get(toolId);
49
+ if (!tool) {
50
+ return false;
51
+ }
52
+
53
+ this.tools.delete(toolId);
54
+ this.nameIndex.delete(tool.name);
55
+ return true;
56
+ }
57
+
58
+ /**
59
+ * Get a tool by ID
60
+ */
61
+ get(toolId: EntityId): Tool | undefined {
62
+ return this.tools.get(toolId);
63
+ }
64
+
65
+ /**
66
+ * Get a tool by name
67
+ */
68
+ getByName(name: string): Tool | undefined {
69
+ const id = this.nameIndex.get(name);
70
+ if (!id) {
71
+ return undefined;
72
+ }
73
+ return this.tools.get(id);
74
+ }
75
+
76
+ /**
77
+ * List all registered tools
78
+ */
79
+ list(): Tool[] {
80
+ return Array.from(this.tools.values());
81
+ }
82
+
83
+ /**
84
+ * List tools by category
85
+ */
86
+ listByCategory(category: ToolCategory): Tool[] {
87
+ return this.list().filter(tool => tool.category === category);
88
+ }
89
+
90
+ /**
91
+ * Search tools by name or description
92
+ */
93
+ search(query: string): Tool[] {
94
+ const lowerQuery = query.toLowerCase();
95
+ return this.list().filter(
96
+ tool =>
97
+ tool.name.toLowerCase().includes(lowerQuery) ||
98
+ tool.description.toLowerCase().includes(lowerQuery) ||
99
+ (tool.tags?.some(tag => tag.toLowerCase().includes(lowerQuery)) ?? false)
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Check if a tool is registered
105
+ */
106
+ has(toolId: EntityId): boolean {
107
+ return this.tools.has(toolId);
108
+ }
109
+
110
+ /**
111
+ * Check if a tool name is registered
112
+ */
113
+ hasName(name: string): boolean {
114
+ return this.nameIndex.has(name);
115
+ }
116
+
117
+ /**
118
+ * Clear all registered tools
119
+ */
120
+ clear(): void {
121
+ this.tools.clear();
122
+ this.nameIndex.clear();
123
+ }
124
+
125
+ /**
126
+ * Get the number of registered tools
127
+ */
128
+ size(): number {
129
+ return this.tools.size;
130
+ }
131
+ }