signalwire-agents 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 (245) hide show
  1. package/CLAUDE.md +112 -0
  2. package/README.md +417 -0
  3. package/dist/AgentBase.d.ts +481 -0
  4. package/dist/AgentBase.js +1155 -0
  5. package/dist/AgentBase.js.map +1 -0
  6. package/dist/AgentServer.d.ts +60 -0
  7. package/dist/AgentServer.js +128 -0
  8. package/dist/AgentServer.js.map +1 -0
  9. package/dist/AuthHandler.d.ts +56 -0
  10. package/dist/AuthHandler.js +119 -0
  11. package/dist/AuthHandler.js.map +1 -0
  12. package/dist/ConfigLoader.d.ts +68 -0
  13. package/dist/ConfigLoader.js +150 -0
  14. package/dist/ConfigLoader.js.map +1 -0
  15. package/dist/ContextBuilder.d.ts +431 -0
  16. package/dist/ContextBuilder.js +751 -0
  17. package/dist/ContextBuilder.js.map +1 -0
  18. package/dist/DataMap.d.ts +179 -0
  19. package/dist/DataMap.js +321 -0
  20. package/dist/DataMap.js.map +1 -0
  21. package/dist/Logger.d.ts +91 -0
  22. package/dist/Logger.js +191 -0
  23. package/dist/Logger.js.map +1 -0
  24. package/dist/PomBuilder.d.ts +144 -0
  25. package/dist/PomBuilder.js +249 -0
  26. package/dist/PomBuilder.js.map +1 -0
  27. package/dist/PromptManager.d.ts +85 -0
  28. package/dist/PromptManager.js +108 -0
  29. package/dist/PromptManager.js.map +1 -0
  30. package/dist/SchemaUtils.d.ts +42 -0
  31. package/dist/SchemaUtils.js +148 -0
  32. package/dist/SchemaUtils.js.map +1 -0
  33. package/dist/ServerlessAdapter.d.ts +100 -0
  34. package/dist/ServerlessAdapter.js +166 -0
  35. package/dist/ServerlessAdapter.js.map +1 -0
  36. package/dist/SessionManager.d.ts +86 -0
  37. package/dist/SessionManager.js +155 -0
  38. package/dist/SessionManager.js.map +1 -0
  39. package/dist/SslConfig.d.ts +77 -0
  40. package/dist/SslConfig.js +100 -0
  41. package/dist/SslConfig.js.map +1 -0
  42. package/dist/SwaigFunction.d.ts +88 -0
  43. package/dist/SwaigFunction.js +125 -0
  44. package/dist/SwaigFunction.js.map +1 -0
  45. package/dist/SwaigFunctionResult.d.ts +442 -0
  46. package/dist/SwaigFunctionResult.js +735 -0
  47. package/dist/SwaigFunctionResult.js.map +1 -0
  48. package/dist/SwmlBuilder.d.ts +37 -0
  49. package/dist/SwmlBuilder.js +55 -0
  50. package/dist/SwmlBuilder.js.map +1 -0
  51. package/dist/cli/agent-loader.d.ts +22 -0
  52. package/dist/cli/agent-loader.js +106 -0
  53. package/dist/cli/agent-loader.js.map +1 -0
  54. package/dist/cli/mock-data.d.ts +37 -0
  55. package/dist/cli/mock-data.js +79 -0
  56. package/dist/cli/mock-data.js.map +1 -0
  57. package/dist/cli/swaig-test.d.ts +32 -0
  58. package/dist/cli/swaig-test.js +405 -0
  59. package/dist/cli/swaig-test.js.map +1 -0
  60. package/dist/index.d.ts +36 -0
  61. package/dist/index.js +44 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/prefabs/ConciergeAgent.d.ts +64 -0
  64. package/dist/prefabs/ConciergeAgent.js +188 -0
  65. package/dist/prefabs/ConciergeAgent.js.map +1 -0
  66. package/dist/prefabs/FAQBotAgent.d.ts +68 -0
  67. package/dist/prefabs/FAQBotAgent.js +206 -0
  68. package/dist/prefabs/FAQBotAgent.js.map +1 -0
  69. package/dist/prefabs/InfoGathererAgent.d.ts +73 -0
  70. package/dist/prefabs/InfoGathererAgent.js +202 -0
  71. package/dist/prefabs/InfoGathererAgent.js.map +1 -0
  72. package/dist/prefabs/ReceptionistAgent.d.ts +65 -0
  73. package/dist/prefabs/ReceptionistAgent.js +205 -0
  74. package/dist/prefabs/ReceptionistAgent.js.map +1 -0
  75. package/dist/prefabs/SurveyAgent.d.ts +84 -0
  76. package/dist/prefabs/SurveyAgent.js +324 -0
  77. package/dist/prefabs/SurveyAgent.js.map +1 -0
  78. package/dist/prefabs/index.d.ts +13 -0
  79. package/dist/prefabs/index.js +14 -0
  80. package/dist/prefabs/index.js.map +1 -0
  81. package/dist/skills/SkillBase.d.ts +134 -0
  82. package/dist/skills/SkillBase.js +105 -0
  83. package/dist/skills/SkillBase.js.map +1 -0
  84. package/dist/skills/SkillManager.d.ts +83 -0
  85. package/dist/skills/SkillManager.js +159 -0
  86. package/dist/skills/SkillManager.js.map +1 -0
  87. package/dist/skills/SkillRegistry.d.ts +105 -0
  88. package/dist/skills/SkillRegistry.js +195 -0
  89. package/dist/skills/SkillRegistry.js.map +1 -0
  90. package/dist/skills/builtin/api_ninjas_trivia.d.ts +35 -0
  91. package/dist/skills/builtin/api_ninjas_trivia.js +161 -0
  92. package/dist/skills/builtin/api_ninjas_trivia.js.map +1 -0
  93. package/dist/skills/builtin/claude_skills.d.ts +34 -0
  94. package/dist/skills/builtin/claude_skills.js +156 -0
  95. package/dist/skills/builtin/claude_skills.js.map +1 -0
  96. package/dist/skills/builtin/custom_skills.d.ts +59 -0
  97. package/dist/skills/builtin/custom_skills.js +231 -0
  98. package/dist/skills/builtin/custom_skills.js.map +1 -0
  99. package/dist/skills/builtin/datasphere.d.ts +34 -0
  100. package/dist/skills/builtin/datasphere.js +163 -0
  101. package/dist/skills/builtin/datasphere.js.map +1 -0
  102. package/dist/skills/builtin/datasphere_serverless.d.ts +54 -0
  103. package/dist/skills/builtin/datasphere_serverless.js +155 -0
  104. package/dist/skills/builtin/datasphere_serverless.js.map +1 -0
  105. package/dist/skills/builtin/datetime.d.ts +31 -0
  106. package/dist/skills/builtin/datetime.js +95 -0
  107. package/dist/skills/builtin/datetime.js.map +1 -0
  108. package/dist/skills/builtin/google_maps.d.ts +33 -0
  109. package/dist/skills/builtin/google_maps.js +233 -0
  110. package/dist/skills/builtin/google_maps.js.map +1 -0
  111. package/dist/skills/builtin/index.d.ts +28 -0
  112. package/dist/skills/builtin/index.js +75 -0
  113. package/dist/skills/builtin/index.js.map +1 -0
  114. package/dist/skills/builtin/info_gatherer.d.ts +52 -0
  115. package/dist/skills/builtin/info_gatherer.js +237 -0
  116. package/dist/skills/builtin/info_gatherer.js.map +1 -0
  117. package/dist/skills/builtin/joke.d.ts +32 -0
  118. package/dist/skills/builtin/joke.js +142 -0
  119. package/dist/skills/builtin/joke.js.map +1 -0
  120. package/dist/skills/builtin/math.d.ts +33 -0
  121. package/dist/skills/builtin/math.js +120 -0
  122. package/dist/skills/builtin/math.js.map +1 -0
  123. package/dist/skills/builtin/mcp_gateway.d.ts +35 -0
  124. package/dist/skills/builtin/mcp_gateway.js +85 -0
  125. package/dist/skills/builtin/mcp_gateway.js.map +1 -0
  126. package/dist/skills/builtin/native_vector_search.d.ts +44 -0
  127. package/dist/skills/builtin/native_vector_search.js +248 -0
  128. package/dist/skills/builtin/native_vector_search.js.map +1 -0
  129. package/dist/skills/builtin/play_background_file.d.ts +34 -0
  130. package/dist/skills/builtin/play_background_file.js +135 -0
  131. package/dist/skills/builtin/play_background_file.js.map +1 -0
  132. package/dist/skills/builtin/spider.d.ts +34 -0
  133. package/dist/skills/builtin/spider.js +164 -0
  134. package/dist/skills/builtin/spider.js.map +1 -0
  135. package/dist/skills/builtin/swml_transfer.d.ts +44 -0
  136. package/dist/skills/builtin/swml_transfer.js +204 -0
  137. package/dist/skills/builtin/swml_transfer.js.map +1 -0
  138. package/dist/skills/builtin/weather_api.d.ts +34 -0
  139. package/dist/skills/builtin/weather_api.js +136 -0
  140. package/dist/skills/builtin/weather_api.js.map +1 -0
  141. package/dist/skills/builtin/web_search.d.ts +34 -0
  142. package/dist/skills/builtin/web_search.js +141 -0
  143. package/dist/skills/builtin/web_search.js.map +1 -0
  144. package/dist/skills/builtin/wikipedia_search.d.ts +34 -0
  145. package/dist/skills/builtin/wikipedia_search.js +166 -0
  146. package/dist/skills/builtin/wikipedia_search.js.map +1 -0
  147. package/dist/skills/index.d.ts +11 -0
  148. package/dist/skills/index.js +10 -0
  149. package/dist/skills/index.js.map +1 -0
  150. package/dist/types.d.ts +85 -0
  151. package/dist/types.js +5 -0
  152. package/dist/types.js.map +1 -0
  153. package/docs/agent-guide.md +869 -0
  154. package/docs/api-reference.md +2419 -0
  155. package/docs/architecture.md +843 -0
  156. package/docs/cli-guide.md +579 -0
  157. package/docs/configuration.md +422 -0
  158. package/docs/contexts-guide.md +787 -0
  159. package/docs/datamap-guide.md +1141 -0
  160. package/docs/prefabs-guide.md +830 -0
  161. package/docs/security.md +436 -0
  162. package/docs/serverless-guide.md +513 -0
  163. package/docs/skills-guide.md +845 -0
  164. package/docs/swaig-reference.md +1627 -0
  165. package/examples/call-flow.ts +60 -0
  166. package/examples/contexts-steps.ts +62 -0
  167. package/examples/datamap-tools.ts +52 -0
  168. package/examples/dynamic-config.ts +51 -0
  169. package/examples/multi-agent.ts +67 -0
  170. package/examples/pom-prompt.ts +68 -0
  171. package/examples/simple-agent.ts +33 -0
  172. package/package.json +35 -0
  173. package/src/AgentBase.ts +1279 -0
  174. package/src/AgentServer.ts +149 -0
  175. package/src/AuthHandler.ts +141 -0
  176. package/src/ConfigLoader.ts +165 -0
  177. package/src/ContextBuilder.ts +796 -0
  178. package/src/DataMap.ts +365 -0
  179. package/src/Logger.ts +197 -0
  180. package/src/PomBuilder.ts +298 -0
  181. package/src/PromptManager.ts +133 -0
  182. package/src/SchemaUtils.ts +170 -0
  183. package/src/ServerlessAdapter.ts +218 -0
  184. package/src/SessionManager.ts +161 -0
  185. package/src/SslConfig.ts +118 -0
  186. package/src/SwaigFunction.ts +168 -0
  187. package/src/SwaigFunctionResult.ts +864 -0
  188. package/src/SwmlBuilder.ts +62 -0
  189. package/src/cli/agent-loader.ts +122 -0
  190. package/src/cli/mock-data.ts +110 -0
  191. package/src/cli/swaig-test.ts +440 -0
  192. package/src/index.ts +99 -0
  193. package/src/prefabs/ConciergeAgent.ts +251 -0
  194. package/src/prefabs/FAQBotAgent.ts +263 -0
  195. package/src/prefabs/InfoGathererAgent.ts +276 -0
  196. package/src/prefabs/ReceptionistAgent.ts +277 -0
  197. package/src/prefabs/SurveyAgent.ts +413 -0
  198. package/src/prefabs/index.ts +23 -0
  199. package/src/skills/SkillBase.ts +184 -0
  200. package/src/skills/SkillManager.ts +177 -0
  201. package/src/skills/SkillRegistry.ts +231 -0
  202. package/src/skills/builtin/api_ninjas_trivia.ts +230 -0
  203. package/src/skills/builtin/claude_skills.ts +239 -0
  204. package/src/skills/builtin/custom_skills.ts +326 -0
  205. package/src/skills/builtin/datasphere.ts +225 -0
  206. package/src/skills/builtin/datasphere_serverless.ts +194 -0
  207. package/src/skills/builtin/datetime.ts +115 -0
  208. package/src/skills/builtin/google_maps.ts +347 -0
  209. package/src/skills/builtin/index.ts +79 -0
  210. package/src/skills/builtin/info_gatherer.ts +331 -0
  211. package/src/skills/builtin/joke.ts +180 -0
  212. package/src/skills/builtin/math.ts +150 -0
  213. package/src/skills/builtin/mcp_gateway.ts +99 -0
  214. package/src/skills/builtin/native_vector_search.ts +327 -0
  215. package/src/skills/builtin/play_background_file.ts +189 -0
  216. package/src/skills/builtin/spider.ts +232 -0
  217. package/src/skills/builtin/swml_transfer.ts +285 -0
  218. package/src/skills/builtin/weather_api.ts +193 -0
  219. package/src/skills/builtin/web_search.ts +212 -0
  220. package/src/skills/builtin/wikipedia_search.ts +248 -0
  221. package/src/skills/index.ts +12 -0
  222. package/src/types.ts +99 -0
  223. package/tests/AgentBase.test.ts +693 -0
  224. package/tests/AgentServer.test.ts +102 -0
  225. package/tests/AuthHandler.test.ts +121 -0
  226. package/tests/ConfigLoader.test.ts +121 -0
  227. package/tests/DataMap.test.ts +286 -0
  228. package/tests/Logger.test.ts +218 -0
  229. package/tests/PomBuilder.test.ts +164 -0
  230. package/tests/ProxyDetection.test.ts +185 -0
  231. package/tests/SchemaUtils.test.ts +106 -0
  232. package/tests/ServerlessAdapter.test.ts +200 -0
  233. package/tests/SessionManager.test.ts +139 -0
  234. package/tests/SslConfig.test.ts +97 -0
  235. package/tests/SwaigFunctionResult.test.ts +298 -0
  236. package/tests/cli/mock-data.test.ts +94 -0
  237. package/tests/cli/swaig-test.test.ts +96 -0
  238. package/tests/prefabs/prefabs.test.ts +854 -0
  239. package/tests/setup.ts +6 -0
  240. package/tests/skills/SkillBase.test.ts +118 -0
  241. package/tests/skills/SkillManager.test.ts +164 -0
  242. package/tests/skills/SkillRegistry.test.ts +124 -0
  243. package/tests/skills/builtin.test.ts +924 -0
  244. package/tsconfig.json +18 -0
  245. package/vitest.config.ts +9 -0
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Weather API Skill - Fetches current weather data from OpenWeatherMap.
3
+ *
4
+ * Tier 2 built-in skill: requires WEATHER_API_KEY environment variable.
5
+ * Uses the OpenWeatherMap API to retrieve current weather conditions
6
+ * for a specified location.
7
+ */
8
+
9
+ import { SkillBase } from '../SkillBase.js';
10
+ import type {
11
+ SkillManifest,
12
+ SkillToolDefinition,
13
+ SkillPromptSection,
14
+ SkillConfig,
15
+ } from '../SkillBase.js';
16
+ import { SwaigFunctionResult } from '../../SwaigFunctionResult.js';
17
+
18
+ /** Response shape from the OpenWeatherMap current weather endpoint. */
19
+ interface WeatherApiResponse {
20
+ name: string;
21
+ sys: { country: string };
22
+ main: {
23
+ temp: number;
24
+ feels_like: number;
25
+ humidity: number;
26
+ pressure: number;
27
+ temp_min: number;
28
+ temp_max: number;
29
+ };
30
+ weather: Array<{ main: string; description: string }>;
31
+ wind: { speed: number; deg: number };
32
+ visibility?: number;
33
+ clouds?: { all: number };
34
+ cod: number;
35
+ message?: string;
36
+ }
37
+
38
+ /**
39
+ * Fetches current weather data from OpenWeatherMap for any location worldwide.
40
+ *
41
+ * Tier 2 built-in skill. Requires the `WEATHER_API_KEY` environment variable
42
+ * containing a valid OpenWeatherMap API key. Supports metric, imperial, and
43
+ * standard temperature units via the `units` config option.
44
+ */
45
+ export class WeatherApiSkill extends SkillBase {
46
+ /**
47
+ * @param config - Optional configuration; supports `units` ("metric"|"imperial"|"standard").
48
+ */
49
+ constructor(config?: SkillConfig) {
50
+ super('weather_api', config);
51
+ }
52
+
53
+ /** @returns Manifest declaring WEATHER_API_KEY as required and config schema for units. */
54
+ getManifest(): SkillManifest {
55
+ return {
56
+ name: 'weather_api',
57
+ description:
58
+ 'Fetches current weather data from OpenWeatherMap for any location worldwide.',
59
+ version: '1.0.0',
60
+ author: 'SignalWire',
61
+ tags: ['weather', 'api', 'openweathermap', 'external'],
62
+ requiredEnvVars: ['WEATHER_API_KEY'],
63
+ configSchema: {
64
+ units: {
65
+ type: 'string',
66
+ description:
67
+ 'Temperature units: "metric" (Celsius), "imperial" (Fahrenheit), or "standard" (Kelvin).',
68
+ default: 'metric',
69
+ },
70
+ },
71
+ };
72
+ }
73
+
74
+ /** @returns A single `get_weather` tool that fetches current weather for a location. */
75
+ getTools(): SkillToolDefinition[] {
76
+ const units = this.getConfig<string>('units', 'metric');
77
+
78
+ return [
79
+ {
80
+ name: 'get_weather',
81
+ description:
82
+ 'Get the current weather conditions for a specified location. Returns temperature, humidity, wind speed, and weather description.',
83
+ parameters: {
84
+ location: {
85
+ type: 'string',
86
+ description:
87
+ 'The city name, optionally with country code (e.g., "London", "Paris,FR", "New York,US").',
88
+ },
89
+ },
90
+ required: ['location'],
91
+ handler: async (args: Record<string, unknown>) => {
92
+ const location = args.location as string | undefined;
93
+
94
+ if (!location || typeof location !== 'string' || location.trim().length === 0) {
95
+ return new SwaigFunctionResult(
96
+ 'Please provide a location to get the weather for.',
97
+ );
98
+ }
99
+
100
+ const apiKey = process.env['WEATHER_API_KEY'];
101
+ if (!apiKey) {
102
+ return new SwaigFunctionResult(
103
+ 'Weather service is not configured. The WEATHER_API_KEY environment variable is missing.',
104
+ );
105
+ }
106
+
107
+ try {
108
+ const encodedLocation = encodeURIComponent(location.trim());
109
+ const url = `https://api.openweathermap.org/data/2.5/weather?q=${encodedLocation}&appid=${apiKey}&units=${units}`;
110
+
111
+ const response = await fetch(url);
112
+ const data = (await response.json()) as WeatherApiResponse;
113
+
114
+ if (!response.ok || data.cod !== 200) {
115
+ const errorMessage = data.message ?? `HTTP ${response.status}`;
116
+ return new SwaigFunctionResult(
117
+ `Could not retrieve weather for "${location}": ${errorMessage}. Please check the location name and try again.`,
118
+ );
119
+ }
120
+
121
+ const unitLabel = units === 'imperial' ? 'F' : units === 'standard' ? 'K' : 'C';
122
+ const speedUnit = units === 'imperial' ? 'mph' : 'm/s';
123
+
124
+ const weatherDesc =
125
+ data.weather.length > 0
126
+ ? data.weather.map((w) => w.description).join(', ')
127
+ : 'unknown';
128
+
129
+ const parts: string[] = [
130
+ `Weather for ${data.name}, ${data.sys.country}:`,
131
+ `Conditions: ${weatherDesc}.`,
132
+ `Temperature: ${data.main.temp}\u00B0${unitLabel} (feels like ${data.main.feels_like}\u00B0${unitLabel}).`,
133
+ `High: ${data.main.temp_max}\u00B0${unitLabel}, Low: ${data.main.temp_min}\u00B0${unitLabel}.`,
134
+ `Humidity: ${data.main.humidity}%.`,
135
+ `Wind: ${data.wind.speed} ${speedUnit}.`,
136
+ `Pressure: ${data.main.pressure} hPa.`,
137
+ ];
138
+
139
+ if (data.visibility !== undefined) {
140
+ const visKm = (data.visibility / 1000).toFixed(1);
141
+ parts.push(`Visibility: ${visKm} km.`);
142
+ }
143
+
144
+ if (data.clouds?.all !== undefined) {
145
+ parts.push(`Cloud cover: ${data.clouds.all}%.`);
146
+ }
147
+
148
+ return new SwaigFunctionResult(parts.join(' '));
149
+ } catch (err) {
150
+ const message = err instanceof Error ? err.message : String(err);
151
+ return new SwaigFunctionResult(
152
+ `Failed to fetch weather data for "${location}": ${message}`,
153
+ );
154
+ }
155
+ },
156
+ },
157
+ ];
158
+ }
159
+
160
+ /** @returns Prompt section describing weather lookup capabilities and configured units. */
161
+ getPromptSections(): SkillPromptSection[] {
162
+ const units = this.getConfig<string>('units', 'metric');
163
+ const unitDesc =
164
+ units === 'imperial'
165
+ ? 'Fahrenheit'
166
+ : units === 'standard'
167
+ ? 'Kelvin'
168
+ : 'Celsius';
169
+
170
+ return [
171
+ {
172
+ title: 'Weather Information',
173
+ body: 'You can look up current weather conditions for any location worldwide.',
174
+ bullets: [
175
+ 'Use the get_weather tool when a user asks about current weather conditions.',
176
+ 'You can specify a city name, optionally with a country code (e.g., "London", "Paris,FR").',
177
+ `Temperature is reported in ${unitDesc}.`,
178
+ 'The weather data includes temperature, humidity, wind speed, pressure, and general conditions.',
179
+ 'If the user asks about a forecast or future weather, let them know you can only provide current conditions.',
180
+ ],
181
+ },
182
+ ];
183
+ }
184
+ }
185
+
186
+ /**
187
+ * Factory function for creating WeatherApiSkill instances.
188
+ * @param config - Optional skill configuration.
189
+ * @returns A new WeatherApiSkill instance.
190
+ */
191
+ export function createSkill(config?: SkillConfig): WeatherApiSkill {
192
+ return new WeatherApiSkill(config);
193
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Web Search Skill - Searches the web using Google Custom Search API.
3
+ *
4
+ * Tier 3 built-in skill: requires GOOGLE_SEARCH_API_KEY and GOOGLE_SEARCH_CX
5
+ * environment variables. Uses the Google Custom Search JSON API to perform
6
+ * web searches and return formatted results.
7
+ */
8
+
9
+ import { SkillBase } from '../SkillBase.js';
10
+ import type {
11
+ SkillManifest,
12
+ SkillToolDefinition,
13
+ SkillPromptSection,
14
+ SkillConfig,
15
+ } from '../SkillBase.js';
16
+ import { SwaigFunctionResult } from '../../SwaigFunctionResult.js';
17
+
18
+ /** A single search result item from the Google Custom Search API. */
19
+ interface GoogleSearchItem {
20
+ /** Title of the search result. */
21
+ title: string;
22
+ /** URL of the search result. */
23
+ link: string;
24
+ /** Brief text snippet from the page. */
25
+ snippet: string;
26
+ /** Display-friendly domain name. */
27
+ displayLink?: string;
28
+ /** Structured page metadata. */
29
+ pagemap?: Record<string, unknown>;
30
+ }
31
+
32
+ /** Response shape from the Google Custom Search JSON API. */
33
+ interface GoogleSearchResponse {
34
+ /** Array of search result items. */
35
+ items?: GoogleSearchItem[];
36
+ /** Metadata about the search execution. */
37
+ searchInformation?: {
38
+ totalResults: string;
39
+ searchTime: number;
40
+ formattedTotalResults: string;
41
+ formattedSearchTime: string;
42
+ };
43
+ /** Error details if the request failed. */
44
+ error?: {
45
+ code: number;
46
+ message: string;
47
+ errors: Array<{ message: string; domain: string; reason: string }>;
48
+ };
49
+ }
50
+
51
+ /**
52
+ * Searches the web using the Google Custom Search JSON API.
53
+ *
54
+ * Tier 3 built-in skill. Requires `GOOGLE_SEARCH_API_KEY` and `GOOGLE_SEARCH_CX`
55
+ * environment variables. Supports `max_results` (1-10) and `safe_search`
56
+ * ("off"|"medium"|"high") config options.
57
+ */
58
+ export class WebSearchSkill extends SkillBase {
59
+ /**
60
+ * @param config - Optional configuration; supports `max_results` and `safe_search`.
61
+ */
62
+ constructor(config?: SkillConfig) {
63
+ super('web_search', config);
64
+ }
65
+
66
+ /** @returns Manifest declaring GOOGLE_SEARCH_API_KEY and GOOGLE_SEARCH_CX as required. */
67
+ getManifest(): SkillManifest {
68
+ return {
69
+ name: 'web_search',
70
+ description:
71
+ 'Searches the web using Google Custom Search API and returns formatted results with titles, links, and snippets.',
72
+ version: '1.0.0',
73
+ author: 'SignalWire',
74
+ tags: ['search', 'web', 'google', 'api', 'external'],
75
+ requiredEnvVars: ['GOOGLE_SEARCH_API_KEY', 'GOOGLE_SEARCH_CX'],
76
+ configSchema: {
77
+ max_results: {
78
+ type: 'number',
79
+ description:
80
+ 'Maximum number of results to return (1-10). Defaults to 5.',
81
+ default: 5,
82
+ },
83
+ safe_search: {
84
+ type: 'string',
85
+ description:
86
+ 'Safe search level: "off", "medium", or "high". Defaults to "medium".',
87
+ default: 'medium',
88
+ },
89
+ },
90
+ };
91
+ }
92
+
93
+ /** @returns A single `web_search` tool that performs a Google search and returns formatted results. */
94
+ getTools(): SkillToolDefinition[] {
95
+ const configMaxResults = this.getConfig<number>('max_results', 5);
96
+ const safeSearch = this.getConfig<string>('safe_search', 'medium');
97
+
98
+ return [
99
+ {
100
+ name: 'web_search',
101
+ description:
102
+ 'Search the web for information on any topic. Returns a list of results with titles, links, and brief descriptions.',
103
+ parameters: {
104
+ query: {
105
+ type: 'string',
106
+ description: 'The search query to look up on the web.',
107
+ },
108
+ num_results: {
109
+ type: 'number',
110
+ description: `Number of results to return (1-10). Defaults to ${configMaxResults}.`,
111
+ },
112
+ },
113
+ required: ['query'],
114
+ handler: async (args: Record<string, unknown>) => {
115
+ const query = args.query as string | undefined;
116
+ const numResults = args.num_results as number | undefined;
117
+
118
+ if (!query || typeof query !== 'string' || query.trim().length === 0) {
119
+ return new SwaigFunctionResult(
120
+ 'Please provide a search query.',
121
+ );
122
+ }
123
+
124
+ const apiKey = process.env['GOOGLE_SEARCH_API_KEY'];
125
+ const cx = process.env['GOOGLE_SEARCH_CX'];
126
+
127
+ if (!apiKey || !cx) {
128
+ return new SwaigFunctionResult(
129
+ 'Web search is not configured. The GOOGLE_SEARCH_API_KEY and GOOGLE_SEARCH_CX environment variables are required.',
130
+ );
131
+ }
132
+
133
+ const count = Math.max(1, Math.min(10, numResults ?? configMaxResults));
134
+
135
+ try {
136
+ const encodedQuery = encodeURIComponent(query.trim());
137
+ const safeParam = safeSearch !== 'off' ? `&safe=${safeSearch}` : '';
138
+ const url =
139
+ `https://www.googleapis.com/customsearch/v1` +
140
+ `?key=${apiKey}&cx=${cx}&q=${encodedQuery}&num=${count}${safeParam}`;
141
+
142
+ const response = await fetch(url);
143
+ const data = (await response.json()) as GoogleSearchResponse;
144
+
145
+ if (data.error) {
146
+ return new SwaigFunctionResult(
147
+ `Web search failed: ${data.error.message}`,
148
+ );
149
+ }
150
+
151
+ if (!data.items || data.items.length === 0) {
152
+ return new SwaigFunctionResult(
153
+ `No results found for "${query}".`,
154
+ );
155
+ }
156
+
157
+ const totalResults = data.searchInformation?.formattedTotalResults ?? 'unknown';
158
+ const searchTime = data.searchInformation?.formattedSearchTime ?? 'unknown';
159
+
160
+ const parts: string[] = [
161
+ `Web search results for "${query}" (${totalResults} results in ${searchTime} seconds):`,
162
+ '',
163
+ ];
164
+
165
+ for (let i = 0; i < data.items.length; i++) {
166
+ const item = data.items[i];
167
+ parts.push(`${i + 1}. ${item.title}`);
168
+ parts.push(` URL: ${item.link}`);
169
+ if (item.snippet) {
170
+ parts.push(` ${item.snippet.replace(/\n/g, ' ').trim()}`);
171
+ }
172
+ parts.push('');
173
+ }
174
+
175
+ return new SwaigFunctionResult(parts.join('\n').trim());
176
+ } catch (err) {
177
+ const message = err instanceof Error ? err.message : String(err);
178
+ return new SwaigFunctionResult(
179
+ `Failed to perform web search for "${query}": ${message}`,
180
+ );
181
+ }
182
+ },
183
+ },
184
+ ];
185
+ }
186
+
187
+ /** @returns Prompt section describing web search capabilities and usage guidance. */
188
+ getPromptSections(): SkillPromptSection[] {
189
+ return [
190
+ {
191
+ title: 'Web Search',
192
+ body: 'You can search the web for current information on any topic.',
193
+ bullets: [
194
+ 'Use the web_search tool when the user asks about current events, facts, or any information you may not have.',
195
+ 'You can specify the number of results to return (1-10).',
196
+ 'Results include titles, URLs, and brief descriptions/snippets.',
197
+ 'If a search returns no results, try rephrasing the query or using different terms.',
198
+ 'Summarize the search results for the user rather than reading them verbatim.',
199
+ ],
200
+ },
201
+ ];
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Factory function for creating WebSearchSkill instances.
207
+ * @param config - Optional skill configuration.
208
+ * @returns A new WebSearchSkill instance.
209
+ */
210
+ export function createSkill(config?: SkillConfig): WebSearchSkill {
211
+ return new WebSearchSkill(config);
212
+ }
@@ -0,0 +1,248 @@
1
+ /**
2
+ * Wikipedia Search Skill - Searches Wikipedia for article summaries.
3
+ *
4
+ * Tier 3 built-in skill: no external API key required.
5
+ * Uses the Wikipedia REST API to fetch article summaries and extracts
6
+ * for any given topic.
7
+ */
8
+
9
+ import { SkillBase } from '../SkillBase.js';
10
+ import type {
11
+ SkillManifest,
12
+ SkillToolDefinition,
13
+ SkillPromptSection,
14
+ SkillConfig,
15
+ } from '../SkillBase.js';
16
+ import { SwaigFunctionResult } from '../../SwaigFunctionResult.js';
17
+
18
+ /** Response shape from the Wikipedia REST API page summary endpoint. */
19
+ interface WikipediaSummaryResponse {
20
+ /** Page type (e.g., "standard", "disambiguation"). */
21
+ type: string;
22
+ /** Article title. */
23
+ title: string;
24
+ /** HTML-formatted display title. */
25
+ displaytitle?: string;
26
+ /** Plain-text article extract/summary. */
27
+ extract: string;
28
+ /** HTML-formatted extract. */
29
+ extract_html?: string;
30
+ /** Short description of the article. */
31
+ description?: string;
32
+ /** URLs to the full article. */
33
+ content_urls?: {
34
+ desktop?: { page: string };
35
+ mobile?: { page: string };
36
+ };
37
+ /** Thumbnail image information. */
38
+ thumbnail?: {
39
+ source: string;
40
+ width: number;
41
+ height: number;
42
+ };
43
+ }
44
+
45
+ /** Response shape from the Wikipedia search API. */
46
+ interface WikipediaSearchResponse {
47
+ /** Array of matching page results. */
48
+ pages?: Array<{
49
+ id: number;
50
+ key: string;
51
+ title: string;
52
+ description?: string;
53
+ excerpt?: string;
54
+ }>;
55
+ }
56
+
57
+ /**
58
+ * Searches Wikipedia for article summaries and extracts.
59
+ *
60
+ * Tier 3 built-in skill with no external API key required. Uses the Wikipedia
61
+ * REST API to fetch article summaries for any given topic, with a configurable
62
+ * number of sentences.
63
+ */
64
+ export class WikipediaSearchSkill extends SkillBase {
65
+ /**
66
+ * @param config - Optional configuration (no config keys used by this skill).
67
+ */
68
+ constructor(config?: SkillConfig) {
69
+ super('wikipedia_search', config);
70
+ }
71
+
72
+ /** @returns Manifest with skill metadata (no required env vars). */
73
+ getManifest(): SkillManifest {
74
+ return {
75
+ name: 'wikipedia_search',
76
+ description:
77
+ 'Searches Wikipedia for article summaries and extracts. No API key required.',
78
+ version: '1.0.0',
79
+ author: 'SignalWire',
80
+ tags: ['search', 'wikipedia', 'encyclopedia', 'knowledge', 'external'],
81
+ };
82
+ }
83
+
84
+ /** @returns A single `search_wikipedia` tool that fetches article summaries from Wikipedia. */
85
+ getTools(): SkillToolDefinition[] {
86
+ return [
87
+ {
88
+ name: 'search_wikipedia',
89
+ description:
90
+ 'Search Wikipedia for information about a topic. Returns a summary of the most relevant article.',
91
+ parameters: {
92
+ query: {
93
+ type: 'string',
94
+ description:
95
+ 'The topic or term to search for on Wikipedia.',
96
+ },
97
+ sentences: {
98
+ type: 'number',
99
+ description:
100
+ 'Number of sentences to return in the summary (1-10). Defaults to 3.',
101
+ },
102
+ },
103
+ required: ['query'],
104
+ handler: async (args: Record<string, unknown>) => {
105
+ const query = args.query as string | undefined;
106
+ const sentences = args.sentences as number | undefined;
107
+
108
+ if (!query || typeof query !== 'string' || query.trim().length === 0) {
109
+ return new SwaigFunctionResult(
110
+ 'Please provide a topic to search for on Wikipedia.',
111
+ );
112
+ }
113
+
114
+ const sentenceCount = Math.max(1, Math.min(10, sentences ?? 3));
115
+
116
+ try {
117
+ // First, try a direct page summary lookup
118
+ const encodedQuery = encodeURIComponent(query.trim().replace(/\s+/g, '_'));
119
+ const summaryUrl = `https://en.wikipedia.org/api/rest_v1/page/summary/${encodedQuery}`;
120
+
121
+ const summaryResponse = await fetch(summaryUrl, {
122
+ headers: { 'Accept': 'application/json', 'User-Agent': 'SignalWireAgentsSDK/1.0' },
123
+ });
124
+
125
+ if (summaryResponse.ok) {
126
+ const data = (await summaryResponse.json()) as WikipediaSummaryResponse;
127
+
128
+ if (data.type !== 'disambiguation' && data.extract) {
129
+ const extractSentences = data.extract.split(/(?<=[.!?])\s+/);
130
+ const trimmedExtract = extractSentences
131
+ .slice(0, sentenceCount)
132
+ .join(' ')
133
+ .trim();
134
+
135
+ const pageUrl = data.content_urls?.desktop?.page ?? '';
136
+ const parts: string[] = [
137
+ `Wikipedia: ${data.title}`,
138
+ ];
139
+ if (data.description) {
140
+ parts.push(`(${data.description})`);
141
+ }
142
+ parts.push('');
143
+ parts.push(trimmedExtract);
144
+ if (pageUrl) {
145
+ parts.push('');
146
+ parts.push(`Read more: ${pageUrl}`);
147
+ }
148
+
149
+ return new SwaigFunctionResult(parts.join('\n').trim());
150
+ }
151
+ }
152
+
153
+ // If direct lookup failed, use the search API
154
+ const searchUrl = `https://en.wikipedia.org/w/rest.php/v1/search/page?q=${encodeURIComponent(query.trim())}&limit=1`;
155
+
156
+ const searchResponse = await fetch(searchUrl, {
157
+ headers: { 'Accept': 'application/json', 'User-Agent': 'SignalWireAgentsSDK/1.0' },
158
+ });
159
+
160
+ if (!searchResponse.ok) {
161
+ return new SwaigFunctionResult(
162
+ `Wikipedia search failed with status ${searchResponse.status}. Please try a different search term.`,
163
+ );
164
+ }
165
+
166
+ const searchData = (await searchResponse.json()) as WikipediaSearchResponse;
167
+
168
+ if (!searchData.pages || searchData.pages.length === 0) {
169
+ return new SwaigFunctionResult(
170
+ `No Wikipedia article found for "${query}". Try a different search term.`,
171
+ );
172
+ }
173
+
174
+ // Fetch the summary for the best match
175
+ const bestMatch = searchData.pages[0];
176
+ const matchUrl = `https://en.wikipedia.org/api/rest_v1/page/summary/${encodeURIComponent(bestMatch.key)}`;
177
+
178
+ const matchResponse = await fetch(matchUrl, {
179
+ headers: { 'Accept': 'application/json', 'User-Agent': 'SignalWireAgentsSDK/1.0' },
180
+ });
181
+
182
+ if (!matchResponse.ok) {
183
+ // Fall back to the search excerpt
184
+ const excerpt = bestMatch.excerpt
185
+ ? bestMatch.excerpt.replace(/<[^>]*>/g, '').trim()
186
+ : 'No summary available.';
187
+ return new SwaigFunctionResult(
188
+ `Wikipedia: ${bestMatch.title}\n\n${excerpt}`,
189
+ );
190
+ }
191
+
192
+ const matchData = (await matchResponse.json()) as WikipediaSummaryResponse;
193
+ const extractSentences = matchData.extract.split(/(?<=[.!?])\s+/);
194
+ const trimmedExtract = extractSentences
195
+ .slice(0, sentenceCount)
196
+ .join(' ')
197
+ .trim();
198
+
199
+ const pageUrl = matchData.content_urls?.desktop?.page ?? '';
200
+ const parts: string[] = [`Wikipedia: ${matchData.title}`];
201
+ if (matchData.description) {
202
+ parts.push(`(${matchData.description})`);
203
+ }
204
+ parts.push('');
205
+ parts.push(trimmedExtract);
206
+ if (pageUrl) {
207
+ parts.push('');
208
+ parts.push(`Read more: ${pageUrl}`);
209
+ }
210
+
211
+ return new SwaigFunctionResult(parts.join('\n').trim());
212
+ } catch (err) {
213
+ const message = err instanceof Error ? err.message : String(err);
214
+ return new SwaigFunctionResult(
215
+ `Failed to search Wikipedia for "${query}": ${message}`,
216
+ );
217
+ }
218
+ },
219
+ },
220
+ ];
221
+ }
222
+
223
+ /** @returns Prompt section describing Wikipedia search capabilities and usage guidance. */
224
+ getPromptSections(): SkillPromptSection[] {
225
+ return [
226
+ {
227
+ title: 'Wikipedia Search',
228
+ body: 'You can search Wikipedia for information about any topic.',
229
+ bullets: [
230
+ 'Use the search_wikipedia tool when the user asks about a specific topic, person, place, or concept.',
231
+ 'Wikipedia provides encyclopedic summaries that are generally reliable for factual information.',
232
+ 'You can specify the number of sentences to return (1-10) for shorter or more detailed summaries.',
233
+ 'If a search returns no results, try rephrasing with the most common name or spelling.',
234
+ 'Summarize the information naturally rather than reading it verbatim.',
235
+ ],
236
+ },
237
+ ];
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Factory function for creating WikipediaSearchSkill instances.
243
+ * @param config - Optional skill configuration.
244
+ * @returns A new WikipediaSearchSkill instance.
245
+ */
246
+ export function createSkill(config?: SkillConfig): WikipediaSearchSkill {
247
+ return new WikipediaSearchSkill(config);
248
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Skills framework for SignalWire AI Agents.
3
+ *
4
+ * Exports the base class, manager, registry, and all associated types
5
+ * needed to create, register, and manage agent skills.
6
+ */
7
+
8
+ export { SkillBase } from './SkillBase.js';
9
+ export type { SkillConfig, SkillToolDefinition, SkillPromptSection, SkillManifest } from './SkillBase.js';
10
+ export { SkillManager } from './SkillManager.js';
11
+ export { SkillRegistry } from './SkillRegistry.js';
12
+ export type { SkillFactory } from './SkillRegistry.js';