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,432 @@
1
+ /**
2
+ * GitHub tool implementation - TypeScript GithubTools
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
+ * GitHub API types
11
+ */
12
+ interface GitHubRepository {
13
+ full_name: string;
14
+ description: string | null;
15
+ html_url: string;
16
+ stargazers_count: number;
17
+ forks_count: number;
18
+ language: string | null;
19
+ open_issues_count: number;
20
+ default_branch: string;
21
+ private: boolean;
22
+ }
23
+
24
+ interface GitHubIssue {
25
+ number: number;
26
+ title: string;
27
+ state: string;
28
+ html_url: string;
29
+ user: { login: string };
30
+ body: string | null;
31
+ labels: Array<{ name: string }>;
32
+ }
33
+
34
+ interface GitHubPullRequest {
35
+ number: number;
36
+ title: string;
37
+ state: string;
38
+ html_url: string;
39
+ user: { login: string };
40
+ body: string | null;
41
+ head: { ref: string };
42
+ base: { ref: string };
43
+ }
44
+
45
+ interface GitHubSearchResult {
46
+ items: Array<{
47
+ full_name: string;
48
+ description: string | null;
49
+ html_url: string;
50
+ stargazers_count: number;
51
+ forks_count: number;
52
+ language: string | null;
53
+ }>;
54
+ total_count: number;
55
+ }
56
+
57
+ interface GitHubResult {
58
+ data?: unknown;
59
+ error?: string;
60
+ }
61
+
62
+ // Base GitHub API URL
63
+ const GITHUB_API_BASE = 'https://api.github.com';
64
+
65
+ /**
66
+ * Helper function to make authenticated GitHub API requests
67
+ */
68
+ async function githubRequest(
69
+ endpoint: string,
70
+ token?: string,
71
+ options: RequestInit = {}
72
+ ): Promise<Response> {
73
+ const headers: Record<string, string> = {
74
+ Accept: 'application/vnd.github.v3+json',
75
+ 'User-Agent': 'AgentFramework/1.0',
76
+ ...((options.headers as Record<string, string>) || {}),
77
+ };
78
+
79
+ if (token) {
80
+ headers.Authorization = `Bearer ${token}`;
81
+ }
82
+
83
+ return fetch(`${GITHUB_API_BASE}${endpoint}`, {
84
+ ...options,
85
+ headers,
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Search repositories tool
91
+ */
92
+ const GitHubSearchRepositoriesParameters = z.object({
93
+ query: z.string().describe('Search query for repositories'),
94
+ sort: z.enum(['stars', 'forks', 'updated']).optional().default('stars'),
95
+ order: z.enum(['asc', 'desc']).optional().default('desc'),
96
+ per_page: z.number().min(1).max(100).optional().default(30),
97
+ });
98
+
99
+ export class GitHubSearchRepositoriesTool extends BaseTool<typeof GitHubSearchRepositoriesParameters, GitHubResult> {
100
+ private token?: string;
101
+
102
+ constructor(
103
+ config?: Partial<Omit<BaseToolConfig<typeof GitHubSearchRepositoriesParameters>, 'parameters'>> & {
104
+ token?: string;
105
+ }
106
+ ) {
107
+ super({
108
+ name: config?.name ?? 'github.search_repositories',
109
+ description: config?.description ?? 'Search for repositories on GitHub',
110
+ parameters: GitHubSearchRepositoriesParameters,
111
+ category: config?.category ?? ToolCategory.API,
112
+ permissions: {
113
+ allowNetwork: true,
114
+ maxExecutionTimeMs: 30000,
115
+ ...config?.permissions,
116
+ },
117
+ ...config,
118
+ });
119
+ this.token = config?.token || process.env.GITHUB_ACCESS_TOKEN;
120
+ }
121
+
122
+ protected async performExecute(
123
+ params: z.infer<typeof GitHubSearchRepositoriesParameters>,
124
+ _context: ToolContext
125
+ ): Promise<GitHubResult> {
126
+ try {
127
+ const response = await githubRequest(
128
+ `/search/repositories?q=${encodeURIComponent(params.query)}&sort=${params.sort}&order=${params.order}&per_page=${params.per_page}`,
129
+ this.token
130
+ );
131
+
132
+ if (!response.ok) {
133
+ throw new Error(`GitHub API error: ${response.status}`);
134
+ }
135
+
136
+ const data = (await response.json()) as GitHubSearchResult;
137
+ return { data };
138
+ } catch (error) {
139
+ return {
140
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
141
+ };
142
+ }
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Get repository tool
148
+ */
149
+ const GitHubGetRepositoryParameters = z.object({
150
+ owner: z.string().describe('Repository owner (username or organization)'),
151
+ repo: z.string().describe('Repository name'),
152
+ });
153
+
154
+ export class GitHubGetRepositoryTool extends BaseTool<typeof GitHubGetRepositoryParameters, GitHubResult> {
155
+ private token?: string;
156
+
157
+ constructor(
158
+ config?: Partial<Omit<BaseToolConfig<typeof GitHubGetRepositoryParameters>, 'parameters'>> & {
159
+ token?: string;
160
+ }
161
+ ) {
162
+ super({
163
+ name: config?.name ?? 'github.get_repository',
164
+ description: config?.description ?? 'Get details of a specific repository',
165
+ parameters: GitHubGetRepositoryParameters,
166
+ category: config?.category ?? ToolCategory.API,
167
+ permissions: {
168
+ allowNetwork: true,
169
+ maxExecutionTimeMs: 30000,
170
+ ...config?.permissions,
171
+ },
172
+ ...config,
173
+ });
174
+ this.token = config?.token || process.env.GITHUB_ACCESS_TOKEN;
175
+ }
176
+
177
+ protected async performExecute(
178
+ params: z.infer<typeof GitHubGetRepositoryParameters>,
179
+ _context: ToolContext
180
+ ): Promise<GitHubResult> {
181
+ try {
182
+ const response = await githubRequest(
183
+ `/repos/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.repo)}`,
184
+ this.token
185
+ );
186
+
187
+ if (!response.ok) {
188
+ throw new Error(`GitHub API error: ${response.status}`);
189
+ }
190
+
191
+ const data = (await response.json()) as GitHubRepository;
192
+ return { data };
193
+ } catch (error) {
194
+ return {
195
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
196
+ };
197
+ }
198
+ }
199
+ }
200
+
201
+ /**
202
+ * List issues tool
203
+ */
204
+ const GitHubListIssuesParameters = z.object({
205
+ owner: z.string().describe('Repository owner'),
206
+ repo: z.string().describe('Repository name'),
207
+ state: z.enum(['open', 'closed', 'all']).optional().default('open'),
208
+ per_page: z.number().min(1).max(100).optional().default(30),
209
+ });
210
+
211
+ export class GitHubListIssuesTool extends BaseTool<typeof GitHubListIssuesParameters, GitHubResult> {
212
+ private token?: string;
213
+
214
+ constructor(
215
+ config?: Partial<Omit<BaseToolConfig<typeof GitHubListIssuesParameters>, 'parameters'>> & {
216
+ token?: string;
217
+ }
218
+ ) {
219
+ super({
220
+ name: config?.name ?? 'github.list_issues',
221
+ description: config?.description ?? 'List issues in a repository',
222
+ parameters: GitHubListIssuesParameters,
223
+ category: config?.category ?? ToolCategory.API,
224
+ permissions: {
225
+ allowNetwork: true,
226
+ maxExecutionTimeMs: 30000,
227
+ ...config?.permissions,
228
+ },
229
+ ...config,
230
+ });
231
+ this.token = config?.token || process.env.GITHUB_ACCESS_TOKEN;
232
+ }
233
+
234
+ protected async performExecute(
235
+ params: z.infer<typeof GitHubListIssuesParameters>,
236
+ _context: ToolContext
237
+ ): Promise<GitHubResult> {
238
+ try {
239
+ const response = await githubRequest(
240
+ `/repos/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.repo)}/issues?state=${params.state}&per_page=${params.per_page}`,
241
+ this.token
242
+ );
243
+
244
+ if (!response.ok) {
245
+ throw new Error(`GitHub API error: ${response.status}`);
246
+ }
247
+
248
+ const data = (await response.json()) as GitHubIssue[];
249
+ return { data };
250
+ } catch (error) {
251
+ return {
252
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
253
+ };
254
+ }
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Create issue tool
260
+ */
261
+ const GitHubCreateIssueParameters = z.object({
262
+ owner: z.string().describe('Repository owner'),
263
+ repo: z.string().describe('Repository name'),
264
+ title: z.string().describe('Issue title'),
265
+ body: z.string().optional().describe('Issue body/description'),
266
+ labels: z.array(z.string()).optional().describe('Labels to apply to the issue'),
267
+ });
268
+
269
+ export class GitHubCreateIssueTool extends BaseTool<typeof GitHubCreateIssueParameters, GitHubResult> {
270
+ private token?: string;
271
+
272
+ constructor(
273
+ config?: Partial<Omit<BaseToolConfig<typeof GitHubCreateIssueParameters>, 'parameters'>> & {
274
+ token?: string;
275
+ }
276
+ ) {
277
+ super({
278
+ name: config?.name ?? 'github.create_issue',
279
+ description: config?.description ?? 'Create a new issue in a repository',
280
+ parameters: GitHubCreateIssueParameters,
281
+ category: config?.category ?? ToolCategory.API,
282
+ permissions: {
283
+ allowNetwork: true,
284
+ maxExecutionTimeMs: 30000,
285
+ ...config?.permissions,
286
+ },
287
+ ...config,
288
+ });
289
+ this.token = config?.token || process.env.GITHUB_ACCESS_TOKEN;
290
+
291
+ if (!this.token) {
292
+ throw new Error('GitHub access token is required for creating issues');
293
+ }
294
+ }
295
+
296
+ protected async performExecute(
297
+ params: z.infer<typeof GitHubCreateIssueParameters>,
298
+ _context: ToolContext
299
+ ): Promise<GitHubResult> {
300
+ try {
301
+ const response = await githubRequest(
302
+ `/repos/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.repo)}/issues`,
303
+ this.token,
304
+ {
305
+ method: 'POST',
306
+ headers: {
307
+ 'Content-Type': 'application/json',
308
+ },
309
+ body: JSON.stringify({
310
+ title: params.title,
311
+ body: params.body,
312
+ labels: params.labels,
313
+ }),
314
+ }
315
+ );
316
+
317
+ if (!response.ok) {
318
+ throw new Error(`GitHub API error: ${response.status}`);
319
+ }
320
+
321
+ const data = (await response.json()) as GitHubIssue;
322
+ return { data };
323
+ } catch (error) {
324
+ return {
325
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
326
+ };
327
+ }
328
+ }
329
+ }
330
+
331
+ /**
332
+ * List pull requests tool
333
+ */
334
+ const GitHubListPullRequestsParameters = z.object({
335
+ owner: z.string().describe('Repository owner'),
336
+ repo: z.string().describe('Repository name'),
337
+ state: z.enum(['open', 'closed', 'all']).optional().default('open'),
338
+ per_page: z.number().min(1).max(100).optional().default(30),
339
+ });
340
+
341
+ export class GitHubListPullRequestsTool extends BaseTool<typeof GitHubListPullRequestsParameters, GitHubResult> {
342
+ private token?: string;
343
+
344
+ constructor(
345
+ config?: Partial<Omit<BaseToolConfig<typeof GitHubListPullRequestsParameters>, 'parameters'>> & {
346
+ token?: string;
347
+ }
348
+ ) {
349
+ super({
350
+ name: config?.name ?? 'github.list_pull_requests',
351
+ description: config?.description ?? 'List pull requests in a repository',
352
+ parameters: GitHubListPullRequestsParameters,
353
+ category: config?.category ?? ToolCategory.API,
354
+ permissions: {
355
+ allowNetwork: true,
356
+ maxExecutionTimeMs: 30000,
357
+ ...config?.permissions,
358
+ },
359
+ ...config,
360
+ });
361
+ this.token = config?.token || process.env.GITHUB_ACCESS_TOKEN;
362
+ }
363
+
364
+ protected async performExecute(
365
+ params: z.infer<typeof GitHubListPullRequestsParameters>,
366
+ _context: ToolContext
367
+ ): Promise<GitHubResult> {
368
+ try {
369
+ const response = await githubRequest(
370
+ `/repos/${encodeURIComponent(params.owner)}/${encodeURIComponent(params.repo)}/pulls?state=${params.state}&per_page=${params.per_page}`,
371
+ this.token
372
+ );
373
+
374
+ if (!response.ok) {
375
+ throw new Error(`GitHub API error: ${response.status}`);
376
+ }
377
+
378
+ const data = (await response.json()) as GitHubPullRequest[];
379
+ return { data };
380
+ } catch (error) {
381
+ return {
382
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
383
+ };
384
+ }
385
+ }
386
+ }
387
+
388
+ /**
389
+ * GitHub toolkit
390
+ */
391
+ export class GitHubToolkit {
392
+ static create(options?: {
393
+ token?: string;
394
+ enableSearch?: boolean;
395
+ enableGetRepo?: boolean;
396
+ enableListIssues?: boolean;
397
+ enableCreateIssue?: boolean;
398
+ enableListPRs?: boolean;
399
+ }): Array<
400
+ | GitHubSearchRepositoriesTool
401
+ | GitHubGetRepositoryTool
402
+ | GitHubListIssuesTool
403
+ | GitHubCreateIssueTool
404
+ | GitHubListPullRequestsTool
405
+ > {
406
+ const tools: Array<
407
+ | GitHubSearchRepositoriesTool
408
+ | GitHubGetRepositoryTool
409
+ | GitHubListIssuesTool
410
+ | GitHubCreateIssueTool
411
+ | GitHubListPullRequestsTool
412
+ > = [];
413
+
414
+ if (options?.enableSearch !== false) {
415
+ tools.push(new GitHubSearchRepositoriesTool({ token: options?.token }));
416
+ }
417
+ if (options?.enableGetRepo !== false) {
418
+ tools.push(new GitHubGetRepositoryTool({ token: options?.token }));
419
+ }
420
+ if (options?.enableListIssues !== false) {
421
+ tools.push(new GitHubListIssuesTool({ token: options?.token }));
422
+ }
423
+ if (options?.enableCreateIssue !== false) {
424
+ tools.push(new GitHubCreateIssueTool({ token: options?.token }));
425
+ }
426
+ if (options?.enableListPRs !== false) {
427
+ tools.push(new GitHubListPullRequestsTool({ token: options?.token }));
428
+ }
429
+
430
+ return tools;
431
+ }
432
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * HackerNews tool implementation - TypeScript HackerNewsTools
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
+ * HackerNews story
11
+ */
12
+ interface HackerNewsStory {
13
+ id: number;
14
+ title?: string;
15
+ url?: string;
16
+ score?: number;
17
+ by?: string;
18
+ time?: number;
19
+ descendants?: number;
20
+ type: string;
21
+ username?: string;
22
+ }
23
+
24
+ /**
25
+ * HackerNews user details
26
+ */
27
+ interface HackerNewsUser {
28
+ id?: string;
29
+ karma?: number;
30
+ about?: string;
31
+ total_items_submitted?: number;
32
+ }
33
+
34
+ interface HackerNewsResult {
35
+ stories?: HackerNewsStory[];
36
+ user?: HackerNewsUser;
37
+ error?: string;
38
+ }
39
+
40
+ /**
41
+ * Parameters for getting top stories
42
+ */
43
+ const HackerNewsTopStoriesParameters = z.object({
44
+ num_stories: z.number().min(1).max(50).optional().default(10).describe('Number of stories to return'),
45
+ });
46
+
47
+ /**
48
+ * Parameters for getting user details
49
+ */
50
+ const HackerNewsUserParameters = z.object({
51
+ username: z.string().describe('Username of the Hacker News user'),
52
+ });
53
+
54
+ /**
55
+ * HackerNews top stories tool
56
+ */
57
+ export class HackerNewsTopStoriesTool extends BaseTool<typeof HackerNewsTopStoriesParameters, HackerNewsResult> {
58
+ constructor(config?: Partial<Omit<BaseToolConfig<typeof HackerNewsTopStoriesParameters>, 'parameters'>>) {
59
+ super({
60
+ name: config?.name ?? 'hackernews.top_stories',
61
+ description: config?.description ?? 'Get top stories from Hacker News',
62
+ parameters: HackerNewsTopStoriesParameters,
63
+ category: config?.category ?? ToolCategory.WEB,
64
+ permissions: {
65
+ allowNetwork: true,
66
+ maxExecutionTimeMs: 30000,
67
+ ...config?.permissions,
68
+ },
69
+ ...config,
70
+ });
71
+ }
72
+
73
+ protected async performExecute(
74
+ params: z.infer<typeof HackerNewsTopStoriesParameters>,
75
+ _context: ToolContext
76
+ ): Promise<HackerNewsResult> {
77
+ try {
78
+ // Fetch top story IDs
79
+ const response = await fetch('https://hacker-news.firebaseio.com/v0/topstories.json');
80
+
81
+ if (!response.ok) {
82
+ throw new Error(`Failed to fetch top stories: ${response.status}`);
83
+ }
84
+
85
+ const storyIds = (await response.json()) as number[];
86
+ const topIds = storyIds.slice(0, params.num_stories);
87
+
88
+ // Fetch story details
89
+ const stories: HackerNewsStory[] = [];
90
+ for (const storyId of topIds) {
91
+ try {
92
+ const storyResponse = await fetch(`https://hacker-news.firebaseio.com/v0/item/${storyId}.json`);
93
+ if (storyResponse.ok) {
94
+ const story = (await storyResponse.json()) as HackerNewsStory;
95
+ story.username = story.by;
96
+ stories.push(story);
97
+ }
98
+ } catch {
99
+ // Skip failed story fetches
100
+ }
101
+ }
102
+
103
+ return { stories };
104
+ } catch (error) {
105
+ return {
106
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
107
+ };
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * HackerNews user details tool
114
+ */
115
+ export class HackerNewsUserTool extends BaseTool<typeof HackerNewsUserParameters, HackerNewsResult> {
116
+ constructor(config?: Partial<Omit<BaseToolConfig<typeof HackerNewsUserParameters>, 'parameters'>>) {
117
+ super({
118
+ name: config?.name ?? 'hackernews.user_details',
119
+ description: config?.description ?? 'Get details of a Hacker News user',
120
+ parameters: HackerNewsUserParameters,
121
+ category: config?.category ?? ToolCategory.WEB,
122
+ permissions: {
123
+ allowNetwork: true,
124
+ maxExecutionTimeMs: 30000,
125
+ ...config?.permissions,
126
+ },
127
+ ...config,
128
+ });
129
+ }
130
+
131
+ protected async performExecute(
132
+ params: z.infer<typeof HackerNewsUserParameters>,
133
+ _context: ToolContext
134
+ ): Promise<HackerNewsResult> {
135
+ try {
136
+ const response = await fetch(`https://hacker-news.firebaseio.com/v0/user/${params.username}.json`);
137
+
138
+ if (!response.ok) {
139
+ throw new Error(`Failed to fetch user details: ${response.status}`);
140
+ }
141
+
142
+ const userData = (await response.json()) as {
143
+ id?: string;
144
+ karma?: number;
145
+ about?: string;
146
+ submitted?: number[];
147
+ };
148
+
149
+ if (!userData) {
150
+ return {
151
+ error: `User ${params.username} not found`,
152
+ };
153
+ }
154
+
155
+ const user: HackerNewsUser = {
156
+ id: userData.id,
157
+ karma: userData.karma,
158
+ about: userData.about,
159
+ total_items_submitted: userData.submitted?.length || 0,
160
+ };
161
+
162
+ return { user };
163
+ } catch (error) {
164
+ return {
165
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
166
+ };
167
+ }
168
+ }
169
+ }
170
+
171
+ /**
172
+ * HackerNews toolkit
173
+ */
174
+ export class HackerNewsToolkit {
175
+ static create(options?: { enableTopStories?: boolean; enableUserDetails?: boolean }): Array<HackerNewsTopStoriesTool | HackerNewsUserTool> {
176
+ const tools: Array<HackerNewsTopStoriesTool | HackerNewsUserTool> = [];
177
+
178
+ if (options?.enableTopStories !== false) {
179
+ tools.push(new HackerNewsTopStoriesTool());
180
+ }
181
+ if (options?.enableUserDetails !== false) {
182
+ tools.push(new HackerNewsUserTool());
183
+ }
184
+
185
+ return tools;
186
+ }
187
+ }