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,830 @@
1
+ # Prefab Agents Guide
2
+
3
+ Complete guide to the pre-built Prefab agents included in the SignalWire AI Agents TypeScript SDK.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Overview](#overview)
10
+ - [InfoGathererAgent](#infogathereragent)
11
+ - [Configuration](#infogatherer-configuration)
12
+ - [Fields](#infogatherer-fields)
13
+ - [Tools](#infogatherer-tools)
14
+ - [Session Tracking](#infogatherer-session-tracking)
15
+ - [Example](#infogatherer-example)
16
+ - [SurveyAgent](#surveyagent)
17
+ - [Configuration](#survey-configuration)
18
+ - [Question Types](#survey-question-types)
19
+ - [Branching Logic](#survey-branching-logic)
20
+ - [Scoring](#survey-scoring)
21
+ - [Tools](#survey-tools)
22
+ - [Example](#survey-example)
23
+ - [FAQBotAgent](#faqbotagent)
24
+ - [Configuration](#faqbot-configuration)
25
+ - [Matching Engine](#faqbot-matching-engine)
26
+ - [Tools](#faqbot-tools)
27
+ - [Example](#faqbot-example)
28
+ - [ConciergeAgent](#conciergeagent)
29
+ - [Configuration](#concierge-configuration)
30
+ - [Tools](#concierge-tools)
31
+ - [Example](#concierge-example)
32
+ - [ReceptionistAgent](#receptionistagent)
33
+ - [Configuration](#receptionist-configuration)
34
+ - [Tools](#receptionist-tools)
35
+ - [Example](#receptionist-example)
36
+ - [Factory Functions](#factory-functions)
37
+ - [Subclassing Prefabs](#subclassing-prefabs)
38
+
39
+ ---
40
+
41
+ ## Overview
42
+
43
+ Prefab agents are ready-to-use agent implementations built on top of `AgentBase`. They provide complete, production-ready AI voice agents for common use cases -- information gathering, surveys, FAQ bots, department routing, and front-desk reception.
44
+
45
+ Each prefab:
46
+
47
+ - **Extends `AgentBase`** and inherits all core functionality (HTTP serving, SWML generation, basic auth, CORS, proxy detection).
48
+ - **Defines its own SWAIG tools** through the `defineTools()` override, giving the AI model the ability to perform domain-specific actions.
49
+ - **Declares static `PROMPT_SECTIONS`** that set up the AI's role and behavioral rules automatically.
50
+ - **Adds dynamic prompt sections** in the constructor based on your configuration (field lists, question lists, department directories, etc.).
51
+ - **Tracks per-call session state** using `call_id` from the incoming request data, so multiple concurrent calls are isolated.
52
+
53
+ Prefabs can be used in two ways:
54
+
55
+ 1. **Directly via the constructor** -- pass a configuration object and get a fully functional agent.
56
+ 2. **Via a factory function** -- a shorthand that creates and returns a new instance (e.g., `createInfoGathererAgent(config)`).
57
+
58
+ All prefabs are exported from the main SDK entry point:
59
+
60
+ ```typescript
61
+ import {
62
+ InfoGathererAgent, SurveyAgent, FAQBotAgent,
63
+ ConciergeAgent, ReceptionistAgent,
64
+ } from 'signalwire-agents';
65
+ ```
66
+
67
+ ---
68
+
69
+ ## InfoGathererAgent
70
+
71
+ A conversational agent that sequentially collects named fields from a caller, validates each value against optional regex patterns, and fires a completion callback once all required fields are gathered.
72
+
73
+ **Source:** `src/prefabs/InfoGathererAgent.ts`
74
+
75
+ ### InfoGatherer Configuration
76
+
77
+ The constructor accepts an `InfoGathererConfig` object:
78
+
79
+ | Property | Type | Required | Default | Description |
80
+ |---|---|---|---|---|
81
+ | `name` | `string` | No | `"InfoGatherer"` | Agent display name. |
82
+ | `fields` | `InfoGathererField[]` | **Yes** | -- | Fields to collect from the caller. |
83
+ | `introMessage` | `string` | No | `"Hello! I need to collect some information from you. Let me walk you through it."` | Opening message the agent speaks when the call starts. |
84
+ | `confirmationMessage` | `string` | No | `"Thank you! I have collected all the required information."` | Message spoken after all required fields are gathered. |
85
+ | `onComplete` | `(data: Record<string, string>) => void \| Promise<void>` | No | -- | Callback fired when all required fields have been collected. Receives a copy of the collected data. |
86
+ | `agentOptions` | `Partial<AgentOptions>` | No | -- | Additional AgentBase options forwarded to `super()` (e.g., `route`, `port`, `basicAuth`). |
87
+
88
+ ### InfoGatherer Fields
89
+
90
+ Each entry in the `fields` array is an `InfoGathererField`:
91
+
92
+ | Property | Type | Required | Default | Description |
93
+ |---|---|---|---|---|
94
+ | `name` | `string` | **Yes** | -- | Unique field name, used as the key in collected data. |
95
+ | `description` | `string` | **Yes** | -- | Human-readable description shown to the AI agent. |
96
+ | `required` | `boolean` | No | `true` | Whether this field must be collected before completion. |
97
+ | `validation` | `RegExp \| string` | No | -- | Optional validation pattern. Strings are compiled to `RegExp`. |
98
+
99
+ Fields are presented to the AI prompt as a numbered list, each annotated with `(required)` or `(optional)` and the validation pattern source if present.
100
+
101
+ ### InfoGatherer Tools
102
+
103
+ The agent registers two SWAIG tools:
104
+
105
+ #### `save_field`
106
+
107
+ Saves a collected field value from the caller. Validates the value if a validation pattern is configured.
108
+
109
+ **Parameters:**
110
+
111
+ | Name | Type | Required | Description |
112
+ |---|---|---|---|
113
+ | `field_name` | `string` | Yes | The name of the field to save. |
114
+ | `value` | `string` | Yes | The value provided by the caller. |
115
+
116
+ **Behavior:**
117
+ - Returns an error if the field name is unknown, listing available fields.
118
+ - Runs the value against the field's validation regex (if configured). Returns a validation error with the expected pattern if it fails.
119
+ - Saves the value into the per-call session.
120
+ - If this save completes all required fields and `onComplete` has not yet fired, fires the `onComplete` callback with a shallow copy of the collected data and returns the confirmation message with a summary.
121
+ - Otherwise, returns the list of remaining required fields.
122
+
123
+ #### `get_status`
124
+
125
+ Returns the current status of information gathering.
126
+
127
+ **Parameters:** None.
128
+
129
+ **Returns:** A string summarizing collected fields, remaining required fields, and remaining optional fields.
130
+
131
+ ### InfoGatherer Session Tracking
132
+
133
+ Session state is keyed by `call_id` from the raw POST data. Each session tracks:
134
+
135
+ - `collected` -- a `Record<string, string>` of field name to value.
136
+ - `completeFired` -- a boolean ensuring the `onComplete` callback fires at most once per call.
137
+
138
+ When a call comes in without a `call_id`, the fallback key `"default"` is used.
139
+
140
+ ### InfoGatherer Example
141
+
142
+ ```typescript
143
+ import { InfoGathererAgent } from 'signalwire-agents';
144
+
145
+ const agent = new InfoGathererAgent({
146
+ name: 'PatientIntake',
147
+ introMessage: 'Welcome to our clinic! I need to collect some information before your appointment.',
148
+ confirmationMessage: 'Thank you! Your information has been saved. A nurse will be with you shortly.',
149
+ fields: [
150
+ {
151
+ name: 'full_name',
152
+ description: 'The patient\'s full legal name',
153
+ required: true,
154
+ },
155
+ {
156
+ name: 'date_of_birth',
157
+ description: 'Date of birth in MM/DD/YYYY format',
158
+ required: true,
159
+ validation: /^\d{2}\/\d{2}\/\d{4}$/,
160
+ },
161
+ {
162
+ name: 'phone_number',
163
+ description: 'Contact phone number',
164
+ required: true,
165
+ validation: '^\\+?\\d{10,15}$',
166
+ },
167
+ {
168
+ name: 'insurance_provider',
169
+ description: 'Name of the insurance provider',
170
+ required: false,
171
+ },
172
+ ],
173
+ onComplete: async (data) => {
174
+ console.log('Patient intake complete:', data);
175
+ // Save to database, send notification, etc.
176
+ },
177
+ agentOptions: {
178
+ port: 3001,
179
+ route: '/intake',
180
+ },
181
+ });
182
+
183
+ agent.start();
184
+ ```
185
+
186
+ ---
187
+
188
+ ## SurveyAgent
189
+
190
+ A conversational agent that conducts surveys with multiple question types, branching logic based on answers, and per-answer scoring.
191
+
192
+ **Source:** `src/prefabs/SurveyAgent.ts`
193
+
194
+ ### Survey Configuration
195
+
196
+ The constructor accepts a `SurveyConfig` object:
197
+
198
+ | Property | Type | Required | Default | Description |
199
+ |---|---|---|---|---|
200
+ | `name` | `string` | No | `"SurveyAgent"` | Agent display name. |
201
+ | `questions` | `SurveyQuestion[]` | **Yes** | -- | Ordered list of survey questions. |
202
+ | `introMessage` | `string` | No | `"Thank you for taking our survey. I have a few questions for you."` | Opening message before the first question. |
203
+ | `completionMessage` | `string` | No | `"Thank you for completing the survey! Your responses have been recorded."` | Message spoken after the survey is complete. |
204
+ | `onComplete` | `(responses: Record<string, unknown>, score: number) => void \| Promise<void>` | No | -- | Callback fired when the survey is finished. Receives all responses and the total score. |
205
+ | `agentOptions` | `Partial<AgentOptions>` | No | -- | Additional AgentBase options. |
206
+
207
+ Each `SurveyQuestion` has the following shape:
208
+
209
+ | Property | Type | Required | Description |
210
+ |---|---|---|---|
211
+ | `id` | `string` | **Yes** | Unique question identifier. |
212
+ | `text` | `string` | **Yes** | The question text to ask the caller. |
213
+ | `type` | `'multiple_choice' \| 'open_ended' \| 'rating' \| 'yes_no'` | **Yes** | Question type; determines validation and display. |
214
+ | `options` | `string[]` | No | Answer options (used when `type` is `multiple_choice`). |
215
+ | `nextQuestion` | `string \| Record<string, string>` | No | Controls flow after this question (see Branching Logic). |
216
+ | `points` | `number \| Record<string, number>` | No | Points awarded for answers (see Scoring). |
217
+
218
+ ### Survey Question Types
219
+
220
+ | Type | Validation | Notes |
221
+ |---|---|---|
222
+ | `multiple_choice` | Answer must exactly match one of the `options` (case-insensitive). | All options are read aloud. |
223
+ | `open_ended` | No validation; any answer is accepted. | Free-form response. |
224
+ | `rating` | Must be an integer between 1 and 10. | The AI specifies the scale to the caller. |
225
+ | `yes_no` | Must be a recognized affirmative or negative word. | Accepts: `yes`, `y`, `yeah`, `yep`, `sure`, `absolutely`, `correct`, `true`, `no`, `n`, `nah`, `nope`, `negative`, `false`. Normalized to `"yes"` or `"no"` before storage. |
226
+
227
+ ### Survey Branching Logic
228
+
229
+ The `nextQuestion` property controls which question follows the current one:
230
+
231
+ | Value | Behavior |
232
+ |---|---|
233
+ | `undefined` (omitted) | Proceed to the next question in array order. If this is the last question, the survey ends. |
234
+ | `string` | Always jump to the question with this ID, regardless of the answer. |
235
+ | `Record<string, string>` | Conditional branching: the answer value (case-insensitive) is looked up as a key, and the value is the next question ID. A special `_default` key serves as a fallback. If no key matches and there is no `_default`, falls through to the next question in array order. |
236
+
237
+ ### Survey Scoring
238
+
239
+ The `points` property controls point accumulation:
240
+
241
+ | Value | Behavior |
242
+ |---|---|
243
+ | `undefined` (omitted) | No points for this question. |
244
+ | `number` | Fixed points awarded for any answer. |
245
+ | `Record<string, number>` | Per-answer scoring: the answer value (case-insensitive) is looked up as a key, and the matched value is the number of points awarded. Unmatched answers receive 0 points. |
246
+
247
+ The total score is accumulated across all answered questions and passed to the `onComplete` callback.
248
+
249
+ ### Survey Tools
250
+
251
+ The agent registers three SWAIG tools:
252
+
253
+ #### `answer_question`
254
+
255
+ Records the caller's answer to the current survey question.
256
+
257
+ **Parameters:**
258
+
259
+ | Name | Type | Required | Description |
260
+ |---|---|---|---|
261
+ | `question_id` | `string` | Yes | The ID of the question being answered. |
262
+ | `answer` | `string` | Yes | The caller's answer. |
263
+
264
+ **Behavior:**
265
+ - Validates the answer based on question type. Returns a validation error if invalid.
266
+ - Normalizes the answer (e.g., yes/no variants become `"yes"` or `"no"`).
267
+ - Calculates and accumulates points.
268
+ - Resolves the next question via branching logic.
269
+ - If there is no next question, marks the survey complete, fires `onComplete`, and returns the completion message with answer count and total score.
270
+ - Otherwise, advances the session and returns the next question text with its type-specific instructions.
271
+
272
+ #### `get_current_question`
273
+
274
+ Returns the current question to be asked to the caller.
275
+
276
+ **Parameters:** None.
277
+
278
+ **Returns:** The question ID, type, text, and type-specific instructions (options for multiple choice, scale for rating, etc.).
279
+
280
+ #### `get_survey_progress`
281
+
282
+ Returns the current progress of the survey.
283
+
284
+ **Parameters:** None.
285
+
286
+ **Returns:** A summary including questions answered out of total, percentage complete, current score, completion status, and all answers so far.
287
+
288
+ ### Survey Example
289
+
290
+ ```typescript
291
+ import { SurveyAgent } from 'signalwire-agents';
292
+
293
+ const agent = new SurveyAgent({
294
+ name: 'CustomerSatisfaction',
295
+ introMessage: 'Hi! We would love to hear your feedback about our service.',
296
+ completionMessage: 'Thanks for your feedback! We really appreciate it.',
297
+ questions: [
298
+ {
299
+ id: 'overall',
300
+ text: 'How would you rate your overall experience?',
301
+ type: 'rating',
302
+ points: { '9': 10, '10': 10, '7': 7, '8': 7 },
303
+ },
304
+ {
305
+ id: 'recommend',
306
+ text: 'Would you recommend us to a friend?',
307
+ type: 'yes_no',
308
+ points: { 'yes': 5, 'no': 0 },
309
+ nextQuestion: {
310
+ 'yes': 'what_liked',
311
+ 'no': 'what_improve',
312
+ },
313
+ },
314
+ {
315
+ id: 'what_liked',
316
+ text: 'What did you like the most about our service?',
317
+ type: 'open_ended',
318
+ nextQuestion: 'followup',
319
+ },
320
+ {
321
+ id: 'what_improve',
322
+ text: 'What could we improve?',
323
+ type: 'open_ended',
324
+ nextQuestion: 'followup',
325
+ },
326
+ {
327
+ id: 'followup',
328
+ text: 'How would you like us to follow up?',
329
+ type: 'multiple_choice',
330
+ options: ['Email', 'Phone call', 'No follow-up needed'],
331
+ },
332
+ ],
333
+ onComplete: async (responses, score) => {
334
+ console.log('Survey complete! Score:', score);
335
+ console.log('Responses:', responses);
336
+ // Store results, send to analytics, etc.
337
+ },
338
+ });
339
+
340
+ agent.start();
341
+ ```
342
+
343
+ ---
344
+
345
+ ## FAQBotAgent
346
+
347
+ A conversational agent that answers frequently asked questions using keyword and word-overlap matching with configurable similarity thresholds and optional escalation to a live agent.
348
+
349
+ **Source:** `src/prefabs/FAQBotAgent.ts`
350
+
351
+ ### FAQBot Configuration
352
+
353
+ The constructor accepts a `FAQBotConfig` object:
354
+
355
+ | Property | Type | Required | Default | Description |
356
+ |---|---|---|---|---|
357
+ | `name` | `string` | No | `"FAQBot"` | Agent display name. |
358
+ | `faqs` | `FAQEntry[]` | **Yes** | -- | List of FAQ entries for the knowledge base. |
359
+ | `threshold` | `number` | No | `0.5` | Minimum match score (0--1) for an FAQ to be considered a match. |
360
+ | `escalationMessage` | `string` | No | `"I'm sorry, I couldn't find an answer to your question. Let me transfer you to someone who can help."` | Message spoken when no FAQ matches the query. |
361
+ | `escalationNumber` | `string` | No | -- | Phone number to transfer to on escalation. If not set, the `escalate` tool is **not registered**. |
362
+ | `agentOptions` | `Partial<AgentOptions>` | No | -- | Additional AgentBase options. |
363
+
364
+ Each `FAQEntry` has the following shape:
365
+
366
+ | Property | Type | Required | Description |
367
+ |---|---|---|---|
368
+ | `question` | `string` | **Yes** | The representative question text. |
369
+ | `answer` | `string` | **Yes** | The answer to provide when this FAQ matches. |
370
+ | `keywords` | `string[]` | No | Additional keywords to boost matching accuracy. |
371
+
372
+ ### FAQBot Matching Engine
373
+
374
+ The matching engine computes a word-overlap similarity score between the caller's query and each FAQ entry.
375
+
376
+ **Tokenization:**
377
+ 1. Text is lowercased.
378
+ 2. Non-alphanumeric characters are replaced with spaces.
379
+ 3. Text is split on whitespace.
380
+ 4. Words shorter than 2 characters are removed.
381
+ 5. Common English stop words are filtered out (e.g., "the", "is", "to", "and", "or", etc.).
382
+
383
+ **Scoring:**
384
+ - **Question overlap** -- the number of shared tokens between the query and the FAQ question text, normalized by the smaller token set.
385
+ - **Keyword overlap** -- each keyword is checked for substring presence in the lowercased query; the hit count is divided by total keywords.
386
+ - **Combined score** -- if keywords are present, the final score is `0.6 * questionOverlap + 0.4 * keywordScore`. Without keywords, only question overlap is used.
387
+
388
+ The best-scoring FAQ is returned if its score meets or exceeds the configured `threshold`. Runner-up matches above the threshold are mentioned as related results.
389
+
390
+ ### FAQBot Tools
391
+
392
+ #### `search_faq`
393
+
394
+ Searches the FAQ knowledge base for an answer to the caller's question.
395
+
396
+ **Parameters:**
397
+
398
+ | Name | Type | Required | Description |
399
+ |---|---|---|---|
400
+ | `query` | `string` | Yes | The caller's question or search query. |
401
+
402
+ **Returns:** The best matching FAQ entry with confidence score, plus up to 2 related runner-ups if they also exceed the threshold. If no match meets the threshold, returns the escalation message.
403
+
404
+ #### `escalate`
405
+
406
+ Transfers the caller to a live agent. **Only registered when `escalationNumber` is configured.**
407
+
408
+ **Parameters:**
409
+
410
+ | Name | Type | Required | Description |
411
+ |---|---|---|---|
412
+ | `reason` | `string` | No | The reason for escalation. Defaults to `"Caller needs assistance beyond FAQ"`. |
413
+
414
+ **Behavior:** Uses `SwaigFunctionResult.connect()` to transfer the call to the configured `escalationNumber`.
415
+
416
+ ### FAQBot Example
417
+
418
+ ```typescript
419
+ import { FAQBotAgent } from 'signalwire-agents';
420
+
421
+ const agent = new FAQBotAgent({
422
+ name: 'HelpDesk',
423
+ threshold: 0.4,
424
+ escalationNumber: '+15559876543',
425
+ escalationMessage: 'I was not able to find an answer for that. Let me connect you with a support specialist.',
426
+ faqs: [
427
+ {
428
+ question: 'What are your business hours?',
429
+ answer: 'We are open Monday through Friday, 9 AM to 5 PM Eastern Time.',
430
+ keywords: ['hours', 'open', 'close', 'schedule', 'when'],
431
+ },
432
+ {
433
+ question: 'How do I reset my password?',
434
+ answer: 'Go to the login page, click "Forgot Password", and follow the email instructions.',
435
+ keywords: ['password', 'reset', 'forgot', 'login', 'locked'],
436
+ },
437
+ {
438
+ question: 'What is your return policy?',
439
+ answer: 'We offer a 30-day return policy for unused items in original packaging.',
440
+ keywords: ['return', 'refund', 'exchange', 'policy'],
441
+ },
442
+ {
443
+ question: 'How do I contact support?',
444
+ answer: 'You can reach our support team by calling this number, by email at support@example.com, or through live chat on our website.',
445
+ keywords: ['support', 'contact', 'help', 'email', 'chat'],
446
+ },
447
+ ],
448
+ agentOptions: {
449
+ route: '/helpdesk',
450
+ },
451
+ });
452
+
453
+ agent.start();
454
+ ```
455
+
456
+ ---
457
+
458
+ ## ConciergeAgent
459
+
460
+ A multi-department routing agent that provides callers with department information, hours of operation, and call transfer capabilities.
461
+
462
+ **Source:** `src/prefabs/ConciergeAgent.ts`
463
+
464
+ ### Concierge Configuration
465
+
466
+ The constructor accepts a `ConciergeConfig` object:
467
+
468
+ | Property | Type | Required | Default | Description |
469
+ |---|---|---|---|---|
470
+ | `name` | `string` | No | `"Concierge"` | Agent display name. |
471
+ | `departments` | `Department[]` | **Yes** | -- | List of departments available for routing. |
472
+ | `companyName` | `string` | No | `"our company"` | Company name used in greetings and prompts. |
473
+ | `generalInfo` | `string` | No | `""` | General company information the agent can share. |
474
+ | `afterHoursMessage` | `string` | No | `"This department is currently closed. Please try again during business hours."` | Message spoken when a department is closed or has no transfer number. |
475
+ | `agentOptions` | `Partial<AgentOptions>` | No | -- | Additional AgentBase options. |
476
+
477
+ Each `Department` has the following shape:
478
+
479
+ | Property | Type | Required | Description |
480
+ |---|---|---|---|
481
+ | `name` | `string` | **Yes** | Department name (e.g., "Sales", "Support"). |
482
+ | `description` | `string` | **Yes** | Description of what the department handles. |
483
+ | `transferNumber` | `string` | No | Phone number or SIP address for transfers. |
484
+ | `keywords` | `string[]` | No | Keywords that help route callers to this department. |
485
+ | `hoursOfOperation` | `string` | No | Human-readable hours (e.g., "Mon-Fri 9am-5pm EST"). |
486
+
487
+ Department names and keywords are added as speech recognition hints. Department lookup supports matching by name or by keyword (both case-insensitive).
488
+
489
+ ### Concierge Tools
490
+
491
+ #### `list_departments`
492
+
493
+ Lists all available departments with their descriptions and hours of operation.
494
+
495
+ **Parameters:** None.
496
+
497
+ **Returns:** A formatted list of every department including name, description, hours, and whether direct transfer is available.
498
+
499
+ #### `get_department_info`
500
+
501
+ Gets detailed information about a specific department.
502
+
503
+ **Parameters:**
504
+
505
+ | Name | Type | Required | Description |
506
+ |---|---|---|---|
507
+ | `department_name` | `string` | Yes | The name of the department to look up. |
508
+
509
+ **Returns:** Department name, description, hours of operation, transfer availability, and related topics (keywords). Returns an error with available department names if the department is not found.
510
+
511
+ #### `transfer_to_department`
512
+
513
+ Transfers the caller to a specific department.
514
+
515
+ **Parameters:**
516
+
517
+ | Name | Type | Required | Description |
518
+ |---|---|---|---|
519
+ | `department_name` | `string` | Yes | The name of the department to transfer to. |
520
+
521
+ **Behavior:**
522
+ - Looks up the department by name or keyword (case-insensitive).
523
+ - Returns an error if the department is not found.
524
+ - Returns the `afterHoursMessage` if the department has no `transferNumber`.
525
+ - Uses `SwaigFunctionResult.connect()` to initiate the transfer.
526
+
527
+ ### Concierge Example
528
+
529
+ ```typescript
530
+ import { ConciergeAgent } from 'signalwire-agents';
531
+
532
+ const agent = new ConciergeAgent({
533
+ name: 'MainLine',
534
+ companyName: 'Acme Corporation',
535
+ generalInfo: 'Acme Corporation is a leading provider of innovative solutions since 1985.',
536
+ afterHoursMessage: 'That department is currently closed. Please call back during business hours or leave a voicemail.',
537
+ departments: [
538
+ {
539
+ name: 'Sales',
540
+ description: 'New product inquiries, pricing, and demos',
541
+ transferNumber: '+15551001001',
542
+ keywords: ['buy', 'purchase', 'pricing', 'demo', 'quote'],
543
+ hoursOfOperation: 'Mon-Fri 8am-6pm EST',
544
+ },
545
+ {
546
+ name: 'Technical Support',
547
+ description: 'Help with existing products, troubleshooting, and bug reports',
548
+ transferNumber: '+15551001002',
549
+ keywords: ['help', 'broken', 'issue', 'bug', 'problem', 'error'],
550
+ hoursOfOperation: 'Mon-Fri 9am-9pm EST, Sat 10am-4pm EST',
551
+ },
552
+ {
553
+ name: 'Billing',
554
+ description: 'Invoices, payments, and subscription management',
555
+ transferNumber: '+15551001003',
556
+ keywords: ['invoice', 'payment', 'bill', 'subscription', 'charge'],
557
+ hoursOfOperation: 'Mon-Fri 9am-5pm EST',
558
+ },
559
+ {
560
+ name: 'Human Resources',
561
+ description: 'Employment opportunities and employee services',
562
+ keywords: ['job', 'career', 'hiring', 'employment'],
563
+ // No transferNumber -- caller will get afterHoursMessage
564
+ },
565
+ ],
566
+ });
567
+
568
+ agent.start();
569
+ ```
570
+
571
+ ---
572
+
573
+ ## ReceptionistAgent
574
+
575
+ A front-desk agent that handles visitor check-in, department directory lookup, and call transfers by extension.
576
+
577
+ **Source:** `src/prefabs/ReceptionistAgent.ts`
578
+
579
+ ### Receptionist Configuration
580
+
581
+ The constructor accepts a `ReceptionistConfig` object:
582
+
583
+ | Property | Type | Required | Default | Description |
584
+ |---|---|---|---|---|
585
+ | `name` | `string` | No | `"Receptionist"` | Agent display name. |
586
+ | `companyName` | `string` | **Yes** | -- | Company name displayed in greetings and prompts. |
587
+ | `departments` | `ReceptionistDepartment[]` | **Yes** | -- | Departments with extensions for the directory. |
588
+ | `welcomeMessage` | `string` | No | `"Welcome to {companyName}! How may I help you today?"` | Custom welcome message. |
589
+ | `checkInEnabled` | `boolean` | No | `true` | Whether visitor check-in functionality is enabled. |
590
+ | `onVisitorCheckIn` | `(visitor: Record<string, string>) => void \| Promise<void>` | No | -- | Callback fired when a visitor checks in. |
591
+ | `agentOptions` | `Partial<AgentOptions>` | No | -- | Additional AgentBase options. |
592
+
593
+ Each `ReceptionistDepartment` has the following shape:
594
+
595
+ | Property | Type | Required | Description |
596
+ |---|---|---|---|
597
+ | `name` | `string` | **Yes** | Department name (e.g., "Engineering", "HR"). |
598
+ | `extension` | `string` | **Yes** | Internal extension number or SIP address. |
599
+ | `description` | `string` | No | Description of the department. |
600
+
601
+ The company name and all department names are added as speech recognition hints. Department lookup is by name (case-insensitive).
602
+
603
+ ### Receptionist Tools
604
+
605
+ #### `get_department_list`
606
+
607
+ Lists all departments with their extensions and descriptions.
608
+
609
+ **Parameters:** None.
610
+
611
+ **Returns:** A formatted department directory for the company.
612
+
613
+ #### `transfer_to_department`
614
+
615
+ Transfers the caller to a department by dialing its extension.
616
+
617
+ **Parameters:**
618
+
619
+ | Name | Type | Required | Description |
620
+ |---|---|---|---|
621
+ | `department_name` | `string` | Yes | The name of the department to transfer to. |
622
+
623
+ **Behavior:**
624
+ - Looks up the department by name (case-insensitive).
625
+ - Returns an error with the list of available departments if not found.
626
+ - Uses `SwaigFunctionResult.connect()` to dial the extension.
627
+
628
+ #### `check_in_visitor`
629
+
630
+ Checks in a visitor by recording their details. **Only registered when `checkInEnabled` is `true` (the default).**
631
+
632
+ **Parameters:**
633
+
634
+ | Name | Type | Required | Description |
635
+ |---|---|---|---|
636
+ | `visitor_name` | `string` | Yes | Full name of the visitor. |
637
+ | `purpose` | `string` | Yes | Purpose of the visit. |
638
+ | `visiting` | `string` | Yes | Name of the person or department the visitor is here to see. |
639
+
640
+ **Behavior:**
641
+ - Validates that all three parameters are provided.
642
+ - Creates a visitor record including `visitor_name`, `purpose`, `visiting`, and `checked_in_at` (ISO timestamp).
643
+ - Stores the record in the per-call session.
644
+ - Fires the `onVisitorCheckIn` callback with the visitor record.
645
+ - Returns a confirmation message.
646
+
647
+ ### Receptionist Example
648
+
649
+ ```typescript
650
+ import { ReceptionistAgent } from 'signalwire-agents';
651
+
652
+ const agent = new ReceptionistAgent({
653
+ companyName: 'Acme Corporation',
654
+ welcomeMessage: 'Hello and welcome to Acme Corporation! I can help you find a department, transfer your call, or check you in as a visitor.',
655
+ checkInEnabled: true,
656
+ departments: [
657
+ {
658
+ name: 'Engineering',
659
+ extension: '1001',
660
+ description: 'Software development and technical teams',
661
+ },
662
+ {
663
+ name: 'Human Resources',
664
+ extension: '1002',
665
+ description: 'Employee services, benefits, and recruiting',
666
+ },
667
+ {
668
+ name: 'Marketing',
669
+ extension: '1003',
670
+ description: 'Brand, communications, and events',
671
+ },
672
+ {
673
+ name: 'Executive Office',
674
+ extension: '1004',
675
+ description: 'CEO and executive leadership',
676
+ },
677
+ ],
678
+ onVisitorCheckIn: async (visitor) => {
679
+ console.log('Visitor checked in:', visitor);
680
+ // Send Slack notification, update visitor log, print badge, etc.
681
+ },
682
+ agentOptions: {
683
+ route: '/reception',
684
+ },
685
+ });
686
+
687
+ agent.start();
688
+ ```
689
+
690
+ ---
691
+
692
+ ## Factory Functions
693
+
694
+ Each prefab provides a factory function that creates and returns a new instance. These are simple shorthands for `new PrefabAgent(config)`:
695
+
696
+ | Factory Function | Creates |
697
+ |---|---|
698
+ | `createInfoGathererAgent(config)` | `InfoGathererAgent` |
699
+ | `createSurveyAgent(config)` | `SurveyAgent` |
700
+ | `createFAQBotAgent(config)` | `FAQBotAgent` |
701
+ | `createConciergeAgent(config)` | `ConciergeAgent` |
702
+ | `createReceptionistAgent(config)` | `ReceptionistAgent` |
703
+
704
+ Factory functions accept the same config type as their corresponding class constructors.
705
+
706
+ ```typescript
707
+ import { createSurveyAgent } from 'signalwire-agents';
708
+
709
+ const agent = createSurveyAgent({
710
+ questions: [
711
+ { id: 'q1', text: 'How was your experience?', type: 'rating' },
712
+ ],
713
+ onComplete: (responses, score) => {
714
+ console.log('Done!', responses, score);
715
+ },
716
+ });
717
+
718
+ agent.start();
719
+ ```
720
+
721
+ Factory functions and class constructors are exported both from the individual prefab modules and from the main SDK entry point:
722
+
723
+ ```typescript
724
+ // From the SDK entry point
725
+ import { createFAQBotAgent, FAQBotAgent } from 'signalwire-agents';
726
+
727
+ // From the prefab module directly
728
+ import { createFAQBotAgent } from 'signalwire-agents/prefabs';
729
+ ```
730
+
731
+ ---
732
+
733
+ ## Subclassing Prefabs
734
+
735
+ Prefab agents can be subclassed to customize their behavior. The two primary extension points are the static `PROMPT_SECTIONS` array and the `defineTools()` method.
736
+
737
+ ### Overriding `PROMPT_SECTIONS`
738
+
739
+ Each prefab declares a `static override PROMPT_SECTIONS` array that the `AgentBase` constructor merges into the prompt. You can override this in your subclass to change the AI's role and rules:
740
+
741
+ ```typescript
742
+ import { InfoGathererAgent } from 'signalwire-agents';
743
+ import type { InfoGathererConfig } from 'signalwire-agents';
744
+
745
+ class SpanishInfoGatherer extends InfoGathererAgent {
746
+ static override PROMPT_SECTIONS = [
747
+ {
748
+ title: 'Role',
749
+ body: 'You are a bilingual information-gathering assistant. You speak Spanish and English. Always respond in the same language the caller uses.',
750
+ },
751
+ {
752
+ title: 'Rules',
753
+ bullets: [
754
+ 'Ask for one field at a time, in the order listed.',
755
+ 'If the caller provides information for a later field, accept it and move on.',
756
+ 'Always confirm each piece of information before saving it using the save_field tool.',
757
+ 'If validation fails, politely ask the caller to try again in their language.',
758
+ 'Use the get_status tool when you need to check progress.',
759
+ 'Once all required fields are collected, summarize the information and confirm with the caller.',
760
+ ],
761
+ },
762
+ ];
763
+
764
+ constructor(config: InfoGathererConfig) {
765
+ super(config);
766
+ }
767
+ }
768
+ ```
769
+
770
+ ### Overriding `defineTools()`
771
+
772
+ You can override `defineTools()` to add additional tools, replace existing tools, or extend the default tool set by calling `super.defineTools()` first:
773
+
774
+ ```typescript
775
+ import { FAQBotAgent, SwaigFunctionResult } from 'signalwire-agents';
776
+ import type { FAQBotConfig } from 'signalwire-agents';
777
+
778
+ class FAQBotWithFeedback extends FAQBotAgent {
779
+ protected override defineTools(): void {
780
+ // Register all the default tools first
781
+ super.defineTools();
782
+
783
+ // Add a custom feedback tool
784
+ this.defineTool({
785
+ name: 'submit_feedback',
786
+ description: 'Allow the caller to submit feedback about the FAQ answers.',
787
+ parameters: {
788
+ type: 'object',
789
+ properties: {
790
+ rating: {
791
+ type: 'string',
792
+ description: 'How helpful was the answer: good, okay, or poor.',
793
+ },
794
+ comment: {
795
+ type: 'string',
796
+ description: 'Optional comment from the caller.',
797
+ },
798
+ },
799
+ required: ['rating'],
800
+ },
801
+ handler: (args: Record<string, unknown>) => {
802
+ const rating = args['rating'] as string;
803
+ const comment = args['comment'] as string | undefined;
804
+ console.log(`FAQ Feedback: ${rating}${comment ? ' - ' + comment : ''}`);
805
+ return new SwaigFunctionResult('Thank you for your feedback!');
806
+ },
807
+ });
808
+ }
809
+ }
810
+ ```
811
+
812
+ ### Adding Dynamic Prompt Sections
813
+
814
+ Beyond the static `PROMPT_SECTIONS`, you can use `this.promptAddSection()` in the constructor body to add additional dynamic prompt content:
815
+
816
+ ```typescript
817
+ import { ConciergeAgent } from 'signalwire-agents';
818
+ import type { ConciergeConfig } from 'signalwire-agents';
819
+
820
+ class HolidayConcierge extends ConciergeAgent {
821
+ constructor(config: ConciergeConfig) {
822
+ super(config);
823
+
824
+ // Add a holiday schedule section to the prompt
825
+ this.promptAddSection('Holiday Schedule', {
826
+ body: 'Note: The office is closed on December 25th and January 1st. All departments will reopen on January 2nd.',
827
+ });
828
+ }
829
+ }
830
+ ```