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.
- package/FEATURES.md +169 -0
- package/package.json +119 -0
- package/src/agent.ts +187 -0
- package/src/agentic/index.ts +87 -0
- package/src/agentic/runner.ts +386 -0
- package/src/agentic/types.ts +91 -0
- package/src/artifacts/artifact.ts +417 -0
- package/src/artifacts/index.ts +42 -0
- package/src/artifacts/media.ts +304 -0
- package/src/cli/index.ts +122 -0
- package/src/core/base-agent.ts +151 -0
- package/src/core/context-builder.ts +106 -0
- package/src/core/index.ts +8 -0
- package/src/core/schemas.ts +17 -0
- package/src/core/types.ts +158 -0
- package/src/create-agent.ts +309 -0
- package/src/debug-logger.ts +188 -0
- package/src/dx/agent.ts +88 -0
- package/src/dx/define-agent.ts +183 -0
- package/src/dx/dev-logger.ts +57 -0
- package/src/dx/index.ts +11 -0
- package/src/errors.ts +175 -0
- package/src/execution/engine.ts +522 -0
- package/src/execution/graph-builder.ts +362 -0
- package/src/execution/index.ts +8 -0
- package/src/execution/types.ts +257 -0
- package/src/execution/worker-pool.ts +308 -0
- package/src/extensions/index.ts +123 -0
- package/src/guardrails/allowlist.ts +155 -0
- package/src/guardrails/index.ts +17 -0
- package/src/guardrails/types.ts +159 -0
- package/src/guardrails/validator.ts +265 -0
- package/src/index.ts +74 -0
- package/src/knowledge/index.ts +5 -0
- package/src/knowledge/types.ts +52 -0
- package/src/learning/in-memory-store.ts +72 -0
- package/src/learning/index.ts +6 -0
- package/src/learning/types.ts +42 -0
- package/src/llm/cache.ts +300 -0
- package/src/llm/index.ts +22 -0
- package/src/llm/model-resolver.ts +81 -0
- package/src/llm/openai-provider.ts +313 -0
- package/src/llm/openrouter-provider.ts +29 -0
- package/src/llm/types.ts +131 -0
- package/src/memory/in-memory-store.ts +255 -0
- package/src/memory/index.ts +7 -0
- package/src/memory/types.ts +193 -0
- package/src/memory/vector-store.ts +251 -0
- package/src/observability/console-logger.ts +123 -0
- package/src/observability/index.ts +12 -0
- package/src/observability/metrics.ts +85 -0
- package/src/observability/otlp-exporter.ts +417 -0
- package/src/observability/tracer.ts +105 -0
- package/src/observability/types.ts +341 -0
- package/src/orchestration/agent-adapter.ts +33 -0
- package/src/orchestration/index.ts +34 -0
- package/src/orchestration/load-balancer.ts +151 -0
- package/src/orchestration/mcp-types.ts +59 -0
- package/src/orchestration/message-bus.ts +192 -0
- package/src/orchestration/orchestrator.ts +349 -0
- package/src/orchestration/pipeline.ts +66 -0
- package/src/orchestration/supervisor.ts +107 -0
- package/src/orchestration/swarm.ts +1099 -0
- package/src/orchestration/toolkit.ts +47 -0
- package/src/orchestration/types.ts +339 -0
- package/src/planner/classical-planner.ts +383 -0
- package/src/planner/index.ts +8 -0
- package/src/planner/llm-planner.ts +353 -0
- package/src/planner/types.ts +227 -0
- package/src/planner/validator.ts +297 -0
- package/src/production/circuit-breaker.ts +290 -0
- package/src/production/graceful-shutdown.ts +251 -0
- package/src/production/health.ts +333 -0
- package/src/production/index.ts +57 -0
- package/src/production/latency-eval.ts +62 -0
- package/src/production/rate-limiter.ts +287 -0
- package/src/production/resumable-stream.ts +289 -0
- package/src/production/types.ts +81 -0
- package/src/sdk/index.ts +374 -0
- package/src/session/db-driver.ts +50 -0
- package/src/session/in-memory-store.ts +235 -0
- package/src/session/index.ts +12 -0
- package/src/session/sql-store.ts +315 -0
- package/src/session/sqlite-store.ts +61 -0
- package/src/session/types.ts +153 -0
- package/src/tools/base-tool.ts +223 -0
- package/src/tools/browser-tool.ts +123 -0
- package/src/tools/calculator-tool.ts +265 -0
- package/src/tools/file-tools.ts +394 -0
- package/src/tools/github-tool.ts +432 -0
- package/src/tools/hackernews-tool.ts +187 -0
- package/src/tools/http-tool.ts +118 -0
- package/src/tools/index.ts +99 -0
- package/src/tools/jira-tool.ts +373 -0
- package/src/tools/notion-tool.ts +322 -0
- package/src/tools/openai-tool.ts +236 -0
- package/src/tools/registry.ts +131 -0
- package/src/tools/serpapi-tool.ts +234 -0
- package/src/tools/shell-tool.ts +118 -0
- package/src/tools/slack-tool.ts +327 -0
- package/src/tools/telegram-tool.ts +127 -0
- package/src/tools/types.ts +229 -0
- package/src/tools/websearch-tool.ts +335 -0
- package/src/tools/wikipedia-tool.ts +177 -0
- package/src/tools/yfinance-tool.ts +33 -0
- package/src/voice/index.ts +17 -0
- package/src/voice/voice-provider.ts +228 -0
- package/tests/artifact.test.ts +241 -0
- package/tests/circuit-breaker.test.ts +171 -0
- package/tests/health.test.ts +192 -0
- package/tests/llm-cache.test.ts +186 -0
- package/tests/rate-limiter.test.ts +161 -0
- package/tsconfig.json +29 -0
- package/vitest.config.ts +47 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SerpApi tool implementation - TypeScript SerpApiTools
|
|
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
|
+
* SerpApi result types
|
|
11
|
+
*/
|
|
12
|
+
interface SerpApiSearchResult {
|
|
13
|
+
title?: string;
|
|
14
|
+
link?: string;
|
|
15
|
+
snippet?: string;
|
|
16
|
+
displayed_link?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface SerpApiVideoResult {
|
|
20
|
+
title?: string;
|
|
21
|
+
link?: string;
|
|
22
|
+
snippet?: string;
|
|
23
|
+
thumbnail?: string;
|
|
24
|
+
duration?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface SerpApiChannelResult {
|
|
28
|
+
title?: string;
|
|
29
|
+
link?: string;
|
|
30
|
+
thumbnail?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface SerpApiResult {
|
|
34
|
+
data?: unknown;
|
|
35
|
+
error?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Base SerpApi tool with common authentication
|
|
40
|
+
*/
|
|
41
|
+
abstract class BaseSerpApiTool<TParams extends z.ZodObject<Record<string, z.ZodType>>> extends BaseTool<TParams, SerpApiResult> {
|
|
42
|
+
protected apiKey: string;
|
|
43
|
+
protected baseUrl = 'https://serpapi.com/search';
|
|
44
|
+
|
|
45
|
+
constructor(
|
|
46
|
+
config: Partial<Omit<BaseToolConfig<TParams>, 'parameters'>> & {
|
|
47
|
+
apiKey?: string;
|
|
48
|
+
},
|
|
49
|
+
params: TParams
|
|
50
|
+
) {
|
|
51
|
+
super({
|
|
52
|
+
name: config.name || 'serpapi.tool',
|
|
53
|
+
description: config.description || 'SerpApi tool',
|
|
54
|
+
parameters: params,
|
|
55
|
+
category: config.category || ToolCategory.WEB,
|
|
56
|
+
permissions: {
|
|
57
|
+
allowNetwork: true,
|
|
58
|
+
maxExecutionTimeMs: 30000,
|
|
59
|
+
...config.permissions,
|
|
60
|
+
},
|
|
61
|
+
...config,
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
this.apiKey = config.apiKey || process.env.SERPAPI_API_KEY || '';
|
|
65
|
+
|
|
66
|
+
if (!this.apiKey) {
|
|
67
|
+
throw new Error('SerpApi API key is required. Set SERPAPI_API_KEY environment variable or pass apiKey in config.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected async serpApiRequest(params: Record<string, string>): Promise<Response> {
|
|
72
|
+
const queryParams = new URLSearchParams({
|
|
73
|
+
api_key: this.apiKey,
|
|
74
|
+
...params,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return fetch(`${this.baseUrl}?${queryParams.toString()}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Google search tool
|
|
83
|
+
*/
|
|
84
|
+
const SerpApiGoogleSearchParameters = z.object({
|
|
85
|
+
query: z.string().describe('The search query'),
|
|
86
|
+
num_results: z.number().min(1).max(100).optional().default(10).describe('Number of results to return'),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
export class SerpApiGoogleSearchTool extends BaseSerpApiTool<typeof SerpApiGoogleSearchParameters> {
|
|
90
|
+
constructor(
|
|
91
|
+
config?: Partial<Omit<BaseToolConfig<typeof SerpApiGoogleSearchParameters>, 'parameters'>> & {
|
|
92
|
+
apiKey?: string;
|
|
93
|
+
}
|
|
94
|
+
) {
|
|
95
|
+
super(
|
|
96
|
+
{
|
|
97
|
+
name: config?.name ?? 'serpapi.google_search',
|
|
98
|
+
description: config?.description ?? 'Search Google using SerpApi',
|
|
99
|
+
...config,
|
|
100
|
+
},
|
|
101
|
+
SerpApiGoogleSearchParameters
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
protected async performExecute(
|
|
106
|
+
params: z.infer<typeof SerpApiGoogleSearchParameters>,
|
|
107
|
+
_context: ToolContext
|
|
108
|
+
): Promise<SerpApiResult> {
|
|
109
|
+
try {
|
|
110
|
+
const response = await this.serpApiRequest({
|
|
111
|
+
engine: 'google',
|
|
112
|
+
q: params.query,
|
|
113
|
+
num: params.num_results.toString(),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
throw new Error(`SerpApi error: ${response.status}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const data = (await response.json()) as {
|
|
121
|
+
organic_results?: SerpApiSearchResult[];
|
|
122
|
+
knowledge_graph?: unknown;
|
|
123
|
+
related_questions?: unknown[];
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
data: {
|
|
128
|
+
search_results: (data.organic_results || []).map((result) => ({
|
|
129
|
+
title: result.title,
|
|
130
|
+
url: result.link,
|
|
131
|
+
snippet: result.snippet,
|
|
132
|
+
displayed_link: result.displayed_link,
|
|
133
|
+
})),
|
|
134
|
+
knowledge_graph: data.knowledge_graph,
|
|
135
|
+
related_questions: data.related_questions,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
} catch (error) {
|
|
139
|
+
return {
|
|
140
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* YouTube search tool
|
|
148
|
+
*/
|
|
149
|
+
const SerpApiYouTubeSearchParameters = z.object({
|
|
150
|
+
query: z.string().describe('The search query'),
|
|
151
|
+
num_results: z.number().min(1).max(50).optional().default(10).describe('Number of results to return'),
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
export class SerpApiYouTubeSearchTool extends BaseSerpApiTool<typeof SerpApiYouTubeSearchParameters> {
|
|
155
|
+
constructor(
|
|
156
|
+
config?: Partial<Omit<BaseToolConfig<typeof SerpApiYouTubeSearchParameters>, 'parameters'>> & {
|
|
157
|
+
apiKey?: string;
|
|
158
|
+
}
|
|
159
|
+
) {
|
|
160
|
+
super(
|
|
161
|
+
{
|
|
162
|
+
name: config?.name ?? 'serpapi.youtube_search',
|
|
163
|
+
description: config?.description ?? 'Search YouTube using SerpApi',
|
|
164
|
+
...config,
|
|
165
|
+
},
|
|
166
|
+
SerpApiYouTubeSearchParameters
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
protected async performExecute(
|
|
171
|
+
params: z.infer<typeof SerpApiYouTubeSearchParameters>,
|
|
172
|
+
_context: ToolContext
|
|
173
|
+
): Promise<SerpApiResult> {
|
|
174
|
+
try {
|
|
175
|
+
const response = await this.serpApiRequest({
|
|
176
|
+
engine: 'youtube',
|
|
177
|
+
search_query: params.query,
|
|
178
|
+
num: params.num_results.toString(),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
throw new Error(`SerpApi error: ${response.status}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const data = (await response.json()) as {
|
|
186
|
+
video_results?: SerpApiVideoResult[];
|
|
187
|
+
channel_results?: SerpApiChannelResult[];
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
data: {
|
|
192
|
+
video_results: (data.video_results || []).map((result) => ({
|
|
193
|
+
title: result.title,
|
|
194
|
+
url: result.link,
|
|
195
|
+
snippet: result.snippet,
|
|
196
|
+
thumbnail: result.thumbnail,
|
|
197
|
+
duration: result.duration,
|
|
198
|
+
})),
|
|
199
|
+
channel_results: (data.channel_results || []).map((result) => ({
|
|
200
|
+
title: result.title,
|
|
201
|
+
url: result.link,
|
|
202
|
+
thumbnail: result.thumbnail,
|
|
203
|
+
})),
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
} catch (error) {
|
|
207
|
+
return {
|
|
208
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* SerpApi toolkit
|
|
216
|
+
*/
|
|
217
|
+
export class SerpApiToolkit {
|
|
218
|
+
static create(options?: {
|
|
219
|
+
apiKey?: string;
|
|
220
|
+
enableGoogleSearch?: boolean;
|
|
221
|
+
enableYouTubeSearch?: boolean;
|
|
222
|
+
}): Array<SerpApiGoogleSearchTool | SerpApiYouTubeSearchTool> {
|
|
223
|
+
const tools: Array<SerpApiGoogleSearchTool | SerpApiYouTubeSearchTool> = [];
|
|
224
|
+
|
|
225
|
+
if (options?.enableGoogleSearch !== false) {
|
|
226
|
+
tools.push(new SerpApiGoogleSearchTool({ apiKey: options?.apiKey }));
|
|
227
|
+
}
|
|
228
|
+
if (options?.enableYouTubeSearch !== false) {
|
|
229
|
+
tools.push(new SerpApiYouTubeSearchTool({ apiKey: options?.apiKey }));
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return tools;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell tool implementation - TypeScript ShellTools
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { BaseTool, BaseToolConfig } from './base-tool.js';
|
|
7
|
+
import { ToolContext, ToolCategory } from './types.js';
|
|
8
|
+
import { exec } from 'child_process';
|
|
9
|
+
import { promisify } from 'util';
|
|
10
|
+
import * as path from 'path';
|
|
11
|
+
|
|
12
|
+
const execAsync = promisify(exec);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Shell command result
|
|
16
|
+
*/
|
|
17
|
+
interface ShellResult {
|
|
18
|
+
stdout: string;
|
|
19
|
+
stderr: string;
|
|
20
|
+
exitCode: number;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parameters for shell command execution
|
|
26
|
+
*/
|
|
27
|
+
const ShellCommandParameters = z.object({
|
|
28
|
+
command: z.string().describe('The shell command to execute'),
|
|
29
|
+
cwd: z.string().optional().describe('Working directory for the command (optional)'),
|
|
30
|
+
timeout: z.number().min(1000).max(300000).optional().default(30000).describe('Timeout in milliseconds'),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Shell tool for running shell commands
|
|
35
|
+
*/
|
|
36
|
+
export class ShellTool extends BaseTool<typeof ShellCommandParameters, ShellResult> {
|
|
37
|
+
private baseDir?: string;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
config?: Partial<Omit<BaseToolConfig<typeof ShellCommandParameters>, 'parameters'>> & {
|
|
41
|
+
baseDir?: string;
|
|
42
|
+
}
|
|
43
|
+
) {
|
|
44
|
+
super({
|
|
45
|
+
name: config?.name ?? 'shell.run',
|
|
46
|
+
description: config?.description ?? 'Run a shell command and return the output',
|
|
47
|
+
parameters: ShellCommandParameters,
|
|
48
|
+
category: config?.category ?? ToolCategory.UTILITY,
|
|
49
|
+
permissions: {
|
|
50
|
+
allowNetwork: false,
|
|
51
|
+
allowFileSystem: true,
|
|
52
|
+
maxExecutionTimeMs: 30000,
|
|
53
|
+
...config?.permissions,
|
|
54
|
+
},
|
|
55
|
+
...config,
|
|
56
|
+
});
|
|
57
|
+
this.baseDir = config?.baseDir;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
protected async performExecute(
|
|
61
|
+
params: z.infer<typeof ShellCommandParameters>,
|
|
62
|
+
_context: ToolContext
|
|
63
|
+
): Promise<ShellResult> {
|
|
64
|
+
const cwd = params.cwd || this.baseDir || process.cwd();
|
|
65
|
+
|
|
66
|
+
// Validate the working directory is safe
|
|
67
|
+
const resolvedCwd = path.resolve(cwd);
|
|
68
|
+
const resolvedBase = this.baseDir ? path.resolve(this.baseDir) : resolvedCwd;
|
|
69
|
+
|
|
70
|
+
if (this.baseDir && !resolvedCwd.startsWith(resolvedBase)) {
|
|
71
|
+
return {
|
|
72
|
+
stdout: '',
|
|
73
|
+
stderr: '',
|
|
74
|
+
exitCode: 1,
|
|
75
|
+
error: `Working directory ${cwd} is outside the allowed base directory`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const { stdout, stderr } = await execAsync(params.command, {
|
|
81
|
+
cwd: resolvedCwd,
|
|
82
|
+
timeout: params.timeout,
|
|
83
|
+
maxBuffer: 1024 * 1024, // 1MB buffer
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
stdout: stdout.trim(),
|
|
88
|
+
stderr: stderr.trim(),
|
|
89
|
+
exitCode: 0,
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (error instanceof Error && 'stdout' in error && 'stderr' in error) {
|
|
93
|
+
const execError = error as unknown as { stdout: string; stderr: string; code?: number };
|
|
94
|
+
return {
|
|
95
|
+
stdout: execError.stdout?.trim() || '',
|
|
96
|
+
stderr: execError.stderr?.trim() || '',
|
|
97
|
+
exitCode: execError.code ?? 1,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
stdout: '',
|
|
103
|
+
stderr: '',
|
|
104
|
+
exitCode: 1,
|
|
105
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Shell toolkit
|
|
113
|
+
*/
|
|
114
|
+
export class ShellToolkit {
|
|
115
|
+
static create(options?: { baseDir?: string }): Array<ShellTool> {
|
|
116
|
+
return [new ShellTool({ baseDir: options?.baseDir })];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack tool implementation - TypeScript SlackTools
|
|
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
|
+
* Slack API response types
|
|
11
|
+
*/
|
|
12
|
+
interface SlackPostMessageResponse {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
channel?: string;
|
|
15
|
+
ts?: string;
|
|
16
|
+
message?: unknown;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SlackChannel {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface SlackChannelsResponse {
|
|
26
|
+
ok: boolean;
|
|
27
|
+
channels?: SlackChannel[];
|
|
28
|
+
error?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface SlackMessage {
|
|
32
|
+
text?: string;
|
|
33
|
+
user?: string;
|
|
34
|
+
ts?: string;
|
|
35
|
+
subtype?: string;
|
|
36
|
+
bot_id?: string;
|
|
37
|
+
attachments?: unknown[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface SlackHistoryResponse {
|
|
41
|
+
ok: boolean;
|
|
42
|
+
messages?: SlackMessage[];
|
|
43
|
+
error?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface SlackResult {
|
|
47
|
+
success: boolean;
|
|
48
|
+
data?: unknown;
|
|
49
|
+
error?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Parameters for sending a Slack message
|
|
54
|
+
*/
|
|
55
|
+
const SlackSendMessageParameters = z.object({
|
|
56
|
+
channel: z.string().describe('The channel ID or name to send the message to'),
|
|
57
|
+
text: z.string().describe('The text of the message to send'),
|
|
58
|
+
thread_ts: z.string().optional().describe('Timestamp of the parent message (for threaded replies)'),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Slack send message tool
|
|
63
|
+
*/
|
|
64
|
+
export class SlackSendMessageTool extends BaseTool<typeof SlackSendMessageParameters, SlackResult> {
|
|
65
|
+
private token: string;
|
|
66
|
+
private baseUrl = 'https://slack.com/api';
|
|
67
|
+
|
|
68
|
+
constructor(
|
|
69
|
+
config?: Partial<Omit<BaseToolConfig<typeof SlackSendMessageParameters>, 'parameters'>> & {
|
|
70
|
+
token?: string;
|
|
71
|
+
}
|
|
72
|
+
) {
|
|
73
|
+
super({
|
|
74
|
+
name: config?.name ?? 'slack.send_message',
|
|
75
|
+
description: config?.description ?? 'Send a message to a Slack channel',
|
|
76
|
+
parameters: SlackSendMessageParameters,
|
|
77
|
+
category: config?.category ?? ToolCategory.API,
|
|
78
|
+
permissions: {
|
|
79
|
+
allowNetwork: true,
|
|
80
|
+
maxExecutionTimeMs: 30000,
|
|
81
|
+
...config?.permissions,
|
|
82
|
+
},
|
|
83
|
+
...config,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
this.token = config?.token || process.env.SLACK_TOKEN || '';
|
|
87
|
+
|
|
88
|
+
if (!this.token) {
|
|
89
|
+
throw new Error('Slack token is required. Set SLACK_TOKEN environment variable or pass token in config.');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected async performExecute(
|
|
94
|
+
params: z.infer<typeof SlackSendMessageParameters>,
|
|
95
|
+
_context: ToolContext
|
|
96
|
+
): Promise<SlackResult> {
|
|
97
|
+
try {
|
|
98
|
+
const body: Record<string, string> = {
|
|
99
|
+
channel: params.channel,
|
|
100
|
+
text: params.text,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
if (params.thread_ts) {
|
|
104
|
+
body.thread_ts = params.thread_ts;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const response = await fetch(`${this.baseUrl}/chat.postMessage`, {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: {
|
|
110
|
+
Authorization: `Bearer ${this.token}`,
|
|
111
|
+
'Content-Type': 'application/json',
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify(body),
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const data = (await response.json()) as SlackPostMessageResponse;
|
|
117
|
+
|
|
118
|
+
if (data.ok) {
|
|
119
|
+
return {
|
|
120
|
+
success: true,
|
|
121
|
+
data: {
|
|
122
|
+
channel: data.channel,
|
|
123
|
+
ts: data.ts,
|
|
124
|
+
message: data.message,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
} else {
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
error: data.error || 'Unknown Slack API error',
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return {
|
|
135
|
+
success: false,
|
|
136
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Parameters for listing Slack channels
|
|
144
|
+
*/
|
|
145
|
+
const SlackListChannelsParameters = z.object({
|
|
146
|
+
limit: z.number().min(1).max(200).optional().default(100).describe('Maximum number of channels to return'),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Slack list channels tool
|
|
151
|
+
*/
|
|
152
|
+
export class SlackListChannelsTool extends BaseTool<typeof SlackListChannelsParameters, SlackResult> {
|
|
153
|
+
private token: string;
|
|
154
|
+
private baseUrl = 'https://slack.com/api';
|
|
155
|
+
|
|
156
|
+
constructor(
|
|
157
|
+
config?: Partial<Omit<BaseToolConfig<typeof SlackListChannelsParameters>, 'parameters'>> & {
|
|
158
|
+
token?: string;
|
|
159
|
+
}
|
|
160
|
+
) {
|
|
161
|
+
super({
|
|
162
|
+
name: config?.name ?? 'slack.list_channels',
|
|
163
|
+
description: config?.description ?? 'List all channels in the Slack workspace',
|
|
164
|
+
parameters: SlackListChannelsParameters,
|
|
165
|
+
category: config?.category ?? ToolCategory.API,
|
|
166
|
+
permissions: {
|
|
167
|
+
allowNetwork: true,
|
|
168
|
+
maxExecutionTimeMs: 30000,
|
|
169
|
+
...config?.permissions,
|
|
170
|
+
},
|
|
171
|
+
...config,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
this.token = config?.token || process.env.SLACK_TOKEN || '';
|
|
175
|
+
|
|
176
|
+
if (!this.token) {
|
|
177
|
+
throw new Error('Slack token is required. Set SLACK_TOKEN environment variable or pass token in config.');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
protected async performExecute(
|
|
182
|
+
params: z.infer<typeof SlackListChannelsParameters>,
|
|
183
|
+
_context: ToolContext
|
|
184
|
+
): Promise<SlackResult> {
|
|
185
|
+
try {
|
|
186
|
+
const response = await fetch(`${this.baseUrl}/conversations.list?limit=${params.limit}`, {
|
|
187
|
+
headers: {
|
|
188
|
+
Authorization: `Bearer ${this.token}`,
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const data = (await response.json()) as SlackChannelsResponse;
|
|
193
|
+
|
|
194
|
+
if (data.ok) {
|
|
195
|
+
const channels = (data.channels || []).map((channel) => ({
|
|
196
|
+
id: channel.id,
|
|
197
|
+
name: channel.name,
|
|
198
|
+
}));
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
success: true,
|
|
202
|
+
data: channels,
|
|
203
|
+
};
|
|
204
|
+
} else {
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
error: data.error || 'Unknown Slack API error',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
success: false,
|
|
213
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Parameters for getting channel history
|
|
221
|
+
*/
|
|
222
|
+
const SlackGetChannelHistoryParameters = z.object({
|
|
223
|
+
channel: z.string().describe('The channel ID to fetch history from'),
|
|
224
|
+
limit: z.number().min(1).max(200).optional().default(100).describe('Maximum number of messages to return'),
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Slack get channel history tool
|
|
229
|
+
*/
|
|
230
|
+
export class SlackGetChannelHistoryTool extends BaseTool<typeof SlackGetChannelHistoryParameters, SlackResult> {
|
|
231
|
+
private token: string;
|
|
232
|
+
private baseUrl = 'https://slack.com/api';
|
|
233
|
+
|
|
234
|
+
constructor(
|
|
235
|
+
config?: Partial<Omit<BaseToolConfig<typeof SlackGetChannelHistoryParameters>, 'parameters'>> & {
|
|
236
|
+
token?: string;
|
|
237
|
+
}
|
|
238
|
+
) {
|
|
239
|
+
super({
|
|
240
|
+
name: config?.name ?? 'slack.get_channel_history',
|
|
241
|
+
description: config?.description ?? 'Get the message history of a Slack channel',
|
|
242
|
+
parameters: SlackGetChannelHistoryParameters,
|
|
243
|
+
category: config?.category ?? ToolCategory.API,
|
|
244
|
+
permissions: {
|
|
245
|
+
allowNetwork: true,
|
|
246
|
+
maxExecutionTimeMs: 30000,
|
|
247
|
+
...config?.permissions,
|
|
248
|
+
},
|
|
249
|
+
...config,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
this.token = config?.token || process.env.SLACK_TOKEN || '';
|
|
253
|
+
|
|
254
|
+
if (!this.token) {
|
|
255
|
+
throw new Error('Slack token is required. Set SLACK_TOKEN environment variable or pass token in config.');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
protected async performExecute(
|
|
260
|
+
params: z.infer<typeof SlackGetChannelHistoryParameters>,
|
|
261
|
+
_context: ToolContext
|
|
262
|
+
): Promise<SlackResult> {
|
|
263
|
+
try {
|
|
264
|
+
const response = await fetch(
|
|
265
|
+
`${this.baseUrl}/conversations.history?channel=${encodeURIComponent(params.channel)}&limit=${params.limit}`,
|
|
266
|
+
{
|
|
267
|
+
headers: {
|
|
268
|
+
Authorization: `Bearer ${this.token}`,
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
const data = (await response.json()) as SlackHistoryResponse;
|
|
274
|
+
|
|
275
|
+
if (data.ok) {
|
|
276
|
+
const messages = (data.messages || []).map((msg) => ({
|
|
277
|
+
text: msg.text,
|
|
278
|
+
user: msg.user || (msg.bot_id ? 'bot' : 'unknown'),
|
|
279
|
+
ts: msg.ts,
|
|
280
|
+
subtype: msg.subtype || 'normal',
|
|
281
|
+
attachments: msg.attachments,
|
|
282
|
+
}));
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
success: true,
|
|
286
|
+
data: messages,
|
|
287
|
+
};
|
|
288
|
+
} else {
|
|
289
|
+
return {
|
|
290
|
+
success: false,
|
|
291
|
+
error: data.error || 'Unknown Slack API error',
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
} catch (error) {
|
|
295
|
+
return {
|
|
296
|
+
success: false,
|
|
297
|
+
error: error instanceof Error ? error.message : 'Unknown error occurred',
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Slack toolkit
|
|
305
|
+
*/
|
|
306
|
+
export class SlackToolkit {
|
|
307
|
+
static create(options?: {
|
|
308
|
+
token?: string;
|
|
309
|
+
enableSendMessage?: boolean;
|
|
310
|
+
enableListChannels?: boolean;
|
|
311
|
+
enableGetHistory?: boolean;
|
|
312
|
+
}): Array<SlackSendMessageTool | SlackListChannelsTool | SlackGetChannelHistoryTool> {
|
|
313
|
+
const tools: Array<SlackSendMessageTool | SlackListChannelsTool | SlackGetChannelHistoryTool> = [];
|
|
314
|
+
|
|
315
|
+
if (options?.enableSendMessage !== false) {
|
|
316
|
+
tools.push(new SlackSendMessageTool({ token: options?.token }));
|
|
317
|
+
}
|
|
318
|
+
if (options?.enableListChannels !== false) {
|
|
319
|
+
tools.push(new SlackListChannelsTool({ token: options?.token }));
|
|
320
|
+
}
|
|
321
|
+
if (options?.enableGetHistory !== false) {
|
|
322
|
+
tools.push(new SlackGetChannelHistoryTool({ token: options?.token }));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return tools;
|
|
326
|
+
}
|
|
327
|
+
}
|