@radaros/core 0.3.0 → 0.3.1

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/dist/index.js CHANGED
@@ -3787,14 +3787,14 @@ var MCPToolProvider = class {
3787
3787
  await this.discoverTools();
3788
3788
  }
3789
3789
  async discoverTools() {
3790
- const { z: z3 } = await import("zod");
3790
+ const { z: z7 } = await import("zod");
3791
3791
  const result = await this.client.listTools();
3792
3792
  const mcpTools = result.tools ?? [];
3793
3793
  this.tools = mcpTools.map((mcpTool) => {
3794
3794
  const toolName = mcpTool.name;
3795
3795
  const description = mcpTool.description ?? "";
3796
3796
  const inputSchema = mcpTool.inputSchema ?? { type: "object", properties: {} };
3797
- const parameters = this.jsonSchemaToZod(inputSchema, z3);
3797
+ const parameters = this.jsonSchemaToZod(inputSchema, z7);
3798
3798
  const execute = async (args, _ctx) => {
3799
3799
  const callResult = await this.client.callTool({
3800
3800
  name: toolName,
@@ -3822,9 +3822,9 @@ var MCPToolProvider = class {
3822
3822
  };
3823
3823
  });
3824
3824
  }
3825
- jsonSchemaToZod(schema, z3) {
3825
+ jsonSchemaToZod(schema, z7) {
3826
3826
  if (!schema || !schema.properties) {
3827
- return z3.object({}).passthrough();
3827
+ return z7.object({}).passthrough();
3828
3828
  }
3829
3829
  const shape = {};
3830
3830
  const required = schema.required ?? [];
@@ -3832,24 +3832,24 @@ var MCPToolProvider = class {
3832
3832
  let field;
3833
3833
  switch (prop.type) {
3834
3834
  case "string":
3835
- field = z3.string();
3836
- if (prop.enum) field = z3.enum(prop.enum);
3835
+ field = z7.string();
3836
+ if (prop.enum) field = z7.enum(prop.enum);
3837
3837
  break;
3838
3838
  case "number":
3839
3839
  case "integer":
3840
- field = z3.number();
3840
+ field = z7.number();
3841
3841
  break;
3842
3842
  case "boolean":
3843
- field = z3.boolean();
3843
+ field = z7.boolean();
3844
3844
  break;
3845
3845
  case "array":
3846
- field = z3.array(z3.any());
3846
+ field = z7.array(z7.any());
3847
3847
  break;
3848
3848
  case "object":
3849
- field = z3.record(z3.any());
3849
+ field = z7.record(z7.any());
3850
3850
  break;
3851
3851
  default:
3852
- field = z3.any();
3852
+ field = z7.any();
3853
3853
  }
3854
3854
  if (prop.description) {
3855
3855
  field = field.describe(prop.description);
@@ -3859,7 +3859,7 @@ var MCPToolProvider = class {
3859
3859
  }
3860
3860
  shape[key] = field;
3861
3861
  }
3862
- return z3.object(shape);
3862
+ return z7.object(shape);
3863
3863
  }
3864
3864
  /**
3865
3865
  * Returns tools from this MCP server as RadarOS ToolDef[].
@@ -4108,14 +4108,499 @@ var A2ARemoteAgent = class {
4108
4108
  ).map((p) => p.text).join("\n");
4109
4109
  }
4110
4110
  };
4111
+
4112
+ // src/toolkits/base.ts
4113
+ var Toolkit = class {
4114
+ };
4115
+
4116
+ // src/toolkits/websearch.ts
4117
+ import { z as z3 } from "zod";
4118
+ var WebSearchToolkit = class extends Toolkit {
4119
+ name = "websearch";
4120
+ config;
4121
+ constructor(config) {
4122
+ super();
4123
+ this.config = config;
4124
+ }
4125
+ getApiKey() {
4126
+ if (this.config.apiKey) return this.config.apiKey;
4127
+ const envKey = this.config.provider === "tavily" ? process.env.TAVILY_API_KEY : process.env.SERPAPI_API_KEY;
4128
+ if (!envKey) {
4129
+ const envName = this.config.provider === "tavily" ? "TAVILY_API_KEY" : "SERPAPI_API_KEY";
4130
+ throw new Error(
4131
+ `WebSearchToolkit: No API key provided. Set ${envName} env var or pass apiKey in config.`
4132
+ );
4133
+ }
4134
+ return envKey;
4135
+ }
4136
+ getTools() {
4137
+ const self = this;
4138
+ return [
4139
+ {
4140
+ name: "web_search",
4141
+ description: "Search the web for current information. Returns titles, URLs, and snippets from search results.",
4142
+ parameters: z3.object({
4143
+ query: z3.string().describe("The search query"),
4144
+ maxResults: z3.number().optional().describe("Maximum number of results (default 5)")
4145
+ }),
4146
+ execute: async (args, _ctx) => {
4147
+ const query = args.query;
4148
+ const max = args.maxResults ?? self.config.maxResults ?? 5;
4149
+ if (self.config.provider === "tavily") {
4150
+ return self.searchTavily(query, max);
4151
+ }
4152
+ return self.searchSerpApi(query, max);
4153
+ }
4154
+ }
4155
+ ];
4156
+ }
4157
+ async searchTavily(query, maxResults) {
4158
+ const apiKey = this.getApiKey();
4159
+ const res = await fetch("https://api.tavily.com/search", {
4160
+ method: "POST",
4161
+ headers: { "Content-Type": "application/json" },
4162
+ body: JSON.stringify({
4163
+ api_key: apiKey,
4164
+ query,
4165
+ max_results: maxResults,
4166
+ include_answer: true
4167
+ })
4168
+ });
4169
+ if (!res.ok) {
4170
+ throw new Error(`Tavily search failed: ${res.status} ${res.statusText}`);
4171
+ }
4172
+ const data = await res.json();
4173
+ const results = [];
4174
+ if (data.answer) {
4175
+ results.push(`Answer: ${data.answer}
4176
+ `);
4177
+ }
4178
+ for (const r of data.results ?? []) {
4179
+ results.push(`Title: ${r.title}
4180
+ URL: ${r.url}
4181
+ Snippet: ${r.content}
4182
+ `);
4183
+ }
4184
+ return results.join("\n---\n") || "No results found.";
4185
+ }
4186
+ async searchSerpApi(query, maxResults) {
4187
+ const apiKey = this.getApiKey();
4188
+ const params = new URLSearchParams({
4189
+ q: query,
4190
+ api_key: apiKey,
4191
+ engine: "google",
4192
+ num: String(maxResults)
4193
+ });
4194
+ const res = await fetch(
4195
+ `https://serpapi.com/search.json?${params.toString()}`
4196
+ );
4197
+ if (!res.ok) {
4198
+ throw new Error(
4199
+ `SerpAPI search failed: ${res.status} ${res.statusText}`
4200
+ );
4201
+ }
4202
+ const data = await res.json();
4203
+ const results = [];
4204
+ if (data.answer_box?.answer) {
4205
+ results.push(`Answer: ${data.answer_box.answer}
4206
+ `);
4207
+ }
4208
+ for (const r of (data.organic_results ?? []).slice(0, maxResults)) {
4209
+ results.push(
4210
+ `Title: ${r.title}
4211
+ URL: ${r.link}
4212
+ Snippet: ${r.snippet ?? ""}
4213
+ `
4214
+ );
4215
+ }
4216
+ return results.join("\n---\n") || "No results found.";
4217
+ }
4218
+ };
4219
+
4220
+ // src/toolkits/gmail.ts
4221
+ import { z as z4 } from "zod";
4222
+ import { createRequire as createRequire16 } from "module";
4223
+ var _require16 = createRequire16(import.meta.url);
4224
+ var GmailToolkit = class extends Toolkit {
4225
+ name = "gmail";
4226
+ config;
4227
+ gmail = null;
4228
+ constructor(config = {}) {
4229
+ super();
4230
+ this.config = config;
4231
+ }
4232
+ async getGmailClient() {
4233
+ if (this.gmail) return this.gmail;
4234
+ if (this.config.authClient) {
4235
+ const { google: google3 } = _require16("googleapis");
4236
+ this.gmail = google3.gmail({ version: "v1", auth: this.config.authClient });
4237
+ return this.gmail;
4238
+ }
4239
+ const credPath = this.config.credentialsPath ?? process.env.GMAIL_CREDENTIALS_PATH;
4240
+ const tokenPath = this.config.tokenPath ?? process.env.GMAIL_TOKEN_PATH;
4241
+ if (!credPath || !tokenPath) {
4242
+ throw new Error(
4243
+ "GmailToolkit: Provide credentialsPath + tokenPath, or an authClient. Set GMAIL_CREDENTIALS_PATH and GMAIL_TOKEN_PATH env vars, or pass them in config."
4244
+ );
4245
+ }
4246
+ const { google: google2 } = _require16("googleapis");
4247
+ const fs = await import("fs");
4248
+ const creds = JSON.parse(fs.readFileSync(credPath, "utf-8"));
4249
+ const token = JSON.parse(fs.readFileSync(tokenPath, "utf-8"));
4250
+ const { client_id, client_secret, redirect_uris } = creds.installed || creds.web;
4251
+ const oAuth2 = new google2.auth.OAuth2(
4252
+ client_id,
4253
+ client_secret,
4254
+ redirect_uris?.[0]
4255
+ );
4256
+ oAuth2.setCredentials(token);
4257
+ this.gmail = google2.gmail({ version: "v1", auth: oAuth2 });
4258
+ return this.gmail;
4259
+ }
4260
+ getTools() {
4261
+ const self = this;
4262
+ return [
4263
+ {
4264
+ name: "gmail_send",
4265
+ description: "Send an email via Gmail. Provide recipient, subject, and body.",
4266
+ parameters: z4.object({
4267
+ to: z4.string().describe("Recipient email address"),
4268
+ subject: z4.string().describe("Email subject line"),
4269
+ body: z4.string().describe("Email body (plain text)")
4270
+ }),
4271
+ execute: async (args, _ctx) => {
4272
+ const gmail = await self.getGmailClient();
4273
+ const rawMessage = [
4274
+ `To: ${args.to}`,
4275
+ `Subject: ${args.subject}`,
4276
+ "Content-Type: text/plain; charset=utf-8",
4277
+ "",
4278
+ args.body
4279
+ ].join("\n");
4280
+ const encoded = Buffer.from(rawMessage).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
4281
+ const res = await gmail.users.messages.send({
4282
+ userId: "me",
4283
+ requestBody: { raw: encoded }
4284
+ });
4285
+ return `Email sent successfully. Message ID: ${res.data.id}`;
4286
+ }
4287
+ },
4288
+ {
4289
+ name: "gmail_search",
4290
+ description: "Search emails in Gmail. Returns subject, from, date, and snippet for matching messages.",
4291
+ parameters: z4.object({
4292
+ query: z4.string().describe(
4293
+ 'Gmail search query (e.g. "from:john subject:meeting is:unread")'
4294
+ ),
4295
+ maxResults: z4.number().optional().describe("Maximum number of results (default 10)")
4296
+ }),
4297
+ execute: async (args, _ctx) => {
4298
+ const gmail = await self.getGmailClient();
4299
+ const max = args.maxResults ?? 10;
4300
+ const list = await gmail.users.messages.list({
4301
+ userId: "me",
4302
+ q: args.query,
4303
+ maxResults: max
4304
+ });
4305
+ const messages = list.data.messages ?? [];
4306
+ if (messages.length === 0) return "No emails found.";
4307
+ const results = [];
4308
+ for (const msg of messages) {
4309
+ const detail = await gmail.users.messages.get({
4310
+ userId: "me",
4311
+ id: msg.id,
4312
+ format: "metadata",
4313
+ metadataHeaders: ["From", "Subject", "Date"]
4314
+ });
4315
+ const headers = detail.data.payload?.headers ?? [];
4316
+ const getHeader = (name) => headers.find(
4317
+ (h) => h.name.toLowerCase() === name.toLowerCase()
4318
+ )?.value ?? "";
4319
+ results.push(
4320
+ `ID: ${msg.id}
4321
+ From: ${getHeader("From")}
4322
+ Subject: ${getHeader("Subject")}
4323
+ Date: ${getHeader("Date")}
4324
+ Snippet: ${detail.data.snippet ?? ""}`
4325
+ );
4326
+ }
4327
+ return results.join("\n\n---\n\n");
4328
+ }
4329
+ },
4330
+ {
4331
+ name: "gmail_read",
4332
+ description: "Read the full content of an email by its message ID.",
4333
+ parameters: z4.object({
4334
+ messageId: z4.string().describe("The Gmail message ID to read")
4335
+ }),
4336
+ execute: async (args, _ctx) => {
4337
+ const gmail = await self.getGmailClient();
4338
+ const detail = await gmail.users.messages.get({
4339
+ userId: "me",
4340
+ id: args.messageId,
4341
+ format: "full"
4342
+ });
4343
+ const headers = detail.data.payload?.headers ?? [];
4344
+ const getHeader = (name) => headers.find(
4345
+ (h) => h.name.toLowerCase() === name.toLowerCase()
4346
+ )?.value ?? "";
4347
+ let body = "";
4348
+ const payload = detail.data.payload;
4349
+ if (payload?.body?.data) {
4350
+ body = Buffer.from(payload.body.data, "base64").toString("utf-8");
4351
+ } else if (payload?.parts) {
4352
+ const textPart = payload.parts.find(
4353
+ (p) => p.mimeType === "text/plain"
4354
+ );
4355
+ if (textPart?.body?.data) {
4356
+ body = Buffer.from(textPart.body.data, "base64").toString(
4357
+ "utf-8"
4358
+ );
4359
+ } else {
4360
+ const htmlPart = payload.parts.find(
4361
+ (p) => p.mimeType === "text/html"
4362
+ );
4363
+ if (htmlPart?.body?.data) {
4364
+ body = Buffer.from(htmlPart.body.data, "base64").toString(
4365
+ "utf-8"
4366
+ );
4367
+ }
4368
+ }
4369
+ }
4370
+ return `From: ${getHeader("From")}
4371
+ To: ${getHeader("To")}
4372
+ Subject: ${getHeader("Subject")}
4373
+ Date: ${getHeader("Date")}
4374
+
4375
+ ${body || "(no body)"}`;
4376
+ }
4377
+ }
4378
+ ];
4379
+ }
4380
+ };
4381
+
4382
+ // src/toolkits/whatsapp.ts
4383
+ import { z as z5 } from "zod";
4384
+ var WhatsAppToolkit = class extends Toolkit {
4385
+ name = "whatsapp";
4386
+ accessToken;
4387
+ phoneNumberId;
4388
+ version;
4389
+ recipientWaid;
4390
+ constructor(config = {}) {
4391
+ super();
4392
+ this.accessToken = config.accessToken ?? process.env.WHATSAPP_ACCESS_TOKEN ?? "";
4393
+ this.phoneNumberId = config.phoneNumberId ?? process.env.WHATSAPP_PHONE_NUMBER_ID ?? "";
4394
+ this.version = config.version ?? process.env.WHATSAPP_VERSION ?? "v22.0";
4395
+ this.recipientWaid = config.recipientWaid ?? process.env.WHATSAPP_RECIPIENT_WAID;
4396
+ }
4397
+ getBaseUrl() {
4398
+ return `https://graph.facebook.com/${this.version}/${this.phoneNumberId}/messages`;
4399
+ }
4400
+ validate() {
4401
+ if (!this.accessToken) {
4402
+ throw new Error(
4403
+ "WhatsAppToolkit: accessToken is required. Set WHATSAPP_ACCESS_TOKEN env var or pass in config."
4404
+ );
4405
+ }
4406
+ if (!this.phoneNumberId) {
4407
+ throw new Error(
4408
+ "WhatsAppToolkit: phoneNumberId is required. Set WHATSAPP_PHONE_NUMBER_ID env var or pass in config."
4409
+ );
4410
+ }
4411
+ }
4412
+ resolveRecipient(recipient) {
4413
+ const r = recipient ?? this.recipientWaid;
4414
+ if (!r) {
4415
+ throw new Error(
4416
+ "WhatsAppToolkit: recipient is required. Provide it in the tool call or set WHATSAPP_RECIPIENT_WAID env var."
4417
+ );
4418
+ }
4419
+ return r.replace(/[^0-9]/g, "");
4420
+ }
4421
+ getTools() {
4422
+ const self = this;
4423
+ return [
4424
+ {
4425
+ name: "whatsapp_send_text",
4426
+ description: "Send a text message to a WhatsApp user via WhatsApp Business Cloud API.",
4427
+ parameters: z5.object({
4428
+ text: z5.string().describe("The text message to send"),
4429
+ recipient: z5.string().optional().describe(
4430
+ "Recipient WhatsApp number with country code (e.g. 919876543210). Uses default if omitted."
4431
+ ),
4432
+ previewUrl: z5.boolean().optional().describe("Enable URL previews in the message (default false)")
4433
+ }),
4434
+ execute: async (args, _ctx) => {
4435
+ self.validate();
4436
+ const recipient = self.resolveRecipient(args.recipient);
4437
+ const res = await fetch(self.getBaseUrl(), {
4438
+ method: "POST",
4439
+ headers: {
4440
+ Authorization: `Bearer ${self.accessToken}`,
4441
+ "Content-Type": "application/json"
4442
+ },
4443
+ body: JSON.stringify({
4444
+ messaging_product: "whatsapp",
4445
+ recipient_type: "individual",
4446
+ to: recipient,
4447
+ type: "text",
4448
+ text: {
4449
+ preview_url: args.previewUrl ?? false,
4450
+ body: args.text
4451
+ }
4452
+ })
4453
+ });
4454
+ if (!res.ok) {
4455
+ const err = await res.text();
4456
+ throw new Error(
4457
+ `WhatsApp send failed: ${res.status} ${err}`
4458
+ );
4459
+ }
4460
+ const data = await res.json();
4461
+ const msgId = data.messages?.[0]?.id ?? "unknown";
4462
+ return `Message sent successfully to ${recipient}. Message ID: ${msgId}`;
4463
+ }
4464
+ },
4465
+ {
4466
+ name: "whatsapp_send_template",
4467
+ description: "Send a template message to a WhatsApp user. Required for first-time outreach (24-hour messaging window).",
4468
+ parameters: z5.object({
4469
+ templateName: z5.string().describe(
4470
+ 'The pre-approved template name (e.g. "hello_world")'
4471
+ ),
4472
+ recipient: z5.string().optional().describe(
4473
+ "Recipient WhatsApp number with country code. Uses default if omitted."
4474
+ ),
4475
+ languageCode: z5.string().optional().describe('Template language code (default "en_US")'),
4476
+ components: z5.array(z5.record(z5.any())).optional().describe(
4477
+ "Template components for dynamic content (header, body, button parameters)"
4478
+ )
4479
+ }),
4480
+ execute: async (args, _ctx) => {
4481
+ self.validate();
4482
+ const recipient = self.resolveRecipient(args.recipient);
4483
+ const langCode = args.languageCode ?? "en_US";
4484
+ const template = {
4485
+ name: args.templateName,
4486
+ language: { code: langCode }
4487
+ };
4488
+ if (args.components) {
4489
+ template.components = args.components;
4490
+ }
4491
+ const res = await fetch(self.getBaseUrl(), {
4492
+ method: "POST",
4493
+ headers: {
4494
+ Authorization: `Bearer ${self.accessToken}`,
4495
+ "Content-Type": "application/json"
4496
+ },
4497
+ body: JSON.stringify({
4498
+ messaging_product: "whatsapp",
4499
+ recipient_type: "individual",
4500
+ to: recipient,
4501
+ type: "template",
4502
+ template
4503
+ })
4504
+ });
4505
+ if (!res.ok) {
4506
+ const err = await res.text();
4507
+ throw new Error(
4508
+ `WhatsApp template send failed: ${res.status} ${err}`
4509
+ );
4510
+ }
4511
+ const data = await res.json();
4512
+ const msgId = data.messages?.[0]?.id ?? "unknown";
4513
+ return `Template message "${args.templateName}" sent to ${recipient}. Message ID: ${msgId}`;
4514
+ }
4515
+ }
4516
+ ];
4517
+ }
4518
+ };
4519
+
4520
+ // src/toolkits/hackernews.ts
4521
+ import { z as z6 } from "zod";
4522
+ var HackerNewsToolkit = class extends Toolkit {
4523
+ name = "hackernews";
4524
+ config;
4525
+ constructor(config = {}) {
4526
+ super();
4527
+ this.config = {
4528
+ enableGetTopStories: config.enableGetTopStories ?? true,
4529
+ enableGetUserDetails: config.enableGetUserDetails ?? true
4530
+ };
4531
+ }
4532
+ getTools() {
4533
+ const tools = [];
4534
+ if (this.config.enableGetTopStories) {
4535
+ tools.push({
4536
+ name: "hackernews_top_stories",
4537
+ description: "Get the top stories from Hacker News. Returns title, URL, score, author, and comment count.",
4538
+ parameters: z6.object({
4539
+ numStories: z6.number().optional().describe("Number of top stories to fetch (default 10, max 30)")
4540
+ }),
4541
+ execute: async (args, _ctx) => {
4542
+ const num = Math.min(args.numStories ?? 10, 30);
4543
+ const idsRes = await fetch(
4544
+ "https://hacker-news.firebaseio.com/v0/topstories.json"
4545
+ );
4546
+ if (!idsRes.ok) throw new Error(`HN API failed: ${idsRes.status}`);
4547
+ const ids = await idsRes.json();
4548
+ const stories = await Promise.all(
4549
+ ids.slice(0, num).map(async (id) => {
4550
+ const res = await fetch(
4551
+ `https://hacker-news.firebaseio.com/v0/item/${id}.json`
4552
+ );
4553
+ return res.json();
4554
+ })
4555
+ );
4556
+ return stories.map(
4557
+ (s, i) => `${i + 1}. ${s.title}
4558
+ URL: ${s.url ?? `https://news.ycombinator.com/item?id=${s.id}`}
4559
+ Score: ${s.score} | By: ${s.by} | Comments: ${s.descendants ?? 0}`
4560
+ ).join("\n\n");
4561
+ }
4562
+ });
4563
+ }
4564
+ if (this.config.enableGetUserDetails) {
4565
+ tools.push({
4566
+ name: "hackernews_user",
4567
+ description: "Get details about a Hacker News user by username. Returns karma, about, and account creation date.",
4568
+ parameters: z6.object({
4569
+ username: z6.string().describe("The HN username to look up")
4570
+ }),
4571
+ execute: async (args, _ctx) => {
4572
+ const username = args.username;
4573
+ const res = await fetch(
4574
+ `https://hacker-news.firebaseio.com/v0/user/${username}.json`
4575
+ );
4576
+ if (!res.ok)
4577
+ throw new Error(`HN user API failed: ${res.status}`);
4578
+ const user = await res.json();
4579
+ if (!user) return `User "${username}" not found.`;
4580
+ const created = new Date(user.created * 1e3).toISOString().split("T")[0];
4581
+ return [
4582
+ `Username: ${user.id}`,
4583
+ `Karma: ${user.karma}`,
4584
+ `Created: ${created}`,
4585
+ user.about ? `About: ${user.about}` : null,
4586
+ `Submitted: ${user.submitted?.length ?? 0} items`
4587
+ ].filter(Boolean).join("\n");
4588
+ }
4589
+ });
4590
+ }
4591
+ return tools;
4592
+ }
4593
+ };
4111
4594
  export {
4112
4595
  A2ARemoteAgent,
4113
4596
  Agent,
4114
4597
  AnthropicProvider,
4115
4598
  BaseVectorStore,
4116
4599
  EventBus,
4600
+ GmailToolkit,
4117
4601
  GoogleEmbedding,
4118
4602
  GoogleProvider,
4603
+ HackerNewsToolkit,
4119
4604
  InMemoryStorage,
4120
4605
  InMemoryVectorStore,
4121
4606
  KnowledgeBase,
@@ -4138,7 +4623,10 @@ export {
4138
4623
  Team,
4139
4624
  TeamMode,
4140
4625
  ToolExecutor,
4626
+ Toolkit,
4141
4627
  VertexAIProvider,
4628
+ WebSearchToolkit,
4629
+ WhatsAppToolkit,
4142
4630
  Workflow,
4143
4631
  anthropic,
4144
4632
  defineTool,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radaros/core",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -34,7 +34,8 @@
34
34
  "ollama": "^0.5.0",
35
35
  "openai": "^4.0.0 || ^5.0.0 || ^6.0.0",
36
36
  "pg": "^8.0.0",
37
- "@modelcontextprotocol/sdk": ">=1.0.0"
37
+ "@modelcontextprotocol/sdk": ">=1.0.0",
38
+ "googleapis": ">=100.0.0"
38
39
  },
39
40
  "peerDependenciesMeta": {
40
41
  "openai": {
@@ -63,6 +64,9 @@
63
64
  },
64
65
  "@modelcontextprotocol/sdk": {
65
66
  "optional": true
67
+ },
68
+ "googleapis": {
69
+ "optional": true
66
70
  }
67
71
  }
68
72
  }
package/src/index.ts CHANGED
@@ -135,3 +135,14 @@ export type {
135
135
  A2ASendParams,
136
136
  A2ATaskQueryParams,
137
137
  } from "./a2a/types.js";
138
+
139
+ // Toolkits
140
+ export { Toolkit } from "./toolkits/base.js";
141
+ export { WebSearchToolkit } from "./toolkits/websearch.js";
142
+ export type { WebSearchConfig } from "./toolkits/websearch.js";
143
+ export { GmailToolkit } from "./toolkits/gmail.js";
144
+ export type { GmailConfig } from "./toolkits/gmail.js";
145
+ export { WhatsAppToolkit } from "./toolkits/whatsapp.js";
146
+ export type { WhatsAppConfig } from "./toolkits/whatsapp.js";
147
+ export { HackerNewsToolkit } from "./toolkits/hackernews.js";
148
+ export type { HackerNewsConfig } from "./toolkits/hackernews.js";
@@ -0,0 +1,15 @@
1
+ import type { ToolDef } from "../tools/types.js";
2
+
3
+ /**
4
+ * Base class for all RadarOS Toolkits.
5
+ * A Toolkit is a collection of related tools that share configuration.
6
+ */
7
+ export abstract class Toolkit {
8
+ abstract readonly name: string;
9
+
10
+ /**
11
+ * Returns all tools provided by this toolkit as ToolDef[].
12
+ * Spread them into an Agent's `tools` array.
13
+ */
14
+ abstract getTools(): ToolDef[];
15
+ }