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,394 @@
1
+ /**
2
+ * File system tools implementation
3
+ */
4
+
5
+ import { z } from 'zod';
6
+ import { BaseTool, BaseToolConfig } from './base-tool.js';
7
+ import { ToolContext, ToolCategory } from './types.js';
8
+ import * as fs from 'fs/promises';
9
+ import * as path from 'path';
10
+
11
+ /**
12
+ * Base configuration for file tools
13
+ */
14
+ export interface FileToolConfig extends Partial<Omit<BaseToolConfig<any>, 'parameters'>> {
15
+ baseDir?: string;
16
+ }
17
+
18
+ /**
19
+ * Helper to check if path is safe
20
+ */
21
+ async function checkPath(baseDir: string, relativePath: string): Promise<string> {
22
+ const resolvedBase = path.resolve(baseDir);
23
+ const resolvedPath = path.resolve(resolvedBase, relativePath);
24
+
25
+ if (!resolvedPath.startsWith(resolvedBase)) {
26
+ throw new Error(`Access denied: Path ${relativePath} is outside base directory ${baseDir}`);
27
+ }
28
+
29
+ return resolvedPath;
30
+ }
31
+
32
+ // --- Write File Tool ---
33
+
34
+ const WriteFileParameters = z.object({
35
+ fileName: z.string().describe('The name of the file to save to'),
36
+ contents: z.string().describe('The contents to save'),
37
+ overwrite: z.boolean().default(true).describe('Overwrite the file if it already exists'),
38
+ encoding: z.string().default('utf-8').describe('Encoding to use'),
39
+ });
40
+
41
+ export class WriteFileTool extends BaseTool<typeof WriteFileParameters, string> {
42
+ private baseDir: string;
43
+
44
+ constructor(config?: FileToolConfig) {
45
+ super({
46
+ name: config?.name ?? 'fs.write_file',
47
+ description: config?.description ?? 'Saves contents to a file',
48
+ parameters: WriteFileParameters,
49
+ category: config?.category ?? ToolCategory.FILE_SYSTEM,
50
+ permissions: {
51
+ allowFileSystem: true,
52
+ ...config?.permissions,
53
+ },
54
+ ...config,
55
+ });
56
+ this.baseDir = config?.baseDir ?? process.cwd();
57
+ }
58
+
59
+ protected async performExecute(params: z.infer<typeof WriteFileParameters>, _context: ToolContext): Promise<string> {
60
+ const { fileName, contents, overwrite, encoding } = params;
61
+ const filePath = await checkPath(this.baseDir, fileName);
62
+
63
+ // Check if directory exists
64
+ const dir = path.dirname(filePath);
65
+ try {
66
+ await fs.access(dir);
67
+ } catch {
68
+ await fs.mkdir(dir, { recursive: true });
69
+ }
70
+
71
+ // Check overwrite
72
+ try {
73
+ await fs.access(filePath);
74
+ if (!overwrite) {
75
+ return `File ${fileName} already exists`;
76
+ }
77
+ } catch {
78
+ // File doesn't exist, proceed
79
+ }
80
+
81
+ await fs.writeFile(filePath, contents, encoding as BufferEncoding);
82
+ return fileName;
83
+ }
84
+ }
85
+
86
+ // --- Read File Tool ---
87
+
88
+ const ReadFileParameters = z.object({
89
+ fileName: z.string().describe('The name of the file to read'),
90
+ encoding: z.string().default('utf-8').describe('Encoding to use'),
91
+ });
92
+
93
+ export class ReadFileTool extends BaseTool<typeof ReadFileParameters, string> {
94
+ private baseDir: string;
95
+
96
+ constructor(config?: FileToolConfig) {
97
+ super({
98
+ name: config?.name ?? 'fs.read_file',
99
+ description: config?.description ?? 'Reads the contents of a file',
100
+ parameters: ReadFileParameters,
101
+ category: config?.category ?? ToolCategory.FILE_SYSTEM,
102
+ permissions: {
103
+ allowFileSystem: true,
104
+ ...config?.permissions,
105
+ },
106
+ ...config,
107
+ });
108
+ this.baseDir = config?.baseDir ?? process.cwd();
109
+ }
110
+
111
+ protected async performExecute(params: z.infer<typeof ReadFileParameters>, _context: ToolContext): Promise<string> {
112
+ const { fileName, encoding } = params;
113
+ const filePath = await checkPath(this.baseDir, fileName);
114
+
115
+ try {
116
+ const content = await fs.readFile(filePath, encoding as BufferEncoding);
117
+ return content;
118
+ } catch (error: any) {
119
+ throw new Error(`Error reading file: ${error.message}`);
120
+ }
121
+ }
122
+ }
123
+
124
+ // --- Read File Chunk Tool ---
125
+
126
+ const ReadFileChunkParameters = z.object({
127
+ fileName: z.string().describe('The name of the file to read'),
128
+ startLine: z.number().int().min(0).describe('Number of first line in the returned chunk'),
129
+ endLine: z.number().int().min(0).describe('Number of the last line in the returned chunk'),
130
+ encoding: z.string().default('utf-8').describe('Encoding to use'),
131
+ });
132
+
133
+ export class ReadFileChunkTool extends BaseTool<typeof ReadFileChunkParameters, string> {
134
+ private baseDir: string;
135
+
136
+ constructor(config?: FileToolConfig) {
137
+ super({
138
+ name: config?.name ?? 'fs.read_file_chunk',
139
+ description: config?.description ?? 'Reads specific lines from a file',
140
+ parameters: ReadFileChunkParameters,
141
+ category: config?.category ?? ToolCategory.FILE_SYSTEM,
142
+ permissions: {
143
+ allowFileSystem: true,
144
+ ...config?.permissions,
145
+ },
146
+ ...config,
147
+ });
148
+ this.baseDir = config?.baseDir ?? process.cwd();
149
+ }
150
+
151
+ protected async performExecute(params: z.infer<typeof ReadFileChunkParameters>, _context: ToolContext): Promise<string> {
152
+ const { fileName, startLine, endLine, encoding } = params;
153
+ const filePath = await checkPath(this.baseDir, fileName);
154
+
155
+ try {
156
+ const content = await fs.readFile(filePath, encoding as BufferEncoding);
157
+ const lines = content.split('\n');
158
+
159
+ // Adjust endLine to not exceed total lines
160
+ const actualEndLine = Math.min(endLine, lines.length - 1);
161
+
162
+ if (startLine > actualEndLine) {
163
+ return "";
164
+ }
165
+
166
+ return lines.slice(startLine, actualEndLine + 1).join('\n');
167
+ } catch (error: any) {
168
+ throw new Error(`Error reading file chunk: ${error.message}`);
169
+ }
170
+ }
171
+ }
172
+
173
+ // --- Replace File Chunk Tool ---
174
+
175
+ const ReplaceFileChunkParameters = z.object({
176
+ fileName: z.string().describe('The name of the file to process'),
177
+ startLine: z.number().int().min(0).describe('Number of first line in the replaced chunk'),
178
+ endLine: z.number().int().min(0).describe('Number of the last line in the replaced chunk'),
179
+ chunk: z.string().describe('String to be inserted instead of lines from start_line to end_line'),
180
+ encoding: z.string().default('utf-8').describe('Encoding to use'),
181
+ });
182
+
183
+ export class UpdateFileChunkTool extends BaseTool<typeof ReplaceFileChunkParameters, string> {
184
+ private baseDir: string;
185
+
186
+ constructor(config?: FileToolConfig) {
187
+ super({
188
+ name: config?.name ?? 'fs.update_file_chunk',
189
+ description: config?.description ?? 'Replaces specific lines in a file with new content',
190
+ parameters: ReplaceFileChunkParameters,
191
+ category: config?.category ?? ToolCategory.FILE_SYSTEM,
192
+ permissions: {
193
+ allowFileSystem: true,
194
+ ...config?.permissions,
195
+ },
196
+ ...config,
197
+ });
198
+ this.baseDir = config?.baseDir ?? process.cwd();
199
+ }
200
+
201
+ protected async performExecute(params: z.infer<typeof ReplaceFileChunkParameters>, _context: ToolContext): Promise<string> {
202
+ const { fileName, startLine, endLine, chunk, encoding } = params;
203
+ const filePath = await checkPath(this.baseDir, fileName);
204
+
205
+ try {
206
+ const content = await fs.readFile(filePath, encoding as BufferEncoding);
207
+ const lines = content.split('\n');
208
+
209
+ const start = lines.slice(0, startLine);
210
+ const end = lines.slice(endLine + 1);
211
+
212
+ const newContent = [...start, chunk, ...end].join('\n');
213
+
214
+ await fs.writeFile(filePath, newContent, encoding as BufferEncoding);
215
+ return fileName;
216
+ } catch (error: any) {
217
+ throw new Error(`Error updating file chunk: ${error.message}`);
218
+ }
219
+ }
220
+ }
221
+
222
+
223
+ // --- Delete File Tool ---
224
+
225
+ const DeleteFileParameters = z.object({
226
+ fileName: z.string().describe('Name of the file to delete'),
227
+ });
228
+
229
+ export class DeleteFileTool extends BaseTool<typeof DeleteFileParameters, string> {
230
+ private baseDir: string;
231
+
232
+ constructor(config?: FileToolConfig) {
233
+ super({
234
+ name: config?.name ?? 'fs.delete_file',
235
+ description: config?.description ?? 'Deletes a file',
236
+ parameters: DeleteFileParameters,
237
+ category: config?.category ?? ToolCategory.FILE_SYSTEM,
238
+ permissions: {
239
+ allowFileSystem: true,
240
+ ...config?.permissions,
241
+ },
242
+ ...config,
243
+ });
244
+ this.baseDir = config?.baseDir ?? process.cwd();
245
+ }
246
+
247
+ protected async performExecute(params: z.infer<typeof DeleteFileParameters>, _context: ToolContext): Promise<string> {
248
+ const { fileName } = params;
249
+ const filePath = await checkPath(this.baseDir, fileName);
250
+
251
+ try {
252
+ const stat = await fs.stat(filePath);
253
+ if (stat.isDirectory()) {
254
+ await fs.rmdir(filePath);
255
+ } else {
256
+ await fs.unlink(filePath);
257
+ }
258
+ return "";
259
+ } catch (error: any) {
260
+ throw new Error(`Error deleting file: ${error.message}`);
261
+ }
262
+ }
263
+ }
264
+
265
+ // --- List Files Tool ---
266
+
267
+ const ListFilesParameters = z.object({
268
+ directory: z.string().optional().default('.').describe('Name of directory to list'),
269
+ });
270
+
271
+ export class ListFilesTool extends BaseTool<typeof ListFilesParameters, string> {
272
+ private baseDir: string;
273
+
274
+ constructor(config?: FileToolConfig) {
275
+ super({
276
+ name: config?.name ?? 'fs.list_files',
277
+ description: config?.description ?? 'Returns a list of files in a directory',
278
+ parameters: ListFilesParameters,
279
+ category: config?.category ?? ToolCategory.FILE_SYSTEM,
280
+ permissions: {
281
+ allowFileSystem: true,
282
+ ...config?.permissions,
283
+ },
284
+ ...config,
285
+ });
286
+ this.baseDir = config?.baseDir ?? process.cwd();
287
+ }
288
+
289
+ protected async performExecute(params: z.infer<typeof ListFilesParameters>, _context: ToolContext): Promise<string> {
290
+ const { directory } = params;
291
+ const targetDir = await checkPath(this.baseDir, directory ?? '.');
292
+
293
+ try {
294
+ const files = await fs.readdir(targetDir, { withFileTypes: true, recursive: false });
295
+ // In Agno it returns paths relative to base_dir, but we are inside the directory.
296
+ // Let's just return filenames for now or relative to baseDir.
297
+
298
+ const paths = files.map(f => {
299
+ const fullPath = path.join(targetDir, f.name);
300
+ return path.relative(this.baseDir, fullPath);
301
+ });
302
+
303
+ return JSON.stringify(paths, null, 4);
304
+ } catch (error: any) {
305
+ return "{}"; // Match Agno behavior on error? Or throw? Agno returns "{}" on error inside safe check, but error msg on exception.
306
+ }
307
+ }
308
+ }
309
+
310
+ // --- Search Files Tool ---
311
+
312
+ const SearchFilesParameters = z.object({
313
+ pattern: z.string().describe('The pattern to search for, e.g. "*.txt", "src/**/*.ts"'),
314
+ });
315
+
316
+ // Since node doesn't have a built-in glob, we might need to implement a simple recursive search or use a library if available.
317
+ // Given the constraints, I will implement a simple recursive search that supports basic globs or just regex if easier,
318
+ // but the prompt implies a port. Agno uses `pathlib.Path.glob`.
319
+ // I'll implement a basic walker that matches simple extensions or just lists all files and filters.
320
+ // For now, let's just list recursively and filter by extension/name if simple.
321
+ // Or I can just check if `glob` package is available? I don't see it in package.json (I haven't checked).
322
+ // I'll use a simple recursive directory walker and manual matching.
323
+
324
+ export class SearchFilesTool extends BaseTool<typeof SearchFilesParameters, string> {
325
+ private baseDir: string;
326
+
327
+ constructor(config?: FileToolConfig) {
328
+ super({
329
+ name: config?.name ?? 'fs.search_files',
330
+ description: config?.description ?? 'Searches for files in the base directory that match the pattern',
331
+ parameters: SearchFilesParameters,
332
+ category: config?.category ?? ToolCategory.FILE_SYSTEM,
333
+ permissions: {
334
+ allowFileSystem: true,
335
+ ...config?.permissions,
336
+ },
337
+ ...config,
338
+ });
339
+ this.baseDir = config?.baseDir ?? process.cwd();
340
+ }
341
+
342
+ protected async performExecute(params: z.infer<typeof SearchFilesParameters>, _context: ToolContext): Promise<string> {
343
+ const { pattern } = params;
344
+
345
+ if (!pattern || !pattern.trim()) {
346
+ return "Error: Pattern cannot be empty";
347
+ }
348
+
349
+ try {
350
+ const files: string[] = [];
351
+
352
+ // Simple recursive walker
353
+ const walk = async (dir: string) => {
354
+ const entries = await fs.readdir(dir, { withFileTypes: true });
355
+ for (const entry of entries) {
356
+ const fullPath = path.join(dir, entry.name);
357
+ if (entry.isDirectory()) {
358
+ await walk(fullPath);
359
+ } else {
360
+ // Simple pattern matching
361
+ // If pattern contains *, convert to regex
362
+ // This is a naive implementation
363
+ if (this.matches(entry.name, pattern) || this.matches(path.relative(this.baseDir, fullPath), pattern)) {
364
+ files.push(path.relative(this.baseDir, fullPath));
365
+ }
366
+ }
367
+ }
368
+ };
369
+
370
+ await walk(this.baseDir);
371
+
372
+ const result = {
373
+ pattern,
374
+ matches_found: files.length,
375
+ files
376
+ };
377
+ return JSON.stringify(result, null, 2);
378
+
379
+ } catch (error: any) {
380
+ return `Error searching files with pattern '${pattern}': ${error.message}`;
381
+ }
382
+ }
383
+
384
+ private matches(text: string, pattern: string): boolean {
385
+ // Very basic wildcard support
386
+ // Convert glob to regex: . -> \., * -> .*, ? -> .
387
+ const regexString = pattern
388
+ .replace(/\./g, '\\.')
389
+ .replace(/\*/g, '.*')
390
+ .replace(/\?/g, '.');
391
+ const regex = new RegExp(`^${regexString}$`);
392
+ return regex.test(text);
393
+ }
394
+ }