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,298 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { SwaigFunctionResult } from '../src/SwaigFunctionResult.js';
3
+
4
+ describe('SwaigFunctionResult', () => {
5
+ it('returns default response when empty', () => {
6
+ const r = new SwaigFunctionResult();
7
+ expect(r.toDict()).toEqual({ response: 'Action completed.' });
8
+ });
9
+
10
+ it('stores response from constructor', () => {
11
+ const r = new SwaigFunctionResult('hello');
12
+ expect(r.toDict()).toEqual({ response: 'hello' });
13
+ });
14
+
15
+ it('supports setResponse', () => {
16
+ const r = new SwaigFunctionResult().setResponse('hi');
17
+ expect(r.toDict()).toEqual({ response: 'hi' });
18
+ });
19
+
20
+ it('supports addAction chaining', () => {
21
+ const r = new SwaigFunctionResult('ok')
22
+ .addAction('hangup', true)
23
+ .addAction('stop', true);
24
+ const d = r.toDict();
25
+ expect(d.response).toBe('ok');
26
+ expect(d.action).toEqual([{ hangup: true }, { stop: true }]);
27
+ });
28
+
29
+ it('supports addActions', () => {
30
+ const r = new SwaigFunctionResult('ok').addActions([
31
+ { set_global_data: { k: 'v' } },
32
+ { play: { url: 'music.mp3' } },
33
+ ]);
34
+ expect((r.toDict().action as unknown[]).length).toBe(2);
35
+ });
36
+
37
+ it('includes post_process only when true and actions present', () => {
38
+ const r1 = new SwaigFunctionResult('hi', true);
39
+ expect(r1.toDict()).toEqual({ response: 'hi' }); // no actions = no post_process
40
+
41
+ const r2 = new SwaigFunctionResult('hi', true).hangup();
42
+ expect(r2.toDict().post_process).toBe(true);
43
+
44
+ const r3 = new SwaigFunctionResult('hi', false).hangup();
45
+ expect(r3.toDict().post_process).toBeUndefined();
46
+ });
47
+
48
+ it('connect builds correct SWML', () => {
49
+ const r = new SwaigFunctionResult('transferring').connect('+15551234567');
50
+ const act = (r.toDict().action as Record<string, unknown>[])[0];
51
+ expect(act['transfer']).toBe('true');
52
+ const swml = act['SWML'] as Record<string, unknown>;
53
+ expect(swml['version']).toBe('1.0.0');
54
+ });
55
+
56
+ it('connect with from address', () => {
57
+ const r = new SwaigFunctionResult('t').connect('+1555', false, '+1666');
58
+ const act = (r.toDict().action as Record<string, unknown>[])[0];
59
+ expect(act['transfer']).toBe('false');
60
+ const swml = act['SWML'] as Record<string, unknown>;
61
+ const sections = swml['sections'] as Record<string, unknown[]>;
62
+ const main = sections['main'][0] as Record<string, Record<string, string>>;
63
+ expect(main['connect']['from']).toBe('+1666');
64
+ });
65
+
66
+ it('swmlTransfer', () => {
67
+ const r = new SwaigFunctionResult('ok').swmlTransfer('https://example.com', 'bye', false);
68
+ const act = (r.toDict().action as Record<string, unknown>[])[0];
69
+ expect(act['transfer']).toBe('false');
70
+ });
71
+
72
+ it('hangup / stop / hold', () => {
73
+ const r = new SwaigFunctionResult('bye').hangup().stop().hold(60);
74
+ const acts = r.toDict().action as Record<string, unknown>[];
75
+ expect(acts[0]).toEqual({ hangup: true });
76
+ expect(acts[1]).toEqual({ stop: true });
77
+ expect(acts[2]).toEqual({ hold: 60 });
78
+ });
79
+
80
+ it('hold clamps timeout', () => {
81
+ const r = new SwaigFunctionResult().hold(9999);
82
+ expect((r.toDict().action as Record<string, unknown>[])[0]).toEqual({ hold: 900 });
83
+ });
84
+
85
+ it('waitForUser variants', () => {
86
+ expect(new SwaigFunctionResult('w').waitForUser().toDict().action)
87
+ .toEqual([{ wait_for_user: true }]);
88
+ expect(new SwaigFunctionResult('w').waitForUser({ answerFirst: true }).toDict().action)
89
+ .toEqual([{ wait_for_user: 'answer_first' }]);
90
+ expect(new SwaigFunctionResult('w').waitForUser({ timeout: 10 }).toDict().action)
91
+ .toEqual([{ wait_for_user: 10 }]);
92
+ expect(new SwaigFunctionResult('w').waitForUser({ enabled: false }).toDict().action)
93
+ .toEqual([{ wait_for_user: false }]);
94
+ });
95
+
96
+ it('say / playBackgroundFile / stopBackgroundFile', () => {
97
+ const r = new SwaigFunctionResult('ok')
98
+ .say('hello')
99
+ .playBackgroundFile('music.mp3')
100
+ .playBackgroundFile('bg.mp3', true)
101
+ .stopBackgroundFile();
102
+ const acts = r.toDict().action as Record<string, unknown>[];
103
+ expect(acts[0]).toEqual({ say: 'hello' });
104
+ expect(acts[1]).toEqual({ playback_bg: 'music.mp3' });
105
+ expect(acts[2]).toEqual({ playback_bg: { file: 'bg.mp3', wait: true } });
106
+ expect(acts[3]).toEqual({ stop_playback_bg: true });
107
+ });
108
+
109
+ it('speech hints', () => {
110
+ const r = new SwaigFunctionResult('ok')
111
+ .addDynamicHints(['word1', { pattern: 'w', replace: 'W' }])
112
+ .clearDynamicHints()
113
+ .setEndOfSpeechTimeout(500)
114
+ .setSpeechEventTimeout(200);
115
+ const acts = r.toDict().action as Record<string, unknown>[];
116
+ expect(acts.length).toBe(4);
117
+ expect(acts[1]).toEqual({ clear_dynamic_hints: {} });
118
+ expect(acts[2]).toEqual({ end_of_speech_timeout: 500 });
119
+ expect(acts[3]).toEqual({ speech_event_timeout: 200 });
120
+ });
121
+
122
+ it('global data / metadata', () => {
123
+ const r = new SwaigFunctionResult('ok')
124
+ .updateGlobalData({ key: 'val' })
125
+ .removeGlobalData(['key'])
126
+ .setMetadata({ m: 1 })
127
+ .removeMetadata('m');
128
+ const acts = r.toDict().action as Record<string, unknown>[];
129
+ expect(acts[0]).toEqual({ set_global_data: { key: 'val' } });
130
+ expect(acts[1]).toEqual({ unset_global_data: ['key'] });
131
+ expect(acts[2]).toEqual({ set_meta_data: { m: 1 } });
132
+ expect(acts[3]).toEqual({ unset_meta_data: 'm' });
133
+ });
134
+
135
+ it('swmlChangeStep / swmlChangeContext', () => {
136
+ const r = new SwaigFunctionResult('ok').swmlChangeStep('step2').swmlChangeContext('ctx2');
137
+ const acts = r.toDict().action as Record<string, unknown>[];
138
+ expect(acts[0]).toEqual({ change_step: 'step2' });
139
+ expect(acts[1]).toEqual({ change_context: 'ctx2' });
140
+ });
141
+
142
+ it('swmlUserEvent', () => {
143
+ const r = new SwaigFunctionResult('ok').swmlUserEvent({ type: 'test', val: 1 });
144
+ const acts = r.toDict().action as Record<string, unknown>[];
145
+ expect(acts[0]).toEqual({
146
+ SWML: {
147
+ sections: { main: [{ user_event: { event: { type: 'test', val: 1 } } }] },
148
+ version: '1.0.0',
149
+ },
150
+ });
151
+ });
152
+
153
+ it('switchContext simple string', () => {
154
+ const r = new SwaigFunctionResult('ok').switchContext({ systemPrompt: 'new prompt' });
155
+ expect((r.toDict().action as Record<string, unknown>[])[0]).toEqual({
156
+ context_switch: 'new prompt',
157
+ });
158
+ });
159
+
160
+ it('switchContext advanced object', () => {
161
+ const r = new SwaigFunctionResult('ok').switchContext({
162
+ systemPrompt: 'sp',
163
+ userPrompt: 'up',
164
+ consolidate: true,
165
+ });
166
+ expect((r.toDict().action as Record<string, unknown>[])[0]).toEqual({
167
+ context_switch: { system_prompt: 'sp', user_prompt: 'up', consolidate: true },
168
+ });
169
+ });
170
+
171
+ it('toggleFunctions / enableFunctionsOnTimeout / updateSettings', () => {
172
+ const r = new SwaigFunctionResult('ok')
173
+ .toggleFunctions([{ function: 'fn1', active: false }])
174
+ .enableFunctionsOnTimeout()
175
+ .updateSettings({ temperature: 0.5 });
176
+ const acts = r.toDict().action as Record<string, unknown>[];
177
+ expect(acts[0]).toEqual({ toggle_functions: [{ function: 'fn1', active: false }] });
178
+ expect(acts[1]).toEqual({ functions_on_speaker_timeout: true });
179
+ expect(acts[2]).toEqual({ settings: { temperature: 0.5 } });
180
+ });
181
+
182
+ it('simulateUserInput / enableExtensiveData / replaceInHistory', () => {
183
+ const r = new SwaigFunctionResult('ok')
184
+ .simulateUserInput('hi')
185
+ .enableExtensiveData()
186
+ .replaceInHistory('replaced');
187
+ const acts = r.toDict().action as Record<string, unknown>[];
188
+ expect(acts[0]).toEqual({ user_input: 'hi' });
189
+ expect(acts[1]).toEqual({ extensive_data: true });
190
+ expect(acts[2]).toEqual({ replace_in_history: 'replaced' });
191
+ });
192
+
193
+ it('sendSms', () => {
194
+ const r = new SwaigFunctionResult('sent').sendSms({
195
+ toNumber: '+1555',
196
+ fromNumber: '+1666',
197
+ body: 'hi',
198
+ });
199
+ const acts = r.toDict().action as Record<string, unknown>[];
200
+ const swml = acts[0]['SWML'] as Record<string, unknown>;
201
+ expect(swml['version']).toBe('1.0.0');
202
+ });
203
+
204
+ it('sendSms throws without body or media', () => {
205
+ expect(() => {
206
+ new SwaigFunctionResult().sendSms({ toNumber: '+1', fromNumber: '+2' });
207
+ }).toThrow('Either body or media must be provided');
208
+ });
209
+
210
+ it('recordCall / stopRecordCall', () => {
211
+ const r = new SwaigFunctionResult('ok')
212
+ .recordCall({ format: 'mp3', stereo: true })
213
+ .stopRecordCall('ctrl1');
214
+ expect((r.toDict().action as unknown[]).length).toBe(2);
215
+ });
216
+
217
+ it('tap / stopTap', () => {
218
+ const r = new SwaigFunctionResult('ok')
219
+ .tap({ uri: 'wss://example.com' })
220
+ .stopTap();
221
+ expect((r.toDict().action as unknown[]).length).toBe(2);
222
+ });
223
+
224
+ it('joinRoom', () => {
225
+ const r = new SwaigFunctionResult('ok').joinRoom('room1');
226
+ const acts = r.toDict().action as Record<string, unknown>[];
227
+ const swml = acts[0]['SWML'] as Record<string, unknown>;
228
+ const sections = swml['sections'] as Record<string, unknown[]>;
229
+ expect((sections['main'][0] as Record<string, unknown>)['join_room']).toEqual({ name: 'room1' });
230
+ });
231
+
232
+ it('sipRefer', () => {
233
+ const r = new SwaigFunctionResult('ok').sipRefer('sip:user@example.com');
234
+ const acts = r.toDict().action as Record<string, unknown>[];
235
+ const swml = acts[0]['SWML'] as Record<string, unknown>;
236
+ const sections = swml['sections'] as Record<string, unknown[]>;
237
+ expect((sections['main'][0] as Record<string, unknown>)['sip_refer']).toEqual({ to_uri: 'sip:user@example.com' });
238
+ });
239
+
240
+ it('joinConference simple', () => {
241
+ const r = new SwaigFunctionResult('ok').joinConference('conf1');
242
+ const acts = r.toDict().action as Record<string, unknown>[];
243
+ const swml = acts[0]['SWML'] as Record<string, unknown>;
244
+ const sections = swml['sections'] as Record<string, unknown[]>;
245
+ expect((sections['main'][0] as Record<string, unknown>)['join_conference']).toBe('conf1');
246
+ });
247
+
248
+ it('joinConference with options', () => {
249
+ const r = new SwaigFunctionResult('ok').joinConference('conf1', { muted: true });
250
+ const acts = r.toDict().action as Record<string, unknown>[];
251
+ const swml = acts[0]['SWML'] as Record<string, unknown>;
252
+ const sections = swml['sections'] as Record<string, unknown[]>;
253
+ const params = (sections['main'][0] as Record<string, unknown>)['join_conference'] as Record<string, unknown>;
254
+ expect(params['name']).toBe('conf1');
255
+ expect(params['muted']).toBe(true);
256
+ });
257
+
258
+ it('executeRpc', () => {
259
+ const r = new SwaigFunctionResult('ok').executeRpc({
260
+ method: 'ai_message',
261
+ callId: 'call-1',
262
+ params: { role: 'system', message_text: 'hi' },
263
+ });
264
+ expect((r.toDict().action as unknown[]).length).toBe(1);
265
+ });
266
+
267
+ it('rpcDial / rpcAiMessage / rpcAiUnhold', () => {
268
+ const r = new SwaigFunctionResult('ok')
269
+ .rpcDial('+1555', '+1666', 'https://example.com/swml')
270
+ .rpcAiMessage('call-1', 'hello')
271
+ .rpcAiUnhold('call-1');
272
+ expect((r.toDict().action as unknown[]).length).toBe(3);
273
+ });
274
+
275
+ it('executeSwml with string', () => {
276
+ const swml = JSON.stringify({ version: '1.0.0', sections: { main: [] } });
277
+ const r = new SwaigFunctionResult('ok').executeSwml(swml, true);
278
+ const acts = r.toDict().action as Record<string, unknown>[];
279
+ const action = acts[0]['SWML'] as Record<string, unknown>;
280
+ expect(action['transfer']).toBe('true');
281
+ });
282
+
283
+ it('static payment helpers', () => {
284
+ const action = SwaigFunctionResult.createPaymentAction('Say', 'Enter card');
285
+ expect(action).toEqual({ type: 'Say', phrase: 'Enter card' });
286
+
287
+ const prompt = SwaigFunctionResult.createPaymentPrompt('payment-card-number', [action]);
288
+ expect(prompt['for']).toBe('payment-card-number');
289
+
290
+ const param = SwaigFunctionResult.createPaymentParameter('k', 'v');
291
+ expect(param).toEqual({ name: 'k', value: 'v' });
292
+ });
293
+
294
+ it('setPostProcess', () => {
295
+ const r = new SwaigFunctionResult('hi').setPostProcess(true).hangup();
296
+ expect(r.toDict().post_process).toBe(true);
297
+ });
298
+ });
@@ -0,0 +1,94 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { generateFakePostData, generateMinimalPostData } from '../../src/cli/mock-data.js';
3
+
4
+ describe('mock-data', () => {
5
+ describe('generateFakePostData', () => {
6
+ it('generates data with defaults', () => {
7
+ const data = generateFakePostData();
8
+ expect(data.call_id).toBeDefined();
9
+ expect(data.call_type).toBe('webrtc');
10
+ expect(data.call_direction).toBe('inbound');
11
+ expect(data.caller_id_number).toBe('+15551234567');
12
+ expect(data.channel_type).toBe('web');
13
+ });
14
+
15
+ it('generates SIP call data', () => {
16
+ const data = generateFakePostData({ callType: 'sip' });
17
+ expect(data.call_type).toBe('sip');
18
+ expect(data.channel_type).toBe('phone');
19
+ expect(data.sip_headers).toBeDefined();
20
+ });
21
+
22
+ it('respects custom options', () => {
23
+ const data = generateFakePostData({
24
+ callType: 'sip',
25
+ callDirection: 'outbound',
26
+ fromNumber: '+18005551212',
27
+ toExtension: 'my-agent',
28
+ });
29
+ expect(data.call_type).toBe('sip');
30
+ expect(data.call_direction).toBe('outbound');
31
+ expect(data.from).toBe('+18005551212');
32
+ expect(data.to).toBe('my-agent');
33
+ });
34
+
35
+ it('generates unique call IDs', () => {
36
+ const data1 = generateFakePostData();
37
+ const data2 = generateFakePostData();
38
+ expect(data1.call_id).not.toBe(data2.call_id);
39
+ });
40
+
41
+ it('webrtc calls have no sip_headers', () => {
42
+ const data = generateFakePostData({ callType: 'webrtc' });
43
+ expect(data.sip_headers).toBeUndefined();
44
+ });
45
+ });
46
+
47
+ describe('generateMinimalPostData', () => {
48
+ it('generates minimal data for function execution', () => {
49
+ const data = generateMinimalPostData('get_time');
50
+ expect(data.function).toBe('get_time');
51
+ expect(data.argument).toEqual({});
52
+ expect(data.call_id).toBeDefined();
53
+ });
54
+
55
+ it('includes provided arguments', () => {
56
+ const data = generateMinimalPostData('search', { query: 'test' });
57
+ expect(data.function).toBe('search');
58
+ expect(data.argument).toEqual({ query: 'test' });
59
+ });
60
+
61
+ it('generates unique call IDs each time', () => {
62
+ const data1 = generateMinimalPostData('fn');
63
+ const data2 = generateMinimalPostData('fn');
64
+ expect(data1.call_id).not.toBe(data2.call_id);
65
+ });
66
+
67
+ it('accepts callId override', () => {
68
+ const data = generateMinimalPostData('fn', {}, { callId: 'my-call-id' });
69
+ expect(data.call_id).toBe('my-call-id');
70
+ });
71
+
72
+ it('accepts overrides', () => {
73
+ const data = generateMinimalPostData('fn', {}, { overrides: { custom_field: 'custom_value' } });
74
+ expect(data['custom_field']).toBe('custom_value');
75
+ });
76
+ });
77
+
78
+ describe('generateFakePostData - extended', () => {
79
+ it('includes call_state', () => {
80
+ const data = generateFakePostData({ callState: 'ringing' });
81
+ expect(data.call_state).toBe('ringing');
82
+ });
83
+
84
+ it('accepts callId override', () => {
85
+ const data = generateFakePostData({ callId: 'fixed-call-id' });
86
+ expect(data.call_id).toBe('fixed-call-id');
87
+ });
88
+
89
+ it('applies overrides to post data', () => {
90
+ const data = generateFakePostData({ overrides: { custom: 'value' } });
91
+ expect(data['custom']).toBe('value');
92
+ });
93
+ });
94
+ });
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { AgentBase } from '../../src/AgentBase.js';
3
+ import { SwaigFunctionResult } from '../../src/SwaigFunctionResult.js';
4
+
5
+ describe('agent introspection', () => {
6
+ function createAgent() {
7
+ const agent = new AgentBase({ name: 'test-agent', route: '/test' });
8
+ agent.setPromptText('Hello from test agent');
9
+ agent.defineTool({
10
+ name: 'greet',
11
+ description: 'Say hello',
12
+ parameters: { name: { type: 'string', description: 'Name' } },
13
+ handler: (args) => new SwaigFunctionResult('Hello ' + (args['name'] || 'world')),
14
+ });
15
+ agent.defineTool({
16
+ name: 'get_time',
17
+ description: 'Get current time',
18
+ parameters: {},
19
+ handler: () => new SwaigFunctionResult('12:00 PM'),
20
+ });
21
+ return agent;
22
+ }
23
+
24
+ it('getRegisteredTools returns all tool info', () => {
25
+ const agent = createAgent();
26
+ const tools = agent.getRegisteredTools();
27
+ expect(tools).toHaveLength(2);
28
+ expect(tools[0].name).toBe('greet');
29
+ expect(tools[0].description).toBe('Say hello');
30
+ expect(tools[0].parameters).toHaveProperty('name');
31
+ expect(tools[1].name).toBe('get_time');
32
+ });
33
+
34
+ it('getRegisteredTools returns empty for no tools', () => {
35
+ const agent = new AgentBase({ name: 'empty', route: '/test' });
36
+ expect(agent.getRegisteredTools()).toHaveLength(0);
37
+ });
38
+
39
+ it('getTool returns a specific SwaigFunction', () => {
40
+ const agent = createAgent();
41
+ const tool = agent.getTool('greet');
42
+ expect(tool).toBeDefined();
43
+ expect(tool!.name).toBe('greet');
44
+ expect(tool!.description).toBe('Say hello');
45
+ });
46
+
47
+ it('getTool returns undefined for missing tool', () => {
48
+ const agent = createAgent();
49
+ expect(agent.getTool('nonexistent')).toBeUndefined();
50
+ });
51
+
52
+ it('can execute tool via getTool', async () => {
53
+ const agent = createAgent();
54
+ const tool = agent.getTool('greet')!;
55
+ const result = await tool.execute({ name: 'Alice' }, {});
56
+ expect(result.response).toBe('Hello Alice');
57
+ });
58
+
59
+ it('can execute tool with no args', async () => {
60
+ const agent = createAgent();
61
+ const tool = agent.getTool('get_time')!;
62
+ const result = await tool.execute({}, {});
63
+ expect(result.response).toBe('12:00 PM');
64
+ });
65
+
66
+ it('renderSwml produces valid SWML with tools', () => {
67
+ const agent = createAgent();
68
+ const swml = agent.renderSwml('test-call-id');
69
+ const parsed = JSON.parse(swml);
70
+ expect(parsed.version).toBe('1.0.0');
71
+ expect(parsed.sections.main).toBeDefined();
72
+ const ai = parsed.sections.main[1].ai;
73
+ expect(ai.SWAIG.functions).toHaveLength(2);
74
+ });
75
+ });
76
+
77
+ describe('agent-loader duck typing', () => {
78
+ it('AgentBase instances pass duck-type check', () => {
79
+ const agent = new AgentBase({ name: 'test', route: '/test' });
80
+ // The duck-type check used by agent-loader
81
+ expect(typeof (agent as any).renderSwml).toBe('function');
82
+ expect(typeof (agent as any).defineTool).toBe('function');
83
+ expect(typeof (agent as any).getPrompt).toBe('function');
84
+ });
85
+
86
+ it('plain objects do not pass duck-type check', () => {
87
+ const obj = { renderSwml: 'not a function' };
88
+ expect(typeof obj.renderSwml).not.toBe('function');
89
+ });
90
+
91
+ it('AgentBase class prototype has required methods', () => {
92
+ expect(typeof AgentBase.prototype.renderSwml).toBe('function');
93
+ expect(typeof AgentBase.prototype.defineTool).toBe('function');
94
+ expect(typeof AgentBase.prototype.getPrompt).toBe('function');
95
+ });
96
+ });