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,845 @@
1
+ # Skills System Guide
2
+
3
+ Complete guide to the Skills system in the SignalWire AI Agents TypeScript SDK -- modular, reusable capabilities that can be added to any agent.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Overview](#overview)
10
+ 2. [Using Skills](#using-skills)
11
+ 3. [Built-in Skills (18)](#built-in-skills-18)
12
+ 4. [Skill Configuration](#skill-configuration)
13
+ 5. [Skill Registry](#skill-registry)
14
+ 6. [Creating Custom Skills](#creating-custom-skills)
15
+ 7. [Skill Lifecycle](#skill-lifecycle)
16
+ 8. [Environment Validation](#environment-validation)
17
+
18
+ ---
19
+
20
+ ## Overview
21
+
22
+ Skills are **modular capabilities** that can be added to an agent at runtime. Each skill encapsulates one or more SWAIG tools, prompt sections, speech recognition hints, and global data -- everything needed to give an agent a new ability.
23
+
24
+ The skills system is built on three core classes:
25
+
26
+ | Class | Role | Location |
27
+ |---|---|---|
28
+ | `SkillBase` | Abstract base class that all skills extend | `src/skills/SkillBase.ts` |
29
+ | `SkillManager` | Manages the lifecycle of skills attached to an agent | `src/skills/SkillManager.ts` |
30
+ | `SkillRegistry` | Global singleton registry for discovering and instantiating skills by name | `src/skills/SkillRegistry.ts` |
31
+
32
+ **What a skill provides:**
33
+
34
+ - **Tools** -- SWAIG function definitions (name, description, parameters, handler) registered with the agent.
35
+ - **Prompt sections** -- Text injected into the agent's system prompt to instruct the AI on how to use the skill's tools.
36
+ - **Hints** -- Speech recognition hints to improve ASR accuracy for skill-related vocabulary.
37
+ - **Global data** -- Key-value pairs merged into the agent's global data store.
38
+ - **Manifest** -- Metadata describing the skill's name, version, required environment variables, and configuration schema.
39
+
40
+ ---
41
+
42
+ ## Using Skills
43
+
44
+ ### `agent.addSkill()`
45
+
46
+ The primary way to add a skill to an agent. This is an async operation because skills may perform setup work (API connections, config validation, etc.):
47
+
48
+ ```typescript
49
+ import { AgentBase } from 'signalwire-agents';
50
+ import { DateTimeSkill, WebSearchSkill } from 'signalwire-agents/skills/builtin';
51
+
52
+ const agent = new AgentBase({ name: 'my-agent' });
53
+
54
+ // Add skills (async)
55
+ await agent.addSkill(new DateTimeSkill());
56
+ await agent.addSkill(new WebSearchSkill({ max_results: 3 }));
57
+ ```
58
+
59
+ When `addSkill()` is called, the agent:
60
+
61
+ 1. Validates the skill's required environment variables (warns if missing).
62
+ 2. Calls `skill.setup()` for initialization.
63
+ 3. Marks the skill as initialized.
64
+ 4. Registers all tools from `skill.getTools()` with the agent via `defineTool()`.
65
+ 5. Injects all prompt sections from `skill.getPromptSections()` into the agent's prompt.
66
+ 6. Adds all hints from `skill.getHints()`.
67
+ 7. Merges all global data from `skill.getGlobalData()`.
68
+
69
+ ### `agent.removeSkill()`
70
+
71
+ Remove a skill by its unique instance ID. Calls `cleanup()` on the skill before removal:
72
+
73
+ ```typescript
74
+ const removed = await agent.removeSkill('datetime-abc123-def456');
75
+ // returns true if found and removed, false otherwise
76
+ ```
77
+
78
+ ### `agent.listSkills()`
79
+
80
+ List all currently loaded skills with their metadata:
81
+
82
+ ```typescript
83
+ const skills = agent.listSkills();
84
+ // Returns: [{ name: 'datetime', instanceId: 'datetime-abc123-def456', initialized: true }, ...]
85
+
86
+ for (const skill of skills) {
87
+ console.log(`${skill.name} (${skill.instanceId}) - initialized: ${skill.initialized}`);
88
+ }
89
+ ```
90
+
91
+ ### `agent.hasSkill()`
92
+
93
+ Check whether a skill with a given name is currently registered:
94
+
95
+ ```typescript
96
+ if (agent.hasSkill('web_search')) {
97
+ console.log('Web search is available');
98
+ }
99
+ ```
100
+
101
+ ---
102
+
103
+ ## Built-in Skills (18)
104
+
105
+ The SDK ships with 18 built-in skills organized into three tiers based on complexity and dependency requirements.
106
+
107
+ ### Tier 1: Simple (No External Dependencies)
108
+
109
+ These skills work out of the box with no API keys or configuration.
110
+
111
+ | Skill Name | Class | Description | Tools | Required Env Vars | Config Options |
112
+ |---|---|---|---|---|---|
113
+ | `datetime` | `DateTimeSkill` | Current date/time with timezone support | `get_datetime` | None | None |
114
+ | `math` | `MathSkill` | Safe mathematical expression evaluation | `calculate` | None | None |
115
+ | `joke` | `JokeSkill` | Random jokes from built-in collection | `tell_joke` | None | None |
116
+
117
+ **datetime** -- Provides the current date and time in any IANA timezone via the `get_datetime` tool. Uses the `Intl.DateTimeFormat` API.
118
+
119
+ ```typescript
120
+ import { DateTimeSkill } from 'signalwire-agents/skills/builtin';
121
+ await agent.addSkill(new DateTimeSkill());
122
+ ```
123
+
124
+ **math** -- Evaluates mathematical expressions safely using a sandboxed parser. Supports `+`, `-`, `*`, `/`, `^`, `%`, and parentheses. Only allows digits, operators, parentheses, decimal points, and spaces.
125
+
126
+ ```typescript
127
+ import { MathSkill } from 'signalwire-agents/skills/builtin';
128
+ await agent.addSkill(new MathSkill());
129
+ ```
130
+
131
+ **joke** -- Tells random jokes from a curated built-in collection. Categories: `general`, `programming`, `dad`.
132
+
133
+ ```typescript
134
+ import { JokeSkill } from 'signalwire-agents/skills/builtin';
135
+ await agent.addSkill(new JokeSkill());
136
+ ```
137
+
138
+ ---
139
+
140
+ ### Tier 2: Single API (Simple External Integration)
141
+
142
+ These skills integrate with a single external API or provide focused call-control features.
143
+
144
+ | Skill Name | Class | Description | Tools | Required Env Vars | Config Options |
145
+ |---|---|---|---|---|---|
146
+ | `weather_api` | `WeatherApiSkill` | Current weather from OpenWeatherMap | `get_weather` | `WEATHER_API_KEY` | `units` |
147
+ | `play_background_file` | `PlayBackgroundFileSkill` | Background audio playback during calls | `play_background`, `stop_background` | None | `default_file_url`, `allowed_domains` |
148
+ | `swml_transfer` | `SwmlTransferSkill` | Call transfer via SWML actions | `transfer_call`, `list_transfer_destinations` | None | `patterns`, `allow_arbitrary`, `default_message` |
149
+ | `api_ninjas_trivia` | `ApiNinjasTriviaSkill` | Trivia questions from API Ninjas | `get_trivia` | `API_NINJAS_KEY` | `default_category`, `reveal_answer` |
150
+ | `info_gatherer` | `InfoGathererSkill` | Structured data collection from users | `save_info`, `get_gathered_info` | None | `fields`, `purpose`, `confirmation_message`, `store_globally` |
151
+ | `custom_skills` | `CustomSkillsSkill` | User-defined tools from configuration | Dynamic (from config) | None | `tools`, `prompt_title`, `prompt_body` |
152
+
153
+ **weather_api** -- Fetches current weather data from OpenWeatherMap. Supports metric, imperial, and standard units.
154
+
155
+ ```typescript
156
+ import { WeatherApiSkill } from 'signalwire-agents/skills/builtin';
157
+ await agent.addSkill(new WeatherApiSkill({ units: 'imperial' }));
158
+ ```
159
+
160
+ **play_background_file** -- Controls background audio playback during calls (hold music, ambient sounds). Supports domain restrictions for audio file URLs.
161
+
162
+ ```typescript
163
+ import { PlayBackgroundFileSkill } from 'signalwire-agents/skills/builtin';
164
+ await agent.addSkill(new PlayBackgroundFileSkill({
165
+ default_file_url: 'https://example.com/hold-music.mp3',
166
+ allowed_domains: ['example.com', 'cdn.example.com'],
167
+ }));
168
+ ```
169
+
170
+ **swml_transfer** -- Transfers calls using SWML transfer actions. Supports named destination patterns and arbitrary transfers.
171
+
172
+ ```typescript
173
+ import { SwmlTransferSkill } from 'signalwire-agents/skills/builtin';
174
+ await agent.addSkill(new SwmlTransferSkill({
175
+ patterns: [
176
+ { name: 'billing', destination: '+15551234567', description: 'Billing department' },
177
+ { name: 'support', destination: 'sip:support@example.com', description: 'Technical support' },
178
+ ],
179
+ allow_arbitrary: false,
180
+ default_message: 'Please hold while I transfer you.',
181
+ }));
182
+ ```
183
+
184
+ **api_ninjas_trivia** -- Fetches trivia questions with optional category filtering. Categories include `general`, `sciencenature`, `entertainment`, `historyholidays`, `geography`, `music`, `mathematics`, and more.
185
+
186
+ ```typescript
187
+ import { ApiNinjasTriviaSkill } from 'signalwire-agents/skills/builtin';
188
+ await agent.addSkill(new ApiNinjasTriviaSkill({
189
+ default_category: 'sciencenature',
190
+ reveal_answer: false, // AI quizzes the user
191
+ }));
192
+ ```
193
+
194
+ **info_gatherer** -- Collects structured information from users based on configurable field definitions with optional validation patterns. Data is stored per-call and can optionally be persisted to global data.
195
+
196
+ ```typescript
197
+ import { InfoGathererSkill } from 'signalwire-agents/skills/builtin';
198
+ await agent.addSkill(new InfoGathererSkill({
199
+ purpose: 'Collecting customer contact information for our records.',
200
+ fields: [
201
+ { name: 'full_name', description: 'Customer full name', required: true },
202
+ { name: 'email', description: 'Email address', required: true, validation: '^[\\w.+-]+@[\\w-]+\\.[\\w.]+$' },
203
+ { name: 'phone', description: 'Phone number', required: false },
204
+ ],
205
+ confirmation_message: 'Thank you! Your information has been saved.',
206
+ store_globally: true,
207
+ }));
208
+ ```
209
+
210
+ **custom_skills** -- A meta-skill that registers user-defined tools from configuration without writing skill classes. Handler code is compiled via the `Function` constructor at instantiation time.
211
+
212
+ ```typescript
213
+ import { CustomSkillsSkill } from 'signalwire-agents/skills/builtin';
214
+ await agent.addSkill(new CustomSkillsSkill({
215
+ prompt_title: 'Greeting Tools',
216
+ tools: [
217
+ {
218
+ name: 'get_greeting',
219
+ description: 'Get a personalized greeting for a customer.',
220
+ parameters: [
221
+ { name: 'name', type: 'string', description: 'Customer name', required: true },
222
+ ],
223
+ handler_code: 'return new SwaigFunctionResult(`Hello, ${args.name}! Welcome back.`);',
224
+ required: ['name'],
225
+ },
226
+ ],
227
+ }));
228
+ ```
229
+
230
+ ---
231
+
232
+ ### Tier 3: Complex (Multi-API or Advanced)
233
+
234
+ These skills involve complex integrations, multiple API calls, or advanced processing.
235
+
236
+ | Skill Name | Class | Description | Tools | Required Env Vars | Config Options |
237
+ |---|---|---|---|---|---|
238
+ | `web_search` | `WebSearchSkill` | Google Custom Search | `web_search` | `GOOGLE_SEARCH_API_KEY`, `GOOGLE_SEARCH_CX` | `max_results`, `safe_search` |
239
+ | `wikipedia_search` | `WikipediaSearchSkill` | Wikipedia article summaries | `search_wikipedia` | None | None |
240
+ | `google_maps` | `GoogleMapsSkill` | Directions and place search | `get_directions`, `find_place` | `GOOGLE_MAPS_API_KEY` | `default_mode` |
241
+ | `datasphere` | `DataSphereSkill` | SignalWire DataSphere semantic search | `search_datasphere` | `SIGNALWIRE_PROJECT_ID`, `SIGNALWIRE_TOKEN`, `SIGNALWIRE_SPACE` | `max_results`, `distance_threshold` |
242
+ | `datasphere_serverless` | `DataSphereServerlessSkill` | DataSphere via server-side DataMap | `search_datasphere` | `SIGNALWIRE_PROJECT_ID`, `SIGNALWIRE_TOKEN`, `SIGNALWIRE_SPACE` | `max_results`, `distance_threshold`, `document_id` |
243
+ | `native_vector_search` | `NativeVectorSearchSkill` | In-memory TF-IDF document search | `search_documents` | None | `documents` |
244
+ | `spider` | `SpiderSkill` | Web page scraping via Spider API | `scrape_url` | `SPIDER_API_KEY` | `max_content_length` |
245
+ | `claude_skills` | `ClaudeSkill` | Anthropic Claude AI sub-queries | `ask_claude` | `ANTHROPIC_API_KEY` | `model`, `max_tokens` |
246
+ | `mcp_gateway` | `McpGatewaySkill` | MCP protocol gateway (placeholder) | `mcp_invoke` | None | None |
247
+
248
+ **web_search** -- Searches the web using Google Custom Search JSON API. Returns formatted results with titles, links, and snippets.
249
+
250
+ ```typescript
251
+ import { WebSearchSkill } from 'signalwire-agents/skills/builtin';
252
+ await agent.addSkill(new WebSearchSkill({
253
+ max_results: 5,
254
+ safe_search: 'medium', // 'off' | 'medium' | 'high'
255
+ }));
256
+ ```
257
+
258
+ **wikipedia_search** -- Searches Wikipedia for article summaries and extracts. Uses the Wikipedia REST API with no API key required. Falls back to the search API if direct page lookup fails.
259
+
260
+ ```typescript
261
+ import { WikipediaSearchSkill } from 'signalwire-agents/skills/builtin';
262
+ await agent.addSkill(new WikipediaSearchSkill());
263
+ ```
264
+
265
+ **google_maps** -- Provides driving/walking/bicycling/transit directions and place search via Google Maps APIs (Directions API and Places Find Place API).
266
+
267
+ ```typescript
268
+ import { GoogleMapsSkill } from 'signalwire-agents/skills/builtin';
269
+ await agent.addSkill(new GoogleMapsSkill({ default_mode: 'walking' }));
270
+ ```
271
+
272
+ **datasphere** -- Searches SignalWire DataSphere for knowledge base content using semantic search across uploaded documents. Results are ranked by relevance score.
273
+
274
+ ```typescript
275
+ import { DataSphereSkill } from 'signalwire-agents/skills/builtin';
276
+ await agent.addSkill(new DataSphereSkill({
277
+ max_results: 5,
278
+ distance_threshold: 0.7, // 0-1, lower is more similar
279
+ }));
280
+ ```
281
+
282
+ **datasphere_serverless** -- Like `datasphere`, but uses a server-side DataMap instead of a webhook handler. The search executes entirely on the SignalWire platform, making it ideal for serverless or edge deployments where no webhook endpoint is available.
283
+
284
+ ```typescript
285
+ import { DataSphereServerlessSkill } from 'signalwire-agents/skills/builtin';
286
+ await agent.addSkill(new DataSphereServerlessSkill({
287
+ max_results: 5,
288
+ distance_threshold: 0.7,
289
+ document_id: 'specific-doc-id', // optional: restrict to one document
290
+ }));
291
+ ```
292
+
293
+ **native_vector_search** -- In-memory document search using TF-IDF-like word overlap scoring. Documents are provided via configuration and indexed at construction time. No external dependencies or API keys required. Suitable for small to medium document collections.
294
+
295
+ ```typescript
296
+ import { NativeVectorSearchSkill } from 'signalwire-agents/skills/builtin';
297
+ await agent.addSkill(new NativeVectorSearchSkill({
298
+ documents: [
299
+ { id: 'faq-1', text: 'To reset your password, go to Settings > Security > Change Password.' },
300
+ { id: 'faq-2', text: 'Our business hours are Monday-Friday 9am-5pm EST.' },
301
+ { id: 'faq-3', text: 'Refunds are processed within 5-7 business days.', metadata: { category: 'billing' } },
302
+ ],
303
+ }));
304
+ ```
305
+
306
+ **spider** -- Scrapes webpage content using the Spider API. Extracts text, markdown, or HTML from any public URL with optional CSS selector filtering.
307
+
308
+ ```typescript
309
+ import { SpiderSkill } from 'signalwire-agents/skills/builtin';
310
+ await agent.addSkill(new SpiderSkill({
311
+ max_content_length: 5000,
312
+ }));
313
+ ```
314
+
315
+ **claude_skills** -- Provides access to Anthropic's Claude AI for sub-queries, complex reasoning, analysis, or summarization. The agent can delegate tasks to Claude when deeper processing is needed.
316
+
317
+ ```typescript
318
+ import { ClaudeSkill } from 'signalwire-agents/skills/builtin';
319
+ await agent.addSkill(new ClaudeSkill({
320
+ model: 'claude-sonnet-4-5-20250929',
321
+ max_tokens: 1024,
322
+ }));
323
+ ```
324
+
325
+ **mcp_gateway** -- Placeholder skill for future Model Context Protocol (MCP) server integration. Currently provides a non-functional `mcp_invoke` tool. Version 0.1.0 (stub).
326
+
327
+ ```typescript
328
+ import { McpGatewaySkill } from 'signalwire-agents/skills/builtin';
329
+ await agent.addSkill(new McpGatewaySkill());
330
+ ```
331
+
332
+ ---
333
+
334
+ ## Skill Configuration
335
+
336
+ Skills accept configuration via a `SkillConfig` key-value object passed to the constructor:
337
+
338
+ ```typescript
339
+ interface SkillConfig {
340
+ [key: string]: unknown;
341
+ }
342
+ ```
343
+
344
+ ### Accessing Config in Skills
345
+
346
+ Skills use `this.getConfig<T>(key, defaultValue)` to read configuration values with type safety and defaults:
347
+
348
+ ```typescript
349
+ // Inside a skill class
350
+ const maxResults = this.getConfig<number>('max_results', 5);
351
+ const safeSearch = this.getConfig<string>('safe_search', 'medium');
352
+ const patterns = this.getConfig<TransferPattern[]>('patterns', []);
353
+ ```
354
+
355
+ ### Config Schema in Manifests
356
+
357
+ Skills declare their expected configuration in the manifest's `configSchema` field. This is a JSON-schema-like description used for documentation and tooling:
358
+
359
+ ```typescript
360
+ getManifest(): SkillManifest {
361
+ return {
362
+ name: 'my_skill',
363
+ description: 'Does something useful.',
364
+ version: '1.0.0',
365
+ configSchema: {
366
+ max_results: {
367
+ type: 'number',
368
+ description: 'Maximum number of results to return.',
369
+ default: 5,
370
+ },
371
+ safe_search: {
372
+ type: 'string',
373
+ description: 'Safe search level: "off", "medium", or "high".',
374
+ default: 'medium',
375
+ },
376
+ },
377
+ };
378
+ }
379
+ ```
380
+
381
+ ---
382
+
383
+ ## Skill Registry
384
+
385
+ The `SkillRegistry` is a global singleton that maps skill names to factory functions. It enables skill discovery and instantiation by name.
386
+
387
+ ### Getting the Registry
388
+
389
+ ```typescript
390
+ import { SkillRegistry } from 'signalwire-agents/skills';
391
+
392
+ const registry = SkillRegistry.getInstance();
393
+ ```
394
+
395
+ ### Registering Built-in Skills
396
+
397
+ All 18 built-in skills can be registered at once:
398
+
399
+ ```typescript
400
+ import { registerBuiltinSkills } from 'signalwire-agents/skills/builtin';
401
+
402
+ registerBuiltinSkills();
403
+ // Now all built-in skills are available via registry.create('datetime'), etc.
404
+ ```
405
+
406
+ This function skips registration for any skill name already present, so it is safe to call multiple times.
407
+
408
+ ### Manual Registration
409
+
410
+ Register a custom skill factory:
411
+
412
+ ```typescript
413
+ registry.register('my_custom_skill', (config) => new MyCustomSkill(config));
414
+ ```
415
+
416
+ Register with a manifest for introspection:
417
+
418
+ ```typescript
419
+ registry.register('my_custom_skill', (config) => new MyCustomSkill(config), {
420
+ name: 'my_custom_skill',
421
+ description: 'Does custom things.',
422
+ version: '1.0.0',
423
+ });
424
+ ```
425
+
426
+ ### Creating Instances by Name
427
+
428
+ ```typescript
429
+ const skill = registry.create('datetime');
430
+ if (skill) {
431
+ await agent.addSkill(skill);
432
+ }
433
+
434
+ // With config
435
+ const weatherSkill = registry.create('weather_api', { units: 'imperial' });
436
+ ```
437
+
438
+ Returns `null` if the skill name is not registered.
439
+
440
+ ### Querying the Registry
441
+
442
+ ```typescript
443
+ // Check if a skill is registered
444
+ registry.has('datetime'); // true
445
+
446
+ // List all registered skill names
447
+ const names = registry.listRegistered();
448
+ // ['datetime', 'math', 'joke', ...]
449
+
450
+ // List with manifests
451
+ const withManifests = registry.listRegisteredWithManifests();
452
+ // [{ name: 'datetime', manifest: { ... } }, ...]
453
+
454
+ // Get a specific manifest
455
+ const manifest = registry.getManifest('web_search');
456
+
457
+ // Count of registered skills
458
+ const count = registry.size;
459
+ ```
460
+
461
+ ### Skill Discovery from Directories
462
+
463
+ The registry can auto-discover skills from filesystem directories:
464
+
465
+ ```typescript
466
+ // Add a search path
467
+ registry.addSearchPath('/path/to/my/skills');
468
+
469
+ // Discover and register skills from a specific directory
470
+ const discovered = await registry.discoverFromDirectory('/path/to/my/skills');
471
+ // Returns array of newly discovered skill names
472
+
473
+ // Discover from all configured search paths
474
+ const allDiscovered = await registry.discoverAll();
475
+ ```
476
+
477
+ Discovery looks for modules that export either:
478
+ - A `createSkill` factory function, or
479
+ - A default export that is a `SkillBase` subclass
480
+
481
+ The `SIGNALWIRE_SKILL_PATHS` environment variable can set additional search paths (colon-separated):
482
+
483
+ ```bash
484
+ export SIGNALWIRE_SKILL_PATHS="/app/skills:/shared/skills"
485
+ ```
486
+
487
+ ### Cleanup
488
+
489
+ ```typescript
490
+ // Unregister a single skill
491
+ registry.unregister('my_skill');
492
+
493
+ // Clear all registrations
494
+ registry.clear();
495
+
496
+ // Reset the singleton (for testing)
497
+ SkillRegistry.resetInstance();
498
+ ```
499
+
500
+ ---
501
+
502
+ ## Creating Custom Skills
503
+
504
+ To create a custom skill, extend `SkillBase` and implement the required abstract methods.
505
+
506
+ ### Minimal Custom Skill
507
+
508
+ ```typescript
509
+ import { SkillBase, SkillManifest, SkillToolDefinition, SkillConfig } from 'signalwire-agents/skills';
510
+ import { SwaigFunctionResult } from 'signalwire-agents';
511
+
512
+ export class GreetingSkill extends SkillBase {
513
+ constructor(config?: SkillConfig) {
514
+ super('greeting', config);
515
+ }
516
+
517
+ getManifest(): SkillManifest {
518
+ return {
519
+ name: 'greeting',
520
+ description: 'Provides personalized greetings.',
521
+ version: '1.0.0',
522
+ author: 'My Team',
523
+ tags: ['utility', 'greeting'],
524
+ };
525
+ }
526
+
527
+ getTools(): SkillToolDefinition[] {
528
+ const style = this.getConfig<string>('style', 'formal');
529
+
530
+ return [
531
+ {
532
+ name: 'greet_customer',
533
+ description: 'Generate a personalized greeting for a customer.',
534
+ parameters: {
535
+ name: {
536
+ type: 'string',
537
+ description: 'The customer name.',
538
+ },
539
+ },
540
+ required: ['name'],
541
+ handler: (args: Record<string, unknown>) => {
542
+ const name = args.name as string;
543
+ if (style === 'casual') {
544
+ return new SwaigFunctionResult(`Hey ${name}! What's up?`);
545
+ }
546
+ return new SwaigFunctionResult(`Good day, ${name}. How may I assist you?`);
547
+ },
548
+ },
549
+ ];
550
+ }
551
+ }
552
+
553
+ // Factory function (required for registry discovery)
554
+ export function createSkill(config?: SkillConfig): GreetingSkill {
555
+ return new GreetingSkill(config);
556
+ }
557
+ ```
558
+
559
+ ### Full-Featured Custom Skill
560
+
561
+ ```typescript
562
+ import {
563
+ SkillBase,
564
+ SkillManifest,
565
+ SkillToolDefinition,
566
+ SkillPromptSection,
567
+ SkillConfig,
568
+ } from 'signalwire-agents/skills';
569
+ import { SwaigFunctionResult } from 'signalwire-agents';
570
+
571
+ export class InventorySkill extends SkillBase {
572
+ private db: Map<string, number> = new Map();
573
+
574
+ constructor(config?: SkillConfig) {
575
+ super('inventory', config);
576
+ }
577
+
578
+ getManifest(): SkillManifest {
579
+ return {
580
+ name: 'inventory',
581
+ description: 'Checks product inventory levels.',
582
+ version: '1.0.0',
583
+ author: 'Acme Corp',
584
+ tags: ['inventory', 'products', 'e-commerce'],
585
+ requiredEnvVars: ['INVENTORY_DB_URL'],
586
+ configSchema: {
587
+ low_stock_threshold: {
588
+ type: 'number',
589
+ description: 'Quantity below which a product is considered low stock.',
590
+ default: 10,
591
+ },
592
+ },
593
+ };
594
+ }
595
+
596
+ // Initialization -- called when the skill is added to an agent
597
+ async setup(): Promise<void> {
598
+ const dbUrl = process.env['INVENTORY_DB_URL'];
599
+ // ... connect to database, load initial data ...
600
+ this.db.set('widget-a', 150);
601
+ this.db.set('widget-b', 3);
602
+ }
603
+
604
+ // Cleanup -- called when the skill is removed
605
+ async cleanup(): Promise<void> {
606
+ // ... close database connection ...
607
+ this.db.clear();
608
+ }
609
+
610
+ getTools(): SkillToolDefinition[] {
611
+ const threshold = this.getConfig<number>('low_stock_threshold', 10);
612
+
613
+ return [
614
+ {
615
+ name: 'check_inventory',
616
+ description: 'Check the current inventory level for a product.',
617
+ parameters: {
618
+ product_id: {
619
+ type: 'string',
620
+ description: 'The product ID to check.',
621
+ },
622
+ },
623
+ required: ['product_id'],
624
+ secure: true, // requires authenticated invocation
625
+ fillers: {
626
+ 'en-US': ['Let me check our stock levels...', 'One moment...'],
627
+ },
628
+ handler: (args: Record<string, unknown>) => {
629
+ const productId = args.product_id as string;
630
+ const quantity = this.db.get(productId);
631
+
632
+ if (quantity === undefined) {
633
+ return new SwaigFunctionResult(`Product "${productId}" not found in inventory.`);
634
+ }
635
+
636
+ const status = quantity <= threshold ? 'LOW STOCK' : 'In Stock';
637
+ return new SwaigFunctionResult(
638
+ `Product ${productId}: ${quantity} units available (${status}).`
639
+ );
640
+ },
641
+ },
642
+ ];
643
+ }
644
+
645
+ getPromptSections(): SkillPromptSection[] {
646
+ return [
647
+ {
648
+ title: 'Inventory Lookup',
649
+ body: 'You can check product inventory levels for customers.',
650
+ bullets: [
651
+ 'Use the check_inventory tool when a customer asks about product availability.',
652
+ 'If a product is low stock, proactively let the customer know.',
653
+ 'Suggest alternatives if a product is out of stock.',
654
+ ],
655
+ },
656
+ ];
657
+ }
658
+
659
+ getHints(): string[] {
660
+ return ['inventory', 'stock', 'availability', 'in stock', 'out of stock'];
661
+ }
662
+
663
+ getGlobalData(): Record<string, unknown> {
664
+ return {
665
+ inventory_skill_version: '1.0.0',
666
+ low_stock_threshold: this.getConfig<number>('low_stock_threshold', 10),
667
+ };
668
+ }
669
+ }
670
+
671
+ export function createSkill(config?: SkillConfig): InventorySkill {
672
+ return new InventorySkill(config);
673
+ }
674
+ ```
675
+
676
+ ### SkillBase Methods Reference
677
+
678
+ | Method | Required | Description |
679
+ |---|---|---|
680
+ | `getManifest()` | Yes (abstract) | Returns the skill manifest with name, description, version, env vars, and config schema |
681
+ | `getTools()` | Yes (abstract) | Returns array of SWAIG tool definitions to register with the agent |
682
+ | `setup()` | No | Async initialization hook called when the skill is added to an agent |
683
+ | `cleanup()` | No | Async teardown hook called when the skill is removed |
684
+ | `getPromptSections()` | No | Returns prompt sections injected into the agent's system prompt |
685
+ | `getHints()` | No | Returns speech recognition hint strings |
686
+ | `getGlobalData()` | No | Returns key-value data merged into the agent's global data store |
687
+ | `validateEnvVars()` | No | Checks required env vars from manifest; returns array of missing names |
688
+ | `getConfig(key, default)` | No | Reads a configuration value with type-safe default |
689
+ | `isInitialized()` | No | Returns whether `setup()` has completed |
690
+
691
+ ### SkillToolDefinition Fields
692
+
693
+ | Field | Type | Required | Description |
694
+ |---|---|---|---|
695
+ | `name` | `string` | Yes | Unique tool name for SWAIG function registration |
696
+ | `description` | `string` | Yes | Human-readable description shown to the AI |
697
+ | `parameters` | `Record<string, unknown>` | No | Parameter schema keyed by parameter name |
698
+ | `handler` | `SwaigHandler` | Yes | Function invoked when the tool is called |
699
+ | `secure` | `boolean` | No | Whether the tool requires authenticated invocation |
700
+ | `fillers` | `Record<string, string[]>` | No | Filler phrases spoken while the tool executes, keyed by language |
701
+ | `required` | `string[]` | No | List of required parameter names |
702
+
703
+ ### SkillPromptSection Fields
704
+
705
+ | Field | Type | Required | Description |
706
+ |---|---|---|---|
707
+ | `title` | `string` | Yes | Section heading displayed in the prompt |
708
+ | `body` | `string` | No | Body text for the section |
709
+ | `bullets` | `string[]` | No | Bullet points appended after the body |
710
+ | `numbered` | `boolean` | No | If true, render bullets as a numbered list |
711
+
712
+ ---
713
+
714
+ ## Skill Lifecycle
715
+
716
+ The lifecycle of a skill follows these phases:
717
+
718
+ ```
719
+ Construction -> setup() -> register tools/prompts/hints -> active use -> cleanup()
720
+ ```
721
+
722
+ ### 1. Construction
723
+
724
+ The skill is instantiated with a name and optional config. The `instanceId` is generated automatically (combination of skill name, timestamp, and random bytes):
725
+
726
+ ```typescript
727
+ const skill = new DateTimeSkill({ /* config */ });
728
+ // skill.skillName === 'datetime'
729
+ // skill.instanceId === 'datetime-m2abc123-deadbeef'
730
+ // skill.isInitialized() === false
731
+ ```
732
+
733
+ ### 2. Setup
734
+
735
+ When `agent.addSkill(skill)` is called, the `SkillManager`:
736
+
737
+ 1. Checks for duplicate instance IDs.
738
+ 2. Calls `skill.validateEnvVars()` and logs warnings for any missing environment variables.
739
+ 3. Calls `await skill.setup()` for async initialization.
740
+ 4. Calls `skill.markInitialized()`.
741
+
742
+ ```typescript
743
+ // Inside a custom skill
744
+ async setup(): Promise<void> {
745
+ // Connect to databases, validate API keys, pre-load data, etc.
746
+ const apiKey = process.env['MY_API_KEY'];
747
+ if (!apiKey) throw new Error('API key is required');
748
+ this.client = await connectToService(apiKey);
749
+ }
750
+ ```
751
+
752
+ ### 3. Registration
753
+
754
+ After setup, the agent registers the skill's contributions:
755
+
756
+ - **Tools** from `getTools()` are registered via `agent.defineTool()`.
757
+ - **Prompt sections** from `getPromptSections()` are injected via `agent.promptAddSection()`.
758
+ - **Hints** from `getHints()` are added via `agent.addHints()`.
759
+ - **Global data** from `getGlobalData()` is merged via `agent.updateGlobalData()`.
760
+
761
+ ### 4. Active Use
762
+
763
+ The skill is now active. Its tools are called by the AI during conversations. The skill instance remains in memory for the lifetime of the agent.
764
+
765
+ ### 5. Cleanup
766
+
767
+ When `agent.removeSkill(instanceId)` is called (or the SkillManager is cleared), the skill's `cleanup()` method is invoked:
768
+
769
+ ```typescript
770
+ async cleanup(): Promise<void> {
771
+ // Close connections, release resources, flush caches
772
+ await this.client.disconnect();
773
+ }
774
+ ```
775
+
776
+ The `SkillManager.clear()` method calls `cleanup()` on all loaded skills and removes them all.
777
+
778
+ ---
779
+
780
+ ## Environment Validation
781
+
782
+ Skills can declare required environment variables in their manifest. The `validateEnvVars()` method checks which ones are missing.
783
+
784
+ ### In the Manifest
785
+
786
+ ```typescript
787
+ getManifest(): SkillManifest {
788
+ return {
789
+ name: 'my_api_skill',
790
+ description: 'Integrates with My API.',
791
+ version: '1.0.0',
792
+ requiredEnvVars: ['MY_API_KEY', 'MY_API_SECRET'],
793
+ };
794
+ }
795
+ ```
796
+
797
+ ### Automatic Validation
798
+
799
+ When `addSkill()` is called, the `SkillManager` automatically validates env vars and logs warnings for any that are missing. The skill is still added even if env vars are missing -- it is up to the skill's handler to return appropriate error messages at call time.
800
+
801
+ ### Manual Validation
802
+
803
+ ```typescript
804
+ const skill = new WebSearchSkill();
805
+ const missing = skill.validateEnvVars();
806
+
807
+ if (missing.length > 0) {
808
+ console.warn(`Missing env vars: ${missing.join(', ')}`);
809
+ }
810
+ // missing might be ['GOOGLE_SEARCH_API_KEY', 'GOOGLE_SEARCH_CX']
811
+ ```
812
+
813
+ ### Runtime Handling
814
+
815
+ Built-in skills check for environment variables at call time and return user-friendly error messages:
816
+
817
+ ```typescript
818
+ // Inside a tool handler
819
+ const apiKey = process.env['GOOGLE_SEARCH_API_KEY'];
820
+ if (!apiKey) {
821
+ return new SwaigFunctionResult(
822
+ 'Web search is not configured. The GOOGLE_SEARCH_API_KEY environment variable is required.'
823
+ );
824
+ }
825
+ ```
826
+
827
+ This two-layer approach (warn at registration, error at call time) allows agents to start up even when some skills are not fully configured, while providing clear feedback when an unconfigured tool is invoked.
828
+
829
+ ---
830
+
831
+ ## Complete Environment Variables Reference
832
+
833
+ | Env Var | Used By |
834
+ |---|---|
835
+ | `WEATHER_API_KEY` | `weather_api` |
836
+ | `API_NINJAS_KEY` | `api_ninjas_trivia` |
837
+ | `GOOGLE_SEARCH_API_KEY` | `web_search` |
838
+ | `GOOGLE_SEARCH_CX` | `web_search` |
839
+ | `GOOGLE_MAPS_API_KEY` | `google_maps` |
840
+ | `SIGNALWIRE_PROJECT_ID` | `datasphere`, `datasphere_serverless` |
841
+ | `SIGNALWIRE_TOKEN` | `datasphere`, `datasphere_serverless` |
842
+ | `SIGNALWIRE_SPACE` | `datasphere`, `datasphere_serverless` |
843
+ | `SPIDER_API_KEY` | `spider` |
844
+ | `ANTHROPIC_API_KEY` | `claude_skills` |
845
+ | `SIGNALWIRE_SKILL_PATHS` | `SkillRegistry` (directory discovery) |