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,1141 @@
1
+ # DataMap Guide
2
+
3
+ Comprehensive guide to the `DataMap` class in the SignalWire AI Agents TypeScript SDK.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Overview](#overview)
10
+ - [Creating a DataMap](#creating-a-datamap)
11
+ - [Configuration](#configuration)
12
+ - [purpose / description](#purpose--description)
13
+ - [parameter](#parameter)
14
+ - [Webhooks](#webhooks)
15
+ - [webhook](#webhook)
16
+ - [body](#body)
17
+ - [params](#params)
18
+ - [Webhook Headers](#webhook-headers)
19
+ - [Expressions](#expressions)
20
+ - [expression](#expression)
21
+ - [Response Processing](#response-processing)
22
+ - [output](#output)
23
+ - [webhookExpressions](#webhookexpressions)
24
+ - [Error Handling](#error-handling)
25
+ - [fallbackOutput](#fallbackoutput)
26
+ - [errorKeys](#errorkeys)
27
+ - [globalErrorKeys](#globalerrorkeys)
28
+ - [Iteration](#iteration)
29
+ - [foreach](#foreach)
30
+ - [Environment Variables](#environment-variables)
31
+ - [enableEnvExpansion](#enableenvexpansion)
32
+ - [Registration](#registration)
33
+ - [registerWithAgent](#registerwithagent)
34
+ - [toSwaigFunction](#toswaigfunction)
35
+ - [Helper Functions](#helper-functions)
36
+ - [createSimpleApiTool](#createsimpleapitool)
37
+ - [createExpressionTool](#createexpressiontool)
38
+ - [Template Variables Reference](#template-variables-reference)
39
+ - [Complete Examples](#complete-examples)
40
+
41
+ ---
42
+
43
+ ## Overview
44
+
45
+ `DataMap` creates server-side tool definitions that execute entirely on the SignalWire platform without requiring your own webhook endpoints for the tool handler. Instead of writing a handler function that runs on your server, you describe what HTTP call to make (or what expression to evaluate), how to process the response, and what to return to the AI -- all as configuration.
46
+
47
+ ### When to use DataMap vs. defineTool
48
+
49
+ | Feature | `defineTool()` | `DataMap` |
50
+ |--------------------|--------------------------------------------|-------------------------------------------------|
51
+ | Execution location | Your server | SignalWire platform |
52
+ | Custom logic | Full TypeScript/JavaScript | Template variables and pattern matching |
53
+ | External API calls | You make them in your handler | SignalWire makes them for you |
54
+ | Webhook required | Yes (your agent server must be reachable) | No (the data_map config is embedded in SWML) |
55
+ | Best for | Complex logic, database access, auth flows | Simple API lookups, pattern matching, transforms |
56
+
57
+ ### How it works
58
+
59
+ 1. You define a `DataMap` with a name, parameters, and either webhooks or expressions.
60
+ 2. You register it with an agent via `registerWithAgent()` or `toSwaigFunction()`.
61
+ 3. When the AI decides to call this tool, SignalWire executes the data_map configuration directly:
62
+ - For **webhooks**: SignalWire makes the HTTP request, processes the response through your output template, and returns the result to the AI.
63
+ - For **expressions**: SignalWire evaluates the test value against the regex pattern and returns the matching output.
64
+
65
+ ### Architecture
66
+
67
+ ```
68
+ Caller <-> SignalWire AI <-> DataMap (runs on SignalWire)
69
+ |
70
+ +--> External API (optional webhook call)
71
+ ```
72
+
73
+ No traffic flows to your server for DataMap tool invocations. Your server only serves the initial SWML document that contains the data_map configuration.
74
+
75
+ ---
76
+
77
+ ## Creating a DataMap
78
+
79
+ The `DataMap` constructor takes a single argument: the function name.
80
+
81
+ ```typescript
82
+ import { DataMap, SwaigFunctionResult } from '@anthropic/signalwire-agents';
83
+
84
+ const tool = new DataMap('get_weather');
85
+ ```
86
+
87
+ | Parameter | Type | Description |
88
+ |----------------|----------|------------------------------------------|
89
+ | `functionName` | `string` | Unique name for this data map tool. |
90
+
91
+ All subsequent configuration is done via fluent method chaining:
92
+
93
+ ```typescript
94
+ const tool = new DataMap('get_weather')
95
+ .purpose('Get current weather for a city')
96
+ .parameter('city', 'string', 'The city name', { required: true })
97
+ .webhook('GET', 'https://wttr.in/${lc:args.city}?format=j1')
98
+ .output(new SwaigFunctionResult('Temperature: ${response.temp_F}F'))
99
+ .fallbackOutput(new SwaigFunctionResult('Weather data unavailable.'));
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Configuration
105
+
106
+ ### purpose / description
107
+
108
+ Set the tool description that the AI sees. The AI uses this description to decide when to call the tool. `description()` is an alias for `purpose()`.
109
+
110
+ ```typescript
111
+ purpose(description: string): this
112
+ description(description: string): this
113
+ ```
114
+
115
+ | Parameter | Type | Description |
116
+ |---------------|----------|----------------------------------------------------|
117
+ | `description` | `string` | Human-readable description of what the tool does. |
118
+
119
+ If not set, the description defaults to `"Execute <functionName>"`.
120
+
121
+ ```typescript
122
+ const tool = new DataMap('lookup_order')
123
+ .purpose('Look up an order by its ID and return the status');
124
+
125
+ // Equivalent:
126
+ const tool2 = new DataMap('lookup_order')
127
+ .description('Look up an order by its ID and return the status');
128
+ ```
129
+
130
+ ---
131
+
132
+ ### parameter
133
+
134
+ Define a parameter that the AI should extract from the conversation and pass to this tool.
135
+
136
+ ```typescript
137
+ parameter(
138
+ name: string,
139
+ paramType: string,
140
+ description: string,
141
+ opts?: { required?: boolean; enum?: string[] }
142
+ ): this
143
+ ```
144
+
145
+ | Parameter | Type | Description |
146
+ |-------------------|------------|----------------------------------------------------------|
147
+ | `name` | `string` | Parameter name (used in `${args.name}` templates). |
148
+ | `paramType` | `string` | JSON Schema type: `"string"`, `"number"`, `"boolean"`, `"integer"`, `"array"`, `"object"`. |
149
+ | `description` | `string` | Description shown to the AI to explain what this parameter is. |
150
+ | `opts.required` | `boolean` | If `true`, the AI must provide this parameter. |
151
+ | `opts.enum` | `string[]` | Restrict the parameter to a fixed set of allowed values. |
152
+
153
+ Multiple calls to `parameter()` define multiple parameters. Required parameters are tracked internally via a `_required` array in the parameter schema.
154
+
155
+ ```typescript
156
+ const tool = new DataMap('search_products')
157
+ .purpose('Search for products in the catalog')
158
+ .parameter('query', 'string', 'Search query text', { required: true })
159
+ .parameter('category', 'string', 'Product category filter', {
160
+ enum: ['electronics', 'clothing', 'home', 'sports'],
161
+ })
162
+ .parameter('max_results', 'integer', 'Maximum number of results to return');
163
+ ```
164
+
165
+ The generated parameter schema:
166
+
167
+ ```json
168
+ {
169
+ "type": "object",
170
+ "properties": {
171
+ "query": { "type": "string", "description": "Search query text" },
172
+ "category": {
173
+ "type": "string",
174
+ "description": "Product category filter",
175
+ "enum": ["electronics", "clothing", "home", "sports"]
176
+ },
177
+ "max_results": { "type": "integer", "description": "Maximum number of results to return" }
178
+ },
179
+ "required": ["query"]
180
+ }
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Webhooks
186
+
187
+ ### webhook
188
+
189
+ Add a webhook that SignalWire calls when the tool is invoked. You can add multiple webhooks to a single DataMap.
190
+
191
+ ```typescript
192
+ webhook(
193
+ method: string,
194
+ url: string,
195
+ opts?: {
196
+ headers?: Record<string, string>;
197
+ formParam?: string;
198
+ inputArgsAsParams?: boolean;
199
+ requireArgs?: string[];
200
+ }
201
+ ): this
202
+ ```
203
+
204
+ | Parameter | Type | Description |
205
+ |------------------------|--------------------------|------------------------------------------------------------|
206
+ | `method` | `string` | HTTP method (`"GET"`, `"POST"`, `"PUT"`, etc.). Automatically uppercased. |
207
+ | `url` | `string` | The webhook URL. Supports template variables like `${args.city}`. |
208
+ | `opts.headers` | `Record<string, string>` | Custom HTTP headers for the request. |
209
+ | `opts.formParam` | `string` | Name of the form parameter to send the body as. |
210
+ | `opts.inputArgsAsParams` | `boolean` | If `true`, pass all input arguments as query/form parameters. |
211
+ | `opts.requireArgs` | `string[]` | Arguments that must be present for this webhook to fire. |
212
+
213
+ **Returns:** `this` for chaining.
214
+
215
+ ```typescript
216
+ // Simple GET request with URL template
217
+ const tool = new DataMap('get_stock_price')
218
+ .purpose('Get the current stock price')
219
+ .parameter('symbol', 'string', 'Stock ticker symbol', { required: true })
220
+ .webhook('GET', 'https://api.stocks.example.com/v1/price/${uc:args.symbol}');
221
+
222
+ // POST with authentication header
223
+ const tool2 = new DataMap('create_ticket')
224
+ .purpose('Create a support ticket')
225
+ .parameter('subject', 'string', 'Ticket subject', { required: true })
226
+ .parameter('description', 'string', 'Ticket description', { required: true })
227
+ .webhook('POST', 'https://api.helpdesk.example.com/tickets', {
228
+ headers: {
229
+ 'Authorization': 'Bearer ${ENV.HELPDESK_API_KEY}',
230
+ 'Content-Type': 'application/json',
231
+ },
232
+ });
233
+ ```
234
+
235
+ ---
236
+
237
+ ### body
238
+
239
+ Set the JSON body for the most recently added webhook. Must be called after `webhook()`.
240
+
241
+ ```typescript
242
+ body(data: Record<string, unknown>): this
243
+ ```
244
+
245
+ | Parameter | Type | Description |
246
+ |-----------|----------------------------|----------------------------|
247
+ | `data` | `Record<string, unknown>` | The request body object. |
248
+
249
+ **Throws:** `Error` if no webhook has been added yet.
250
+
251
+ **Returns:** `this` for chaining.
252
+
253
+ ```typescript
254
+ const tool = new DataMap('create_ticket')
255
+ .purpose('Create a support ticket')
256
+ .parameter('subject', 'string', 'Ticket subject', { required: true })
257
+ .parameter('priority', 'string', 'Priority level', { enum: ['low', 'medium', 'high'] })
258
+ .webhook('POST', 'https://api.helpdesk.example.com/tickets', {
259
+ headers: { 'Authorization': 'Bearer my-api-key' },
260
+ })
261
+ .body({
262
+ subject: '${args.subject}',
263
+ priority: '${args.priority}',
264
+ source: 'phone-call',
265
+ });
266
+ ```
267
+
268
+ ---
269
+
270
+ ### params
271
+
272
+ Set query or form parameters for the most recently added webhook. Must be called after `webhook()`.
273
+
274
+ ```typescript
275
+ params(data: Record<string, unknown>): this
276
+ ```
277
+
278
+ | Parameter | Type | Description |
279
+ |-----------|----------------------------|--------------------------|
280
+ | `data` | `Record<string, unknown>` | The parameters object. |
281
+
282
+ **Throws:** `Error` if no webhook has been added yet.
283
+
284
+ **Returns:** `this` for chaining.
285
+
286
+ ```typescript
287
+ const tool = new DataMap('search_kb')
288
+ .purpose('Search the knowledge base')
289
+ .parameter('query', 'string', 'Search query', { required: true })
290
+ .webhook('GET', 'https://api.kb.example.com/search')
291
+ .params({
292
+ q: '${args.query}',
293
+ limit: 5,
294
+ format: 'json',
295
+ });
296
+ ```
297
+
298
+ ---
299
+
300
+ ### Webhook Headers
301
+
302
+ Headers are set via the `opts.headers` parameter of `webhook()`. Template variables are supported in header values.
303
+
304
+ ```typescript
305
+ const tool = new DataMap('authenticated_lookup')
306
+ .purpose('Look up data from an authenticated API')
307
+ .parameter('id', 'string', 'Record ID', { required: true })
308
+ .enableEnvExpansion()
309
+ .webhook('GET', 'https://api.example.com/records/${args.id}', {
310
+ headers: {
311
+ 'Authorization': 'Bearer ${ENV.API_TOKEN}',
312
+ 'X-Request-Source': 'signalwire-agent',
313
+ },
314
+ });
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Expressions
320
+
321
+ ### expression
322
+
323
+ Add a pattern-matching expression that evaluates a test value against a regex pattern. Expressions work without making any HTTP calls -- they are evaluated entirely on the SignalWire platform.
324
+
325
+ ```typescript
326
+ expression(
327
+ testValue: string,
328
+ pattern: string | RegExp,
329
+ output: SwaigFunctionResult,
330
+ nomatchOutput?: SwaigFunctionResult
331
+ ): this
332
+ ```
333
+
334
+ | Parameter | Type | Description |
335
+ |-----------------|----------------------------|----------------------------------------------------------|
336
+ | `testValue` | `string` | The string or template variable to test (e.g., `"${args.input}"`). |
337
+ | `pattern` | `string \| RegExp` | Regex pattern to match against. If a `RegExp`, the `.source` is used. |
338
+ | `output` | `SwaigFunctionResult` | Result returned when the pattern matches. |
339
+ | `nomatchOutput` | `SwaigFunctionResult` | Optional result returned when the pattern does not match.|
340
+
341
+ **Returns:** `this` for chaining.
342
+
343
+ You can add multiple expressions. They are evaluated in order; the first match wins.
344
+
345
+ ```typescript
346
+ const tool = new DataMap('validate_email')
347
+ .purpose('Validate an email address format')
348
+ .parameter('email', 'string', 'The email address to validate', { required: true })
349
+ .expression(
350
+ '${args.email}',
351
+ /^[^@]+@[^@]+\.[^@]+$/,
352
+ new SwaigFunctionResult('The email address ${args.email} is valid.'),
353
+ new SwaigFunctionResult('The email address ${args.email} is not valid. Please ask for a correct email.'),
354
+ );
355
+ ```
356
+
357
+ Multiple expressions for different test values:
358
+
359
+ ```typescript
360
+ const tool = new DataMap('classify_input')
361
+ .purpose('Classify user input as a question or command')
362
+ .parameter('input', 'string', 'The user input to classify', { required: true })
363
+ .expression(
364
+ '${args.input}',
365
+ '^(what|how|why|when|where|who|is|are|can|do|does)',
366
+ new SwaigFunctionResult('The input is a question.'),
367
+ )
368
+ .expression(
369
+ '${args.input}',
370
+ '^(please|set|change|update|delete|create|send)',
371
+ new SwaigFunctionResult('The input is a command.'),
372
+ new SwaigFunctionResult('The input type could not be determined.'),
373
+ );
374
+ ```
375
+
376
+ ---
377
+
378
+ ## Response Processing
379
+
380
+ ### output
381
+
382
+ Set the output template for the most recently added webhook. The template uses `${response.*}` variables to reference fields in the webhook's JSON response and `${args.*}` to reference the tool's input arguments.
383
+
384
+ ```typescript
385
+ output(result: SwaigFunctionResult): this
386
+ ```
387
+
388
+ | Parameter | Type | Description |
389
+ |-----------|-----------------------|------------------------------------------------------|
390
+ | `result` | `SwaigFunctionResult` | The result template. Use template variables in the response text. |
391
+
392
+ **Throws:** `Error` if no webhook has been added yet.
393
+
394
+ **Returns:** `this` for chaining.
395
+
396
+ ```typescript
397
+ const tool = new DataMap('get_weather')
398
+ .purpose('Get current weather')
399
+ .parameter('city', 'string', 'City name', { required: true })
400
+ .webhook('GET', 'https://wttr.in/${lc:args.city}?format=j1')
401
+ .output(
402
+ new SwaigFunctionResult(
403
+ 'Weather in ${args.city}: ' +
404
+ 'Temperature: ${response.current_condition[0].temp_F}F, ' +
405
+ 'Conditions: ${response.current_condition[0].weatherDesc[0].value}, ' +
406
+ 'Humidity: ${response.current_condition[0].humidity}%'
407
+ ),
408
+ );
409
+ ```
410
+
411
+ The `output()` method calls `toDict()` on the `SwaigFunctionResult`, so you can also include actions:
412
+
413
+ ```typescript
414
+ const tool = new DataMap('urgent_alert')
415
+ .purpose('Send an urgent alert')
416
+ .parameter('message', 'string', 'Alert message', { required: true })
417
+ .webhook('POST', 'https://api.alerts.example.com/send')
418
+ .body({ message: '${args.message}' })
419
+ .output(
420
+ new SwaigFunctionResult('Alert sent: ${response.alert_id}')
421
+ .setMetadata({ alert_id: '${response.alert_id}' }),
422
+ );
423
+ ```
424
+
425
+ ---
426
+
427
+ ### webhookExpressions
428
+
429
+ Set pattern-matching expressions on the most recently added webhook. These expressions are evaluated against the webhook's response, allowing conditional output based on the response content.
430
+
431
+ ```typescript
432
+ webhookExpressions(expressions: Record<string, unknown>[]): this
433
+ ```
434
+
435
+ | Parameter | Type | Description |
436
+ |---------------|-----------------------------|----------------------------------------------------------------|
437
+ | `expressions` | `Record<string, unknown>[]` | Array of expression objects with `string`, `pattern`, and `output` fields. |
438
+
439
+ **Throws:** `Error` if no webhook has been added yet.
440
+
441
+ **Returns:** `this` for chaining.
442
+
443
+ ```typescript
444
+ const tool = new DataMap('check_order_status')
445
+ .purpose('Check the status of an order')
446
+ .parameter('order_id', 'string', 'Order ID', { required: true })
447
+ .webhook('GET', 'https://api.store.example.com/orders/${args.order_id}')
448
+ .webhookExpressions([
449
+ {
450
+ string: '${response.status}',
451
+ pattern: 'shipped',
452
+ output: new SwaigFunctionResult(
453
+ 'Order ${args.order_id} has shipped! Tracking: ${response.tracking_number}'
454
+ ).toDict(),
455
+ },
456
+ {
457
+ string: '${response.status}',
458
+ pattern: 'processing',
459
+ output: new SwaigFunctionResult(
460
+ 'Order ${args.order_id} is being processed. Estimated ship date: ${response.est_ship_date}'
461
+ ).toDict(),
462
+ },
463
+ {
464
+ string: '${response.status}',
465
+ pattern: 'delivered',
466
+ output: new SwaigFunctionResult(
467
+ 'Order ${args.order_id} was delivered on ${response.delivery_date}.'
468
+ ).toDict(),
469
+ },
470
+ ])
471
+ .output(
472
+ new SwaigFunctionResult('Order ${args.order_id} status: ${response.status}'),
473
+ );
474
+ ```
475
+
476
+ ---
477
+
478
+ ## Error Handling
479
+
480
+ ### fallbackOutput
481
+
482
+ Set a fallback output used when no webhook succeeds or no expression matches. This ensures the AI always gets a response even when the tool encounters an error.
483
+
484
+ ```typescript
485
+ fallbackOutput(result: SwaigFunctionResult): this
486
+ ```
487
+
488
+ | Parameter | Type | Description |
489
+ |-----------|-----------------------|-----------------------------------------------|
490
+ | `result` | `SwaigFunctionResult` | The fallback result. |
491
+
492
+ **Returns:** `this` for chaining.
493
+
494
+ ```typescript
495
+ const tool = new DataMap('get_price')
496
+ .purpose('Get the price of a product')
497
+ .parameter('product', 'string', 'Product name', { required: true })
498
+ .webhook('GET', 'https://api.store.example.com/price/${args.product}')
499
+ .output(new SwaigFunctionResult('${args.product} costs $${response.price}'))
500
+ .fallbackOutput(
501
+ new SwaigFunctionResult('Sorry, I could not look up the price for ${args.product}. Please try again later.'),
502
+ );
503
+ ```
504
+
505
+ ---
506
+
507
+ ### errorKeys
508
+
509
+ Set error keys on the most recently added webhook. If the webhook response contains any of these keys, the response is treated as an error and the fallback output is used instead.
510
+
511
+ If no webhook has been added, the error keys are set globally (same as `globalErrorKeys`).
512
+
513
+ ```typescript
514
+ errorKeys(keys: string[]): this
515
+ ```
516
+
517
+ | Parameter | Type | Description |
518
+ |-----------|------------|----------------------------------------------------|
519
+ | `keys` | `string[]` | Response keys that indicate an error occurred. |
520
+
521
+ **Returns:** `this` for chaining.
522
+
523
+ ```typescript
524
+ const tool = new DataMap('api_lookup')
525
+ .purpose('Look up data from an API')
526
+ .parameter('id', 'string', 'Record ID', { required: true })
527
+ .webhook('GET', 'https://api.example.com/data/${args.id}')
528
+ .errorKeys(['error', 'error_message', 'fault'])
529
+ .output(new SwaigFunctionResult('Found: ${response.name}'))
530
+ .fallbackOutput(new SwaigFunctionResult('Lookup failed. Please try again.'));
531
+ ```
532
+
533
+ ---
534
+
535
+ ### globalErrorKeys
536
+
537
+ Set error keys at the top-level data map scope, regardless of webhook context. These apply to all webhooks in the DataMap.
538
+
539
+ ```typescript
540
+ globalErrorKeys(keys: string[]): this
541
+ ```
542
+
543
+ | Parameter | Type | Description |
544
+ |-----------|------------|----------------------------------------------------|
545
+ | `keys` | `string[]` | Response keys that indicate an error occurred. |
546
+
547
+ **Returns:** `this` for chaining.
548
+
549
+ ```typescript
550
+ const tool = new DataMap('multi_api')
551
+ .purpose('Call multiple APIs')
552
+ .globalErrorKeys(['error', 'err'])
553
+ .webhook('GET', 'https://api1.example.com/data')
554
+ .output(new SwaigFunctionResult('API 1: ${response.value}'))
555
+ .webhook('GET', 'https://api2.example.com/data')
556
+ .output(new SwaigFunctionResult('API 2: ${response.value}'))
557
+ .fallbackOutput(new SwaigFunctionResult('Both APIs failed.'));
558
+ ```
559
+
560
+ ---
561
+
562
+ ## Iteration
563
+
564
+ ### foreach
565
+
566
+ Configure iteration over an array in the webhook response. This allows you to loop over a list of items and build a composite response string.
567
+
568
+ ```typescript
569
+ foreach(config: {
570
+ input_key: string;
571
+ output_key: string;
572
+ append: string;
573
+ max?: number;
574
+ }): this
575
+ ```
576
+
577
+ | Parameter | Type | Description |
578
+ |--------------------|----------|--------------------------------------------------------------|
579
+ | `config.input_key` | `string` | Dot-path to the array in the response (e.g., `"results"`). |
580
+ | `config.output_key`| `string` | Variable name that holds the concatenated output. |
581
+ | `config.append` | `string` | Template string appended for each item in the array. |
582
+ | `config.max` | `number` | Optional maximum number of items to iterate over. |
583
+
584
+ **Throws:** `Error` if no webhook has been added yet.
585
+
586
+ **Returns:** `this` for chaining.
587
+
588
+ The `append` template has access to the current item's fields. After iteration, the concatenated result is available via `${output_key}` in the output template.
589
+
590
+ ```typescript
591
+ const tool = new DataMap('list_orders')
592
+ .purpose('List recent orders for a customer')
593
+ .parameter('customer_id', 'string', 'Customer ID', { required: true })
594
+ .webhook('GET', 'https://api.store.example.com/customers/${args.customer_id}/orders')
595
+ .foreach({
596
+ input_key: 'orders',
597
+ output_key: 'order_list',
598
+ append: 'Order #${id}: ${status} - $${total}\n',
599
+ max: 5,
600
+ })
601
+ .output(
602
+ new SwaigFunctionResult('Recent orders for customer ${args.customer_id}:\n${order_list}'),
603
+ )
604
+ .fallbackOutput(
605
+ new SwaigFunctionResult('Could not retrieve orders for customer ${args.customer_id}.'),
606
+ );
607
+ ```
608
+
609
+ In this example, if the API returns:
610
+
611
+ ```json
612
+ {
613
+ "orders": [
614
+ { "id": "1001", "status": "shipped", "total": "29.99" },
615
+ { "id": "1002", "status": "processing", "total": "49.50" },
616
+ { "id": "1003", "status": "delivered", "total": "15.00" }
617
+ ]
618
+ }
619
+ ```
620
+
621
+ The `${order_list}` variable becomes:
622
+
623
+ ```
624
+ Order #1001: shipped - $29.99
625
+ Order #1002: processing - $49.50
626
+ Order #1003: delivered - $15.00
627
+ ```
628
+
629
+ ---
630
+
631
+ ## Environment Variables
632
+
633
+ ### enableEnvExpansion
634
+
635
+ Enable `${ENV.*}` variable expansion in URLs, bodies, headers, and outputs. When enabled, all `${ENV.VARIABLE_NAME}` references are replaced with the corresponding `process.env` values at the time `toSwaigFunction()` is called.
636
+
637
+ ```typescript
638
+ enableEnvExpansion(enabled?: boolean): this
639
+ ```
640
+
641
+ | Parameter | Type | Default | Description |
642
+ |-----------|-----------|---------|-----------------------------------------|
643
+ | `enabled` | `boolean` | `true` | Whether to enable environment expansion.|
644
+
645
+ **Returns:** `this` for chaining.
646
+
647
+ This is useful for embedding API keys or configuration values without hardcoding them:
648
+
649
+ ```typescript
650
+ // Set environment variables (typically via .env file or deployment config)
651
+ // API_KEY=sk-abc123
652
+ // API_BASE_URL=https://api.example.com
653
+
654
+ const tool = new DataMap('secure_lookup')
655
+ .purpose('Look up data from a secure API')
656
+ .parameter('query', 'string', 'Search query', { required: true })
657
+ .enableEnvExpansion()
658
+ .webhook('GET', '${ENV.API_BASE_URL}/search?q=${args.query}', {
659
+ headers: {
660
+ 'Authorization': 'Bearer ${ENV.API_KEY}',
661
+ },
662
+ })
663
+ .output(new SwaigFunctionResult('Result: ${response.data}'));
664
+ ```
665
+
666
+ When `toSwaigFunction()` is called, `${ENV.API_KEY}` is replaced with the value of `process.env.API_KEY`. If the environment variable is not set, it is replaced with an empty string.
667
+
668
+ **Important:** The expansion happens at serialization time (when `toSwaigFunction()` is called), not at runtime. The resolved values are baked into the SWML document.
669
+
670
+ ---
671
+
672
+ ## Registration
673
+
674
+ ### registerWithAgent
675
+
676
+ Register this DataMap tool with an `AgentBase` instance. This is a convenience method that calls `toSwaigFunction()` internally and passes the result to the agent's `registerSwaigFunction()` method.
677
+
678
+ ```typescript
679
+ registerWithAgent(agent: {
680
+ registerSwaigFunction(fn: Record<string, unknown>): unknown
681
+ }): this
682
+ ```
683
+
684
+ | Parameter | Type | Description |
685
+ |-----------|----------|----------------------------------------------------|
686
+ | `agent` | `object` | An object with a `registerSwaigFunction` method. |
687
+
688
+ **Returns:** `this` for chaining.
689
+
690
+ ```typescript
691
+ import { AgentBase, DataMap, SwaigFunctionResult } from '@anthropic/signalwire-agents';
692
+
693
+ const agent = new AgentBase({ name: 'my-agent', basicAuth: ['user', 'pass'] });
694
+
695
+ new DataMap('get_time')
696
+ .purpose('Get the current time in a timezone')
697
+ .parameter('timezone', 'string', 'IANA timezone', { required: true })
698
+ .webhook('GET', 'https://worldtimeapi.org/api/timezone/${args.timezone}')
699
+ .output(new SwaigFunctionResult('Current time: ${response.datetime}'))
700
+ .fallbackOutput(new SwaigFunctionResult('Could not get time for that timezone.'))
701
+ .registerWithAgent(agent);
702
+
703
+ agent.run();
704
+ ```
705
+
706
+ ---
707
+
708
+ ### toSwaigFunction
709
+
710
+ Serialize the DataMap to a SWAIG function definition object suitable for inclusion in a SWML document. This is the terminal method that produces the wire format.
711
+
712
+ ```typescript
713
+ toSwaigFunction(): Record<string, unknown>
714
+ ```
715
+
716
+ **Returns:** A plain object with `function`, `description`, `parameters`, and `data_map` fields.
717
+
718
+ ```typescript
719
+ const tool = new DataMap('echo')
720
+ .purpose('Echo back the input')
721
+ .parameter('text', 'string', 'Text to echo', { required: true })
722
+ .expression(
723
+ '${args.text}',
724
+ '.*',
725
+ new SwaigFunctionResult('You said: ${args.text}'),
726
+ );
727
+
728
+ const swaigDef = tool.toSwaigFunction();
729
+ console.log(JSON.stringify(swaigDef, null, 2));
730
+ ```
731
+
732
+ Output:
733
+
734
+ ```json
735
+ {
736
+ "function": "echo",
737
+ "description": "Echo back the input",
738
+ "parameters": {
739
+ "type": "object",
740
+ "properties": {
741
+ "text": { "type": "string", "description": "Text to echo" }
742
+ },
743
+ "required": ["text"]
744
+ },
745
+ "data_map": {
746
+ "expressions": [
747
+ {
748
+ "string": "${args.text}",
749
+ "pattern": ".*",
750
+ "output": { "response": "You said: ${args.text}" }
751
+ }
752
+ ]
753
+ }
754
+ }
755
+ ```
756
+
757
+ You can then register this definition manually:
758
+
759
+ ```typescript
760
+ agent.registerSwaigFunction(tool.toSwaigFunction());
761
+ ```
762
+
763
+ ---
764
+
765
+ ## Helper Functions
766
+
767
+ ### createSimpleApiTool
768
+
769
+ Create a DataMap tool that calls a single API endpoint and formats the response. This is a convenience function for the most common DataMap pattern: one GET/POST request with a response template.
770
+
771
+ ```typescript
772
+ import { createSimpleApiTool } from '@anthropic/signalwire-agents';
773
+
774
+ createSimpleApiTool(opts: {
775
+ name: string;
776
+ url: string;
777
+ responseTemplate: string;
778
+ parameters?: Record<string, {
779
+ type?: string;
780
+ description?: string;
781
+ required?: boolean;
782
+ }>;
783
+ method?: string;
784
+ headers?: Record<string, string>;
785
+ body?: Record<string, unknown>;
786
+ errorKeys?: string[];
787
+ }): DataMap
788
+ ```
789
+
790
+ | Parameter | Type | Default | Description |
791
+ |-------------------------|----------------------------|-----------|---------------------------------------------------|
792
+ | `opts.name` | `string` | -- | Tool name. |
793
+ | `opts.url` | `string` | -- | Webhook URL (supports template variables). |
794
+ | `opts.responseTemplate` | `string` | -- | Response template with `${response.*}` variables. |
795
+ | `opts.parameters` | `Record<string, {...}>` | -- | Parameter definitions. |
796
+ | `opts.method` | `string` | `'GET'` | HTTP method. |
797
+ | `opts.headers` | `Record<string, string>` | -- | Request headers. |
798
+ | `opts.body` | `Record<string, unknown>` | -- | Request body (for POST/PUT). |
799
+ | `opts.errorKeys` | `string[]` | -- | Response keys indicating errors. |
800
+
801
+ **Returns:** A configured `DataMap` instance ready for registration.
802
+
803
+ ```typescript
804
+ import { createSimpleApiTool } from '@anthropic/signalwire-agents';
805
+
806
+ // Minimal: a single GET endpoint
807
+ const jokeTool = createSimpleApiTool({
808
+ name: 'get_joke',
809
+ url: 'https://official-joke-api.appspot.com/random_joke',
810
+ responseTemplate: 'Here is a joke: ${response.setup} ... ${response.punchline}',
811
+ });
812
+
813
+ agent.registerSwaigFunction(jokeTool.toSwaigFunction());
814
+
815
+ // With parameters and POST
816
+ const searchTool = createSimpleApiTool({
817
+ name: 'search_docs',
818
+ url: 'https://api.docs.example.com/search',
819
+ method: 'POST',
820
+ responseTemplate: 'Found ${response.total} results. Top result: ${response.results[0].title}',
821
+ parameters: {
822
+ query: { type: 'string', description: 'Search query', required: true },
823
+ limit: { type: 'integer', description: 'Max results' },
824
+ },
825
+ headers: {
826
+ 'Authorization': 'Bearer my-token',
827
+ },
828
+ body: {
829
+ q: '${args.query}',
830
+ max: '${args.limit}',
831
+ },
832
+ errorKeys: ['error'],
833
+ });
834
+
835
+ agent.registerSwaigFunction(searchTool.toSwaigFunction());
836
+ ```
837
+
838
+ ---
839
+
840
+ ### createExpressionTool
841
+
842
+ Create a DataMap tool that evaluates expressions against patterns without making any HTTP calls. Useful for validation, classification, and simple lookups.
843
+
844
+ ```typescript
845
+ import { createExpressionTool } from '@anthropic/signalwire-agents';
846
+
847
+ createExpressionTool(opts: {
848
+ name: string;
849
+ patterns: Record<string, [string, SwaigFunctionResult]>;
850
+ parameters?: Record<string, {
851
+ type?: string;
852
+ description?: string;
853
+ required?: boolean;
854
+ }>;
855
+ }): DataMap
856
+ ```
857
+
858
+ | Parameter | Type | Description |
859
+ |------------------|-------------------------------------------------------|--------------------------------------------------------|
860
+ | `opts.name` | `string` | Tool name. |
861
+ | `opts.patterns` | `Record<string, [string, SwaigFunctionResult]>` | Map of test values to `[pattern, output]` tuples. |
862
+ | `opts.parameters`| `Record<string, {...}>` | Parameter definitions. |
863
+
864
+ The `patterns` object maps test values (template strings) to tuples of `[regexPattern, result]`:
865
+
866
+ ```typescript
867
+ import { createExpressionTool, SwaigFunctionResult } from '@anthropic/signalwire-agents';
868
+
869
+ const validator = createExpressionTool({
870
+ name: 'validate_phone',
871
+ patterns: {
872
+ '${args.phone}': [
873
+ '^\\+?1?\\d{10,15}$',
874
+ new SwaigFunctionResult('The phone number ${args.phone} is valid.'),
875
+ ],
876
+ },
877
+ parameters: {
878
+ phone: { type: 'string', description: 'Phone number to validate', required: true },
879
+ },
880
+ });
881
+
882
+ agent.registerSwaigFunction(validator.toSwaigFunction());
883
+ ```
884
+
885
+ ---
886
+
887
+ ## Template Variables Reference
888
+
889
+ Template variables are strings enclosed in `${}` that are evaluated by the SignalWire platform at runtime.
890
+
891
+ ### Argument Variables
892
+
893
+ | Variable | Description | Example |
894
+ |-------------------|----------------------------------------------------------|------------------------|
895
+ | `${args.<name>}` | Value of an input argument passed by the AI. | `${args.city}` |
896
+ | `${lc:args.<name>}` | Argument value converted to lowercase. | `${lc:args.city}` |
897
+ | `${uc:args.<name>}` | Argument value converted to uppercase. | `${uc:args.symbol}` |
898
+
899
+ ### Response Variables
900
+
901
+ | Variable | Description | Example |
902
+ |-----------------------------|-------------------------------------------------------|---------------------------------------------|
903
+ | `${response.<key>}` | Top-level field from the webhook JSON response. | `${response.name}` |
904
+ | `${response.<key>.<subkey>}` | Nested field from the response. | `${response.address.city}` |
905
+ | `${response.<key>[N]}` | Array element by index. | `${response.results[0].title}` |
906
+ | `${response.<key>[N].<sub>}` | Field within an array element. | `${response.current_condition[0].temp_F}` |
907
+
908
+ ### Environment Variables
909
+
910
+ | Variable | Description | Example |
911
+ |-------------------|--------------------------------------------------------------------|------------------------|
912
+ | `${ENV.<name>}` | Value of `process.env.<name>`. Requires `enableEnvExpansion()`. | `${ENV.API_KEY}` |
913
+
914
+ ### Foreach Variables
915
+
916
+ Inside a `foreach` `append` template, you can reference fields of the current item directly:
917
+
918
+ | Variable | Description | Example |
919
+ |------------|--------------------------------------------|----------------|
920
+ | `${<field>}` | Field of the current item in the array. | `${id}`, `${name}` |
921
+
922
+ ### Case Transformation
923
+
924
+ The `${lc:...}` and `${uc:...}` prefixes can be applied to argument variables to transform them to lowercase or uppercase respectively. This is particularly useful in URL templates:
925
+
926
+ ```typescript
927
+ // Lowercase city name for URL-friendly paths
928
+ .webhook('GET', 'https://api.example.com/cities/${lc:args.city_name}')
929
+
930
+ // Uppercase stock ticker symbol
931
+ .webhook('GET', 'https://api.example.com/stocks/${uc:args.symbol}')
932
+ ```
933
+
934
+ ---
935
+
936
+ ## Complete Examples
937
+
938
+ ### Example 1: Weather lookup with DataMap builder
939
+
940
+ ```typescript
941
+ import { AgentBase, DataMap, SwaigFunctionResult } from '@anthropic/signalwire-agents';
942
+
943
+ const agent = new AgentBase({
944
+ name: 'weather-agent',
945
+ route: '/',
946
+ basicAuth: ['user', 'pass'],
947
+ });
948
+
949
+ agent.setPromptText('You are a weather assistant. Help users check the weather.');
950
+
951
+ const weatherTool = new DataMap('get_weather')
952
+ .purpose('Get current weather for a city')
953
+ .parameter('city', 'string', 'The city name', { required: true })
954
+ .webhook('GET', 'https://wttr.in/${lc:args.city}?format=j1')
955
+ .output(
956
+ new SwaigFunctionResult(
957
+ 'Weather in ${args.city}: ${response.current_condition[0].temp_F}F, ' +
958
+ '${response.current_condition[0].weatherDesc[0].value}'
959
+ ),
960
+ )
961
+ .fallbackOutput(
962
+ new SwaigFunctionResult('Sorry, could not fetch weather for that city.'),
963
+ );
964
+
965
+ agent.registerSwaigFunction(weatherTool.toSwaigFunction());
966
+ agent.run();
967
+ ```
968
+
969
+ ### Example 2: Expression-only tool (no HTTP)
970
+
971
+ ```typescript
972
+ import { AgentBase, DataMap, SwaigFunctionResult } from '@anthropic/signalwire-agents';
973
+
974
+ const agent = new AgentBase({
975
+ name: 'validator-agent',
976
+ route: '/',
977
+ basicAuth: ['user', 'pass'],
978
+ });
979
+
980
+ agent.setPromptText('You help validate user data like emails and phone numbers.');
981
+
982
+ const emailValidator = new DataMap('validate_email')
983
+ .purpose('Check if an email address is valid')
984
+ .parameter('email', 'string', 'Email address to validate', { required: true })
985
+ .expression(
986
+ '${args.email}',
987
+ /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
988
+ new SwaigFunctionResult('The email ${args.email} is valid.'),
989
+ new SwaigFunctionResult('The email ${args.email} is invalid. Please provide a correct email address.'),
990
+ );
991
+
992
+ emailValidator.registerWithAgent(agent);
993
+ agent.run();
994
+ ```
995
+
996
+ ### Example 3: API tool with POST body and error handling
997
+
998
+ ```typescript
999
+ import { AgentBase, DataMap, SwaigFunctionResult } from '@anthropic/signalwire-agents';
1000
+
1001
+ const agent = new AgentBase({
1002
+ name: 'ticket-agent',
1003
+ route: '/',
1004
+ basicAuth: ['user', 'pass'],
1005
+ });
1006
+
1007
+ agent.setPromptText('You are a support agent. Create tickets for customer issues.');
1008
+
1009
+ const ticketTool = new DataMap('create_ticket')
1010
+ .purpose('Create a support ticket for the customer')
1011
+ .parameter('subject', 'string', 'Brief description of the issue', { required: true })
1012
+ .parameter('priority', 'string', 'Urgency level', {
1013
+ required: true,
1014
+ enum: ['low', 'medium', 'high', 'critical'],
1015
+ })
1016
+ .parameter('description', 'string', 'Detailed description of the issue')
1017
+ .enableEnvExpansion()
1018
+ .webhook('POST', '${ENV.HELPDESK_URL}/api/tickets', {
1019
+ headers: {
1020
+ 'Authorization': 'Bearer ${ENV.HELPDESK_TOKEN}',
1021
+ 'Content-Type': 'application/json',
1022
+ },
1023
+ })
1024
+ .body({
1025
+ subject: '${args.subject}',
1026
+ priority: '${args.priority}',
1027
+ description: '${args.description}',
1028
+ source: 'phone',
1029
+ })
1030
+ .errorKeys(['error', 'message'])
1031
+ .output(
1032
+ new SwaigFunctionResult(
1033
+ 'Ticket #${response.ticket_id} created successfully with ${args.priority} priority.'
1034
+ ),
1035
+ )
1036
+ .fallbackOutput(
1037
+ new SwaigFunctionResult('Failed to create the ticket. Please try again or contact support via email.'),
1038
+ );
1039
+
1040
+ ticketTool.registerWithAgent(agent);
1041
+ agent.run();
1042
+ ```
1043
+
1044
+ ### Example 4: Iteration over array responses
1045
+
1046
+ ```typescript
1047
+ import { AgentBase, DataMap, SwaigFunctionResult } from '@anthropic/signalwire-agents';
1048
+
1049
+ const agent = new AgentBase({
1050
+ name: 'store-agent',
1051
+ route: '/',
1052
+ basicAuth: ['user', 'pass'],
1053
+ });
1054
+
1055
+ agent.setPromptText('You help customers find products and check orders.');
1056
+
1057
+ const ordersTool = new DataMap('list_orders')
1058
+ .purpose('List recent orders for the caller')
1059
+ .parameter('email', 'string', 'Customer email address', { required: true })
1060
+ .webhook('GET', 'https://api.store.example.com/orders?email=${args.email}')
1061
+ .foreach({
1062
+ input_key: 'orders',
1063
+ output_key: 'order_summary',
1064
+ append: '- Order #${id}: ${status}, Total: $${total}\n',
1065
+ max: 10,
1066
+ })
1067
+ .output(
1068
+ new SwaigFunctionResult('Here are your recent orders:\n${order_summary}'),
1069
+ )
1070
+ .fallbackOutput(
1071
+ new SwaigFunctionResult('No orders found for ${args.email}.'),
1072
+ );
1073
+
1074
+ ordersTool.registerWithAgent(agent);
1075
+ agent.run();
1076
+ ```
1077
+
1078
+ ### Example 5: Quick setup with createSimpleApiTool
1079
+
1080
+ ```typescript
1081
+ import { AgentBase, createSimpleApiTool } from '@anthropic/signalwire-agents';
1082
+
1083
+ const agent = new AgentBase({
1084
+ name: 'fun-agent',
1085
+ route: '/',
1086
+ basicAuth: ['user', 'pass'],
1087
+ });
1088
+
1089
+ agent.setPromptText('You tell jokes and fun facts.');
1090
+
1091
+ // One-liner API integration
1092
+ const jokeTool = createSimpleApiTool({
1093
+ name: 'get_joke',
1094
+ url: 'https://official-joke-api.appspot.com/random_joke',
1095
+ responseTemplate: '${response.setup} ... ${response.punchline}',
1096
+ });
1097
+
1098
+ const factTool = createSimpleApiTool({
1099
+ name: 'get_fact',
1100
+ url: 'https://uselessfacts.jsph.pl/random.json?language=en',
1101
+ responseTemplate: 'Fun fact: ${response.text}',
1102
+ });
1103
+
1104
+ agent.registerSwaigFunction(jokeTool.toSwaigFunction());
1105
+ agent.registerSwaigFunction(factTool.toSwaigFunction());
1106
+ agent.run();
1107
+ ```
1108
+
1109
+ ### Example 6: Expression tool with createExpressionTool
1110
+
1111
+ ```typescript
1112
+ import {
1113
+ AgentBase,
1114
+ createExpressionTool,
1115
+ SwaigFunctionResult,
1116
+ } from '@anthropic/signalwire-agents';
1117
+
1118
+ const agent = new AgentBase({
1119
+ name: 'validator-agent',
1120
+ route: '/',
1121
+ basicAuth: ['user', 'pass'],
1122
+ });
1123
+
1124
+ agent.setPromptText('You validate user inputs like zip codes and phone numbers.');
1125
+
1126
+ const zipValidator = createExpressionTool({
1127
+ name: 'validate_zip',
1128
+ patterns: {
1129
+ '${args.zip}': [
1130
+ '^\\d{5}(-\\d{4})?$',
1131
+ new SwaigFunctionResult('${args.zip} is a valid US zip code.'),
1132
+ ],
1133
+ },
1134
+ parameters: {
1135
+ zip: { type: 'string', description: 'Zip code to validate', required: true },
1136
+ },
1137
+ });
1138
+
1139
+ agent.registerSwaigFunction(zipValidator.toSwaigFunction());
1140
+ agent.run();
1141
+ ```