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.
- package/CLAUDE.md +112 -0
- package/README.md +417 -0
- package/dist/AgentBase.d.ts +481 -0
- package/dist/AgentBase.js +1155 -0
- package/dist/AgentBase.js.map +1 -0
- package/dist/AgentServer.d.ts +60 -0
- package/dist/AgentServer.js +128 -0
- package/dist/AgentServer.js.map +1 -0
- package/dist/AuthHandler.d.ts +56 -0
- package/dist/AuthHandler.js +119 -0
- package/dist/AuthHandler.js.map +1 -0
- package/dist/ConfigLoader.d.ts +68 -0
- package/dist/ConfigLoader.js +150 -0
- package/dist/ConfigLoader.js.map +1 -0
- package/dist/ContextBuilder.d.ts +431 -0
- package/dist/ContextBuilder.js +751 -0
- package/dist/ContextBuilder.js.map +1 -0
- package/dist/DataMap.d.ts +179 -0
- package/dist/DataMap.js +321 -0
- package/dist/DataMap.js.map +1 -0
- package/dist/Logger.d.ts +91 -0
- package/dist/Logger.js +191 -0
- package/dist/Logger.js.map +1 -0
- package/dist/PomBuilder.d.ts +144 -0
- package/dist/PomBuilder.js +249 -0
- package/dist/PomBuilder.js.map +1 -0
- package/dist/PromptManager.d.ts +85 -0
- package/dist/PromptManager.js +108 -0
- package/dist/PromptManager.js.map +1 -0
- package/dist/SchemaUtils.d.ts +42 -0
- package/dist/SchemaUtils.js +148 -0
- package/dist/SchemaUtils.js.map +1 -0
- package/dist/ServerlessAdapter.d.ts +100 -0
- package/dist/ServerlessAdapter.js +166 -0
- package/dist/ServerlessAdapter.js.map +1 -0
- package/dist/SessionManager.d.ts +86 -0
- package/dist/SessionManager.js +155 -0
- package/dist/SessionManager.js.map +1 -0
- package/dist/SslConfig.d.ts +77 -0
- package/dist/SslConfig.js +100 -0
- package/dist/SslConfig.js.map +1 -0
- package/dist/SwaigFunction.d.ts +88 -0
- package/dist/SwaigFunction.js +125 -0
- package/dist/SwaigFunction.js.map +1 -0
- package/dist/SwaigFunctionResult.d.ts +442 -0
- package/dist/SwaigFunctionResult.js +735 -0
- package/dist/SwaigFunctionResult.js.map +1 -0
- package/dist/SwmlBuilder.d.ts +37 -0
- package/dist/SwmlBuilder.js +55 -0
- package/dist/SwmlBuilder.js.map +1 -0
- package/dist/cli/agent-loader.d.ts +22 -0
- package/dist/cli/agent-loader.js +106 -0
- package/dist/cli/agent-loader.js.map +1 -0
- package/dist/cli/mock-data.d.ts +37 -0
- package/dist/cli/mock-data.js +79 -0
- package/dist/cli/mock-data.js.map +1 -0
- package/dist/cli/swaig-test.d.ts +32 -0
- package/dist/cli/swaig-test.js +405 -0
- package/dist/cli/swaig-test.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/prefabs/ConciergeAgent.d.ts +64 -0
- package/dist/prefabs/ConciergeAgent.js +188 -0
- package/dist/prefabs/ConciergeAgent.js.map +1 -0
- package/dist/prefabs/FAQBotAgent.d.ts +68 -0
- package/dist/prefabs/FAQBotAgent.js +206 -0
- package/dist/prefabs/FAQBotAgent.js.map +1 -0
- package/dist/prefabs/InfoGathererAgent.d.ts +73 -0
- package/dist/prefabs/InfoGathererAgent.js +202 -0
- package/dist/prefabs/InfoGathererAgent.js.map +1 -0
- package/dist/prefabs/ReceptionistAgent.d.ts +65 -0
- package/dist/prefabs/ReceptionistAgent.js +205 -0
- package/dist/prefabs/ReceptionistAgent.js.map +1 -0
- package/dist/prefabs/SurveyAgent.d.ts +84 -0
- package/dist/prefabs/SurveyAgent.js +324 -0
- package/dist/prefabs/SurveyAgent.js.map +1 -0
- package/dist/prefabs/index.d.ts +13 -0
- package/dist/prefabs/index.js +14 -0
- package/dist/prefabs/index.js.map +1 -0
- package/dist/skills/SkillBase.d.ts +134 -0
- package/dist/skills/SkillBase.js +105 -0
- package/dist/skills/SkillBase.js.map +1 -0
- package/dist/skills/SkillManager.d.ts +83 -0
- package/dist/skills/SkillManager.js +159 -0
- package/dist/skills/SkillManager.js.map +1 -0
- package/dist/skills/SkillRegistry.d.ts +105 -0
- package/dist/skills/SkillRegistry.js +195 -0
- package/dist/skills/SkillRegistry.js.map +1 -0
- package/dist/skills/builtin/api_ninjas_trivia.d.ts +35 -0
- package/dist/skills/builtin/api_ninjas_trivia.js +161 -0
- package/dist/skills/builtin/api_ninjas_trivia.js.map +1 -0
- package/dist/skills/builtin/claude_skills.d.ts +34 -0
- package/dist/skills/builtin/claude_skills.js +156 -0
- package/dist/skills/builtin/claude_skills.js.map +1 -0
- package/dist/skills/builtin/custom_skills.d.ts +59 -0
- package/dist/skills/builtin/custom_skills.js +231 -0
- package/dist/skills/builtin/custom_skills.js.map +1 -0
- package/dist/skills/builtin/datasphere.d.ts +34 -0
- package/dist/skills/builtin/datasphere.js +163 -0
- package/dist/skills/builtin/datasphere.js.map +1 -0
- package/dist/skills/builtin/datasphere_serverless.d.ts +54 -0
- package/dist/skills/builtin/datasphere_serverless.js +155 -0
- package/dist/skills/builtin/datasphere_serverless.js.map +1 -0
- package/dist/skills/builtin/datetime.d.ts +31 -0
- package/dist/skills/builtin/datetime.js +95 -0
- package/dist/skills/builtin/datetime.js.map +1 -0
- package/dist/skills/builtin/google_maps.d.ts +33 -0
- package/dist/skills/builtin/google_maps.js +233 -0
- package/dist/skills/builtin/google_maps.js.map +1 -0
- package/dist/skills/builtin/index.d.ts +28 -0
- package/dist/skills/builtin/index.js +75 -0
- package/dist/skills/builtin/index.js.map +1 -0
- package/dist/skills/builtin/info_gatherer.d.ts +52 -0
- package/dist/skills/builtin/info_gatherer.js +237 -0
- package/dist/skills/builtin/info_gatherer.js.map +1 -0
- package/dist/skills/builtin/joke.d.ts +32 -0
- package/dist/skills/builtin/joke.js +142 -0
- package/dist/skills/builtin/joke.js.map +1 -0
- package/dist/skills/builtin/math.d.ts +33 -0
- package/dist/skills/builtin/math.js +120 -0
- package/dist/skills/builtin/math.js.map +1 -0
- package/dist/skills/builtin/mcp_gateway.d.ts +35 -0
- package/dist/skills/builtin/mcp_gateway.js +85 -0
- package/dist/skills/builtin/mcp_gateway.js.map +1 -0
- package/dist/skills/builtin/native_vector_search.d.ts +44 -0
- package/dist/skills/builtin/native_vector_search.js +248 -0
- package/dist/skills/builtin/native_vector_search.js.map +1 -0
- package/dist/skills/builtin/play_background_file.d.ts +34 -0
- package/dist/skills/builtin/play_background_file.js +135 -0
- package/dist/skills/builtin/play_background_file.js.map +1 -0
- package/dist/skills/builtin/spider.d.ts +34 -0
- package/dist/skills/builtin/spider.js +164 -0
- package/dist/skills/builtin/spider.js.map +1 -0
- package/dist/skills/builtin/swml_transfer.d.ts +44 -0
- package/dist/skills/builtin/swml_transfer.js +204 -0
- package/dist/skills/builtin/swml_transfer.js.map +1 -0
- package/dist/skills/builtin/weather_api.d.ts +34 -0
- package/dist/skills/builtin/weather_api.js +136 -0
- package/dist/skills/builtin/weather_api.js.map +1 -0
- package/dist/skills/builtin/web_search.d.ts +34 -0
- package/dist/skills/builtin/web_search.js +141 -0
- package/dist/skills/builtin/web_search.js.map +1 -0
- package/dist/skills/builtin/wikipedia_search.d.ts +34 -0
- package/dist/skills/builtin/wikipedia_search.js +166 -0
- package/dist/skills/builtin/wikipedia_search.js.map +1 -0
- package/dist/skills/index.d.ts +11 -0
- package/dist/skills/index.js +10 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/docs/agent-guide.md +869 -0
- package/docs/api-reference.md +2419 -0
- package/docs/architecture.md +843 -0
- package/docs/cli-guide.md +579 -0
- package/docs/configuration.md +422 -0
- package/docs/contexts-guide.md +787 -0
- package/docs/datamap-guide.md +1141 -0
- package/docs/prefabs-guide.md +830 -0
- package/docs/security.md +436 -0
- package/docs/serverless-guide.md +513 -0
- package/docs/skills-guide.md +845 -0
- package/docs/swaig-reference.md +1627 -0
- package/examples/call-flow.ts +60 -0
- package/examples/contexts-steps.ts +62 -0
- package/examples/datamap-tools.ts +52 -0
- package/examples/dynamic-config.ts +51 -0
- package/examples/multi-agent.ts +67 -0
- package/examples/pom-prompt.ts +68 -0
- package/examples/simple-agent.ts +33 -0
- package/package.json +35 -0
- package/src/AgentBase.ts +1279 -0
- package/src/AgentServer.ts +149 -0
- package/src/AuthHandler.ts +141 -0
- package/src/ConfigLoader.ts +165 -0
- package/src/ContextBuilder.ts +796 -0
- package/src/DataMap.ts +365 -0
- package/src/Logger.ts +197 -0
- package/src/PomBuilder.ts +298 -0
- package/src/PromptManager.ts +133 -0
- package/src/SchemaUtils.ts +170 -0
- package/src/ServerlessAdapter.ts +218 -0
- package/src/SessionManager.ts +161 -0
- package/src/SslConfig.ts +118 -0
- package/src/SwaigFunction.ts +168 -0
- package/src/SwaigFunctionResult.ts +864 -0
- package/src/SwmlBuilder.ts +62 -0
- package/src/cli/agent-loader.ts +122 -0
- package/src/cli/mock-data.ts +110 -0
- package/src/cli/swaig-test.ts +440 -0
- package/src/index.ts +99 -0
- package/src/prefabs/ConciergeAgent.ts +251 -0
- package/src/prefabs/FAQBotAgent.ts +263 -0
- package/src/prefabs/InfoGathererAgent.ts +276 -0
- package/src/prefabs/ReceptionistAgent.ts +277 -0
- package/src/prefabs/SurveyAgent.ts +413 -0
- package/src/prefabs/index.ts +23 -0
- package/src/skills/SkillBase.ts +184 -0
- package/src/skills/SkillManager.ts +177 -0
- package/src/skills/SkillRegistry.ts +231 -0
- package/src/skills/builtin/api_ninjas_trivia.ts +230 -0
- package/src/skills/builtin/claude_skills.ts +239 -0
- package/src/skills/builtin/custom_skills.ts +326 -0
- package/src/skills/builtin/datasphere.ts +225 -0
- package/src/skills/builtin/datasphere_serverless.ts +194 -0
- package/src/skills/builtin/datetime.ts +115 -0
- package/src/skills/builtin/google_maps.ts +347 -0
- package/src/skills/builtin/index.ts +79 -0
- package/src/skills/builtin/info_gatherer.ts +331 -0
- package/src/skills/builtin/joke.ts +180 -0
- package/src/skills/builtin/math.ts +150 -0
- package/src/skills/builtin/mcp_gateway.ts +99 -0
- package/src/skills/builtin/native_vector_search.ts +327 -0
- package/src/skills/builtin/play_background_file.ts +189 -0
- package/src/skills/builtin/spider.ts +232 -0
- package/src/skills/builtin/swml_transfer.ts +285 -0
- package/src/skills/builtin/weather_api.ts +193 -0
- package/src/skills/builtin/web_search.ts +212 -0
- package/src/skills/builtin/wikipedia_search.ts +248 -0
- package/src/skills/index.ts +12 -0
- package/src/types.ts +99 -0
- package/tests/AgentBase.test.ts +693 -0
- package/tests/AgentServer.test.ts +102 -0
- package/tests/AuthHandler.test.ts +121 -0
- package/tests/ConfigLoader.test.ts +121 -0
- package/tests/DataMap.test.ts +286 -0
- package/tests/Logger.test.ts +218 -0
- package/tests/PomBuilder.test.ts +164 -0
- package/tests/ProxyDetection.test.ts +185 -0
- package/tests/SchemaUtils.test.ts +106 -0
- package/tests/ServerlessAdapter.test.ts +200 -0
- package/tests/SessionManager.test.ts +139 -0
- package/tests/SslConfig.test.ts +97 -0
- package/tests/SwaigFunctionResult.test.ts +298 -0
- package/tests/cli/mock-data.test.ts +94 -0
- package/tests/cli/swaig-test.test.ts +96 -0
- package/tests/prefabs/prefabs.test.ts +854 -0
- package/tests/setup.ts +6 -0
- package/tests/skills/SkillBase.test.ts +118 -0
- package/tests/skills/SkillManager.test.ts +164 -0
- package/tests/skills/SkillRegistry.test.ts +124 -0
- package/tests/skills/builtin.test.ts +924 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +9 -0
|
@@ -0,0 +1,924 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Comprehensive tests for all 18 built-in skills.
|
|
3
|
+
*
|
|
4
|
+
* Covers instantiation, manifest, tools, prompt sections, and handler execution
|
|
5
|
+
* for each skill. API-dependent skills verify error messages when keys are missing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, expect, beforeAll } from 'vitest';
|
|
9
|
+
import {
|
|
10
|
+
DateTimeSkill,
|
|
11
|
+
createDateTimeSkill,
|
|
12
|
+
MathSkill,
|
|
13
|
+
createMathSkill,
|
|
14
|
+
JokeSkill,
|
|
15
|
+
createJokeSkill,
|
|
16
|
+
WeatherApiSkill,
|
|
17
|
+
createWeatherApiSkill,
|
|
18
|
+
PlayBackgroundFileSkill,
|
|
19
|
+
createPlayBackgroundFileSkill,
|
|
20
|
+
SwmlTransferSkill,
|
|
21
|
+
createSwmlTransferSkill,
|
|
22
|
+
ApiNinjasTriviaSkill,
|
|
23
|
+
createApiNinjasTriviaSkill,
|
|
24
|
+
InfoGathererSkill,
|
|
25
|
+
createInfoGathererSkill,
|
|
26
|
+
CustomSkillsSkill,
|
|
27
|
+
createCustomSkillsSkill,
|
|
28
|
+
WebSearchSkill,
|
|
29
|
+
createWebSearchSkill,
|
|
30
|
+
WikipediaSearchSkill,
|
|
31
|
+
createWikipediaSearchSkill,
|
|
32
|
+
GoogleMapsSkill,
|
|
33
|
+
createGoogleMapsSkill,
|
|
34
|
+
DataSphereSkill,
|
|
35
|
+
createDataSphereSkill,
|
|
36
|
+
DataSphereServerlessSkill,
|
|
37
|
+
createDataSphereServerlessSkill,
|
|
38
|
+
NativeVectorSearchSkill,
|
|
39
|
+
createNativeVectorSearchSkill,
|
|
40
|
+
SpiderSkill,
|
|
41
|
+
createSpiderSkill,
|
|
42
|
+
ClaudeSkill,
|
|
43
|
+
createClaudeSkill,
|
|
44
|
+
McpGatewaySkill,
|
|
45
|
+
createMcpGatewaySkill,
|
|
46
|
+
} from '../../src/skills/builtin/index.js';
|
|
47
|
+
import { SkillBase } from '../../src/skills/SkillBase.js';
|
|
48
|
+
import { SwaigFunctionResult } from '../../src/SwaigFunctionResult.js';
|
|
49
|
+
import { suppressAllLogs } from '../../src/Logger.js';
|
|
50
|
+
|
|
51
|
+
beforeAll(() => {
|
|
52
|
+
suppressAllLogs(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// DateTime Skill
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
describe('DateTimeSkill', () => {
|
|
59
|
+
it('should instantiate via createSkill factory', () => {
|
|
60
|
+
const skill = createDateTimeSkill();
|
|
61
|
+
expect(skill).toBeInstanceOf(DateTimeSkill);
|
|
62
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should return correct manifest name and version', () => {
|
|
66
|
+
const skill = createDateTimeSkill();
|
|
67
|
+
const manifest = skill.getManifest();
|
|
68
|
+
expect(manifest.name).toBe('datetime');
|
|
69
|
+
expect(manifest.version).toBe('1.0.0');
|
|
70
|
+
expect(manifest.description).toBeTruthy();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should return a get_datetime tool', () => {
|
|
74
|
+
const skill = createDateTimeSkill();
|
|
75
|
+
const tools = skill.getTools();
|
|
76
|
+
expect(tools).toHaveLength(1);
|
|
77
|
+
expect(tools[0].name).toBe('get_datetime');
|
|
78
|
+
expect(tools[0].handler).toBeTypeOf('function');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should return non-empty prompt sections', () => {
|
|
82
|
+
const skill = createDateTimeSkill();
|
|
83
|
+
const sections = skill.getPromptSections();
|
|
84
|
+
expect(sections.length).toBeGreaterThan(0);
|
|
85
|
+
expect(sections[0].title).toBe('Date and Time');
|
|
86
|
+
expect(sections[0].bullets).toBeDefined();
|
|
87
|
+
expect(sections[0].bullets!.length).toBeGreaterThan(0);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should execute handler and return datetime for a timezone', () => {
|
|
91
|
+
const skill = createDateTimeSkill();
|
|
92
|
+
const handler = skill.getTools()[0].handler;
|
|
93
|
+
const result = handler({ timezone: 'America/New_York' }, {}) as SwaigFunctionResult;
|
|
94
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
95
|
+
expect(result.response).toContain('America/New_York');
|
|
96
|
+
expect(result.response).toContain('current date and time');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Math Skill
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
describe('MathSkill', () => {
|
|
104
|
+
it('should instantiate via createSkill factory', () => {
|
|
105
|
+
const skill = createMathSkill();
|
|
106
|
+
expect(skill).toBeInstanceOf(MathSkill);
|
|
107
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should return correct manifest name and version', () => {
|
|
111
|
+
const skill = createMathSkill();
|
|
112
|
+
const manifest = skill.getManifest();
|
|
113
|
+
expect(manifest.name).toBe('math');
|
|
114
|
+
expect(manifest.version).toBe('1.0.0');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should return a calculate tool', () => {
|
|
118
|
+
const skill = createMathSkill();
|
|
119
|
+
const tools = skill.getTools();
|
|
120
|
+
expect(tools).toHaveLength(1);
|
|
121
|
+
expect(tools[0].name).toBe('calculate');
|
|
122
|
+
expect(tools[0].required).toContain('expression');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should return non-empty prompt sections', () => {
|
|
126
|
+
const skill = createMathSkill();
|
|
127
|
+
const sections = skill.getPromptSections();
|
|
128
|
+
expect(sections.length).toBeGreaterThan(0);
|
|
129
|
+
expect(sections[0].title).toBe('Mathematical Calculations');
|
|
130
|
+
expect(sections[0].bullets).toBeDefined();
|
|
131
|
+
expect(sections[0].bullets!.length).toBeGreaterThan(0);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should evaluate basic math expressions correctly', () => {
|
|
135
|
+
const skill = createMathSkill();
|
|
136
|
+
const handler = skill.getTools()[0].handler;
|
|
137
|
+
const result = handler({ expression: '2 + 3 * 4' }, {}) as SwaigFunctionResult;
|
|
138
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
139
|
+
expect(result.response).toContain('14');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('should reject unsafe expressions', () => {
|
|
143
|
+
const skill = createMathSkill();
|
|
144
|
+
const handler = skill.getTools()[0].handler;
|
|
145
|
+
const result = handler({ expression: 'process.exit(1)' }, {}) as SwaigFunctionResult;
|
|
146
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
147
|
+
expect(result.response).toContain('invalid characters');
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
// Joke Skill
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
describe('JokeSkill', () => {
|
|
155
|
+
it('should instantiate via createSkill factory', () => {
|
|
156
|
+
const skill = createJokeSkill();
|
|
157
|
+
expect(skill).toBeInstanceOf(JokeSkill);
|
|
158
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should return correct manifest name and version', () => {
|
|
162
|
+
const skill = createJokeSkill();
|
|
163
|
+
const manifest = skill.getManifest();
|
|
164
|
+
expect(manifest.name).toBe('joke');
|
|
165
|
+
expect(manifest.version).toBe('1.0.0');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should return a tell_joke tool', () => {
|
|
169
|
+
const skill = createJokeSkill();
|
|
170
|
+
const tools = skill.getTools();
|
|
171
|
+
expect(tools).toHaveLength(1);
|
|
172
|
+
expect(tools[0].name).toBe('tell_joke');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('should return non-empty prompt sections', () => {
|
|
176
|
+
const skill = createJokeSkill();
|
|
177
|
+
const sections = skill.getPromptSections();
|
|
178
|
+
expect(sections.length).toBeGreaterThan(0);
|
|
179
|
+
expect(sections[0].title).toBe('Jokes');
|
|
180
|
+
expect(sections[0].bullets).toBeDefined();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should execute handler and return a joke', () => {
|
|
184
|
+
const skill = createJokeSkill();
|
|
185
|
+
const handler = skill.getTools()[0].handler;
|
|
186
|
+
const result = handler({}, {}) as SwaigFunctionResult;
|
|
187
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
188
|
+
// Jokes contain " ... " between setup and punchline
|
|
189
|
+
expect(result.response).toContain('...');
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
// Weather API Skill
|
|
195
|
+
// ---------------------------------------------------------------------------
|
|
196
|
+
describe('WeatherApiSkill', () => {
|
|
197
|
+
it('should instantiate via createSkill factory', () => {
|
|
198
|
+
const skill = createWeatherApiSkill();
|
|
199
|
+
expect(skill).toBeInstanceOf(WeatherApiSkill);
|
|
200
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should return correct manifest name and version', () => {
|
|
204
|
+
const skill = createWeatherApiSkill();
|
|
205
|
+
const manifest = skill.getManifest();
|
|
206
|
+
expect(manifest.name).toBe('weather_api');
|
|
207
|
+
expect(manifest.version).toBe('1.0.0');
|
|
208
|
+
expect(manifest.requiredEnvVars).toContain('WEATHER_API_KEY');
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('should return a get_weather tool', () => {
|
|
212
|
+
const skill = createWeatherApiSkill();
|
|
213
|
+
const tools = skill.getTools();
|
|
214
|
+
expect(tools).toHaveLength(1);
|
|
215
|
+
expect(tools[0].name).toBe('get_weather');
|
|
216
|
+
expect(tools[0].required).toContain('location');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should report missing env var via validateEnvVars', () => {
|
|
220
|
+
const originalKey = process.env['WEATHER_API_KEY'];
|
|
221
|
+
delete process.env['WEATHER_API_KEY'];
|
|
222
|
+
const skill = createWeatherApiSkill();
|
|
223
|
+
const missing = skill.validateEnvVars();
|
|
224
|
+
expect(missing).toContain('WEATHER_API_KEY');
|
|
225
|
+
if (originalKey !== undefined) process.env['WEATHER_API_KEY'] = originalKey;
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('should return error when API key is not set', async () => {
|
|
229
|
+
const originalKey = process.env['WEATHER_API_KEY'];
|
|
230
|
+
delete process.env['WEATHER_API_KEY'];
|
|
231
|
+
const skill = createWeatherApiSkill();
|
|
232
|
+
const handler = skill.getTools()[0].handler;
|
|
233
|
+
const result = await handler({ location: 'London' }, {}) as SwaigFunctionResult;
|
|
234
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
235
|
+
expect(result.response).toContain('WEATHER_API_KEY');
|
|
236
|
+
expect(result.response).toContain('not configured');
|
|
237
|
+
if (originalKey !== undefined) process.env['WEATHER_API_KEY'] = originalKey;
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
// Play Background File Skill
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
describe('PlayBackgroundFileSkill', () => {
|
|
245
|
+
it('should instantiate via createSkill factory', () => {
|
|
246
|
+
const skill = createPlayBackgroundFileSkill();
|
|
247
|
+
expect(skill).toBeInstanceOf(PlayBackgroundFileSkill);
|
|
248
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should return correct manifest name and version', () => {
|
|
252
|
+
const skill = createPlayBackgroundFileSkill();
|
|
253
|
+
const manifest = skill.getManifest();
|
|
254
|
+
expect(manifest.name).toBe('play_background_file');
|
|
255
|
+
expect(manifest.version).toBe('1.0.0');
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should return play_background and stop_background tools', () => {
|
|
259
|
+
const skill = createPlayBackgroundFileSkill();
|
|
260
|
+
const tools = skill.getTools();
|
|
261
|
+
expect(tools).toHaveLength(2);
|
|
262
|
+
const names = tools.map((t) => t.name);
|
|
263
|
+
expect(names).toContain('play_background');
|
|
264
|
+
expect(names).toContain('stop_background');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should execute play_background handler with a valid URL', () => {
|
|
268
|
+
const skill = createPlayBackgroundFileSkill();
|
|
269
|
+
const playTool = skill.getTools().find((t) => t.name === 'play_background')!;
|
|
270
|
+
const result = playTool.handler(
|
|
271
|
+
{ file_url: 'https://example.com/music.mp3' },
|
|
272
|
+
{},
|
|
273
|
+
) as SwaigFunctionResult;
|
|
274
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
275
|
+
expect(result.response).toContain('playing background audio');
|
|
276
|
+
// Should have a playback_bg action
|
|
277
|
+
expect(result.action.length).toBeGreaterThan(0);
|
|
278
|
+
expect(result.action[0]).toHaveProperty('playback_bg');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should execute stop_background handler', () => {
|
|
282
|
+
const skill = createPlayBackgroundFileSkill();
|
|
283
|
+
const stopTool = skill.getTools().find((t) => t.name === 'stop_background')!;
|
|
284
|
+
const result = stopTool.handler({}, {}) as SwaigFunctionResult;
|
|
285
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
286
|
+
expect(result.response).toContain('stopped');
|
|
287
|
+
expect(result.action.length).toBeGreaterThan(0);
|
|
288
|
+
expect(result.action[0]).toHaveProperty('stop_playback_bg');
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// ---------------------------------------------------------------------------
|
|
293
|
+
// SWML Transfer Skill
|
|
294
|
+
// ---------------------------------------------------------------------------
|
|
295
|
+
describe('SwmlTransferSkill', () => {
|
|
296
|
+
it('should instantiate via createSkill factory', () => {
|
|
297
|
+
const skill = createSwmlTransferSkill();
|
|
298
|
+
expect(skill).toBeInstanceOf(SwmlTransferSkill);
|
|
299
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it('should return correct manifest name and version', () => {
|
|
303
|
+
const skill = createSwmlTransferSkill();
|
|
304
|
+
const manifest = skill.getManifest();
|
|
305
|
+
expect(manifest.name).toBe('swml_transfer');
|
|
306
|
+
expect(manifest.version).toBe('1.0.0');
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should return a transfer_call tool', () => {
|
|
310
|
+
const skill = createSwmlTransferSkill();
|
|
311
|
+
const tools = skill.getTools();
|
|
312
|
+
expect(tools.length).toBeGreaterThanOrEqual(1);
|
|
313
|
+
expect(tools[0].name).toBe('transfer_call');
|
|
314
|
+
expect(tools[0].required).toContain('destination');
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should execute transfer with arbitrary destination', () => {
|
|
318
|
+
const skill = createSwmlTransferSkill();
|
|
319
|
+
const handler = skill.getTools()[0].handler;
|
|
320
|
+
const result = handler({ destination: '+15551234567' }, {}) as SwaigFunctionResult;
|
|
321
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
322
|
+
expect(result.response).toBe('Transferring your call now.');
|
|
323
|
+
// Should have a SWML transfer action
|
|
324
|
+
expect(result.action.length).toBeGreaterThan(0);
|
|
325
|
+
const swmlAction = result.action[0];
|
|
326
|
+
expect(swmlAction).toHaveProperty('SWML');
|
|
327
|
+
expect(swmlAction).toHaveProperty('transfer', 'true');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('should support named transfer patterns', () => {
|
|
331
|
+
const skill = createSwmlTransferSkill({
|
|
332
|
+
patterns: [
|
|
333
|
+
{ name: 'sales', destination: 'sip:sales@example.com', description: 'Sales team' },
|
|
334
|
+
{ name: 'support', destination: 'sip:support@example.com', description: 'Support team' },
|
|
335
|
+
],
|
|
336
|
+
});
|
|
337
|
+
const tools = skill.getTools();
|
|
338
|
+
// Should have transfer_call + list_transfer_destinations
|
|
339
|
+
expect(tools.length).toBe(2);
|
|
340
|
+
expect(tools[1].name).toBe('list_transfer_destinations');
|
|
341
|
+
|
|
342
|
+
// Transfer by name
|
|
343
|
+
const handler = tools[0].handler;
|
|
344
|
+
const result = handler({ destination: 'sales' }, {}) as SwaigFunctionResult;
|
|
345
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
346
|
+
expect(result.action.length).toBeGreaterThan(0);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('should reject unknown named destinations when patterns configured and arbitrary disallowed', () => {
|
|
350
|
+
const skill = createSwmlTransferSkill({
|
|
351
|
+
patterns: [
|
|
352
|
+
{ name: 'sales', destination: 'sip:sales@example.com' },
|
|
353
|
+
],
|
|
354
|
+
allow_arbitrary: false,
|
|
355
|
+
});
|
|
356
|
+
const handler = skill.getTools()[0].handler;
|
|
357
|
+
const result = handler({ destination: 'unknown_dept' }, {}) as SwaigFunctionResult;
|
|
358
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
359
|
+
expect(result.response).toContain('Unknown transfer destination');
|
|
360
|
+
expect(result.response).toContain('sales');
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
// API Ninjas Trivia Skill
|
|
366
|
+
// ---------------------------------------------------------------------------
|
|
367
|
+
describe('ApiNinjasTriviaSkill', () => {
|
|
368
|
+
it('should instantiate via createSkill factory', () => {
|
|
369
|
+
const skill = createApiNinjasTriviaSkill();
|
|
370
|
+
expect(skill).toBeInstanceOf(ApiNinjasTriviaSkill);
|
|
371
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('should return correct manifest name and version', () => {
|
|
375
|
+
const skill = createApiNinjasTriviaSkill();
|
|
376
|
+
const manifest = skill.getManifest();
|
|
377
|
+
expect(manifest.name).toBe('api_ninjas_trivia');
|
|
378
|
+
expect(manifest.version).toBe('1.0.0');
|
|
379
|
+
expect(manifest.requiredEnvVars).toContain('API_NINJAS_KEY');
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('should return a get_trivia tool', () => {
|
|
383
|
+
const skill = createApiNinjasTriviaSkill();
|
|
384
|
+
const tools = skill.getTools();
|
|
385
|
+
expect(tools).toHaveLength(1);
|
|
386
|
+
expect(tools[0].name).toBe('get_trivia');
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
it('should return hints for speech recognition', () => {
|
|
390
|
+
const skill = createApiNinjasTriviaSkill();
|
|
391
|
+
const hints = skill.getHints();
|
|
392
|
+
expect(hints.length).toBeGreaterThan(0);
|
|
393
|
+
expect(hints).toContain('trivia');
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it('should return error when API key is not set', async () => {
|
|
397
|
+
const originalKey = process.env['API_NINJAS_KEY'];
|
|
398
|
+
delete process.env['API_NINJAS_KEY'];
|
|
399
|
+
const skill = createApiNinjasTriviaSkill();
|
|
400
|
+
const handler = skill.getTools()[0].handler;
|
|
401
|
+
const result = await handler({}, {}) as SwaigFunctionResult;
|
|
402
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
403
|
+
expect(result.response).toContain('API_NINJAS_KEY');
|
|
404
|
+
expect(result.response).toContain('not configured');
|
|
405
|
+
if (originalKey !== undefined) process.env['API_NINJAS_KEY'] = originalKey;
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// ---------------------------------------------------------------------------
|
|
410
|
+
// Info Gatherer Skill
|
|
411
|
+
// ---------------------------------------------------------------------------
|
|
412
|
+
describe('InfoGathererSkill', () => {
|
|
413
|
+
it('should instantiate with fields config via createSkill factory', () => {
|
|
414
|
+
const skill = createInfoGathererSkill({
|
|
415
|
+
fields: [
|
|
416
|
+
{ name: 'full_name', description: 'Full name', required: true },
|
|
417
|
+
{ name: 'email', description: 'Email address' },
|
|
418
|
+
],
|
|
419
|
+
});
|
|
420
|
+
expect(skill).toBeInstanceOf(InfoGathererSkill);
|
|
421
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
it('should return correct manifest name and version', () => {
|
|
425
|
+
const skill = createInfoGathererSkill();
|
|
426
|
+
const manifest = skill.getManifest();
|
|
427
|
+
expect(manifest.name).toBe('info_gatherer');
|
|
428
|
+
expect(manifest.version).toBe('1.0.0');
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('should return save_info and get_gathered_info tools when fields configured', () => {
|
|
432
|
+
const skill = createInfoGathererSkill({
|
|
433
|
+
fields: [
|
|
434
|
+
{ name: 'full_name', description: 'Full name', required: true },
|
|
435
|
+
],
|
|
436
|
+
});
|
|
437
|
+
const tools = skill.getTools();
|
|
438
|
+
expect(tools).toHaveLength(2);
|
|
439
|
+
const names = tools.map((t) => t.name);
|
|
440
|
+
expect(names).toContain('save_info');
|
|
441
|
+
expect(names).toContain('get_gathered_info');
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('should return no tools when no fields configured', () => {
|
|
445
|
+
const skill = createInfoGathererSkill();
|
|
446
|
+
const tools = skill.getTools();
|
|
447
|
+
expect(tools).toHaveLength(0);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it('should execute save_info handler and save data', () => {
|
|
451
|
+
const skill = createInfoGathererSkill({
|
|
452
|
+
fields: [
|
|
453
|
+
{ name: 'full_name', description: 'Full name', required: true },
|
|
454
|
+
{ name: 'email', description: 'Email address' },
|
|
455
|
+
],
|
|
456
|
+
});
|
|
457
|
+
const saveTool = skill.getTools().find((t) => t.name === 'save_info')!;
|
|
458
|
+
const result = saveTool.handler(
|
|
459
|
+
{ full_name: 'Jane Doe', email: 'jane@example.com' },
|
|
460
|
+
{ call_id: 'test-call-123' },
|
|
461
|
+
) as SwaigFunctionResult;
|
|
462
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
463
|
+
expect(result.response).toContain('saved successfully');
|
|
464
|
+
expect(result.response).toContain('Jane Doe');
|
|
465
|
+
expect(result.response).toContain('jane@example.com');
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
// ---------------------------------------------------------------------------
|
|
470
|
+
// Custom Skills Skill
|
|
471
|
+
// ---------------------------------------------------------------------------
|
|
472
|
+
describe('CustomSkillsSkill', () => {
|
|
473
|
+
it('should instantiate with tools config via createSkill factory', () => {
|
|
474
|
+
const skill = createCustomSkillsSkill({
|
|
475
|
+
tools: [
|
|
476
|
+
{
|
|
477
|
+
name: 'greet',
|
|
478
|
+
description: 'Greet a user',
|
|
479
|
+
handler_code: 'return new SwaigFunctionResult("Hello, " + args.name + "!");',
|
|
480
|
+
parameters: [{ name: 'name', type: 'string', description: 'Name to greet' }],
|
|
481
|
+
},
|
|
482
|
+
],
|
|
483
|
+
});
|
|
484
|
+
expect(skill).toBeInstanceOf(CustomSkillsSkill);
|
|
485
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
it('should return correct manifest name and version', () => {
|
|
489
|
+
const skill = createCustomSkillsSkill();
|
|
490
|
+
const manifest = skill.getManifest();
|
|
491
|
+
expect(manifest.name).toBe('custom_skills');
|
|
492
|
+
expect(manifest.version).toBe('1.0.0');
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
it('should register dynamic tools from config', () => {
|
|
496
|
+
const skill = createCustomSkillsSkill({
|
|
497
|
+
tools: [
|
|
498
|
+
{
|
|
499
|
+
name: 'greet',
|
|
500
|
+
description: 'Greet a user',
|
|
501
|
+
handler_code: 'return new SwaigFunctionResult("Hello!");',
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: 'farewell',
|
|
505
|
+
description: 'Say goodbye',
|
|
506
|
+
handler_code: 'return new SwaigFunctionResult("Goodbye!");',
|
|
507
|
+
},
|
|
508
|
+
],
|
|
509
|
+
});
|
|
510
|
+
const tools = skill.getTools();
|
|
511
|
+
expect(tools).toHaveLength(2);
|
|
512
|
+
expect(tools[0].name).toBe('greet');
|
|
513
|
+
expect(tools[1].name).toBe('farewell');
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
it('should execute custom tool handler', async () => {
|
|
517
|
+
const skill = createCustomSkillsSkill({
|
|
518
|
+
tools: [
|
|
519
|
+
{
|
|
520
|
+
name: 'greet',
|
|
521
|
+
description: 'Greet a user',
|
|
522
|
+
handler_code: 'return new SwaigFunctionResult("Hello, " + args.name + "!");',
|
|
523
|
+
parameters: [{ name: 'name', type: 'string', description: 'Name to greet' }],
|
|
524
|
+
},
|
|
525
|
+
],
|
|
526
|
+
});
|
|
527
|
+
const handler = skill.getTools()[0].handler;
|
|
528
|
+
const result = await handler({ name: 'World' }, {}) as SwaigFunctionResult;
|
|
529
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
530
|
+
expect(result.response).toBe('Hello, World!');
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
// ---------------------------------------------------------------------------
|
|
535
|
+
// Web Search Skill
|
|
536
|
+
// ---------------------------------------------------------------------------
|
|
537
|
+
describe('WebSearchSkill', () => {
|
|
538
|
+
it('should instantiate via createSkill factory', () => {
|
|
539
|
+
const skill = createWebSearchSkill();
|
|
540
|
+
expect(skill).toBeInstanceOf(WebSearchSkill);
|
|
541
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
it('should return correct manifest name and version', () => {
|
|
545
|
+
const skill = createWebSearchSkill();
|
|
546
|
+
const manifest = skill.getManifest();
|
|
547
|
+
expect(manifest.name).toBe('web_search');
|
|
548
|
+
expect(manifest.version).toBe('1.0.0');
|
|
549
|
+
expect(manifest.requiredEnvVars).toContain('GOOGLE_SEARCH_API_KEY');
|
|
550
|
+
expect(manifest.requiredEnvVars).toContain('GOOGLE_SEARCH_CX');
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it('should return a web_search tool', () => {
|
|
554
|
+
const skill = createWebSearchSkill();
|
|
555
|
+
const tools = skill.getTools();
|
|
556
|
+
expect(tools).toHaveLength(1);
|
|
557
|
+
expect(tools[0].name).toBe('web_search');
|
|
558
|
+
expect(tools[0].required).toContain('query');
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it('should return non-empty prompt sections', () => {
|
|
562
|
+
const skill = createWebSearchSkill();
|
|
563
|
+
const sections = skill.getPromptSections();
|
|
564
|
+
expect(sections.length).toBeGreaterThan(0);
|
|
565
|
+
expect(sections[0].title).toBe('Web Search');
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('should return error when API keys are not set', async () => {
|
|
569
|
+
const origKey = process.env['GOOGLE_SEARCH_API_KEY'];
|
|
570
|
+
const origCx = process.env['GOOGLE_SEARCH_CX'];
|
|
571
|
+
delete process.env['GOOGLE_SEARCH_API_KEY'];
|
|
572
|
+
delete process.env['GOOGLE_SEARCH_CX'];
|
|
573
|
+
const skill = createWebSearchSkill();
|
|
574
|
+
const handler = skill.getTools()[0].handler;
|
|
575
|
+
const result = await handler({ query: 'test' }, {}) as SwaigFunctionResult;
|
|
576
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
577
|
+
expect(result.response).toContain('GOOGLE_SEARCH_API_KEY');
|
|
578
|
+
expect(result.response).toContain('not configured');
|
|
579
|
+
if (origKey !== undefined) process.env['GOOGLE_SEARCH_API_KEY'] = origKey;
|
|
580
|
+
if (origCx !== undefined) process.env['GOOGLE_SEARCH_CX'] = origCx;
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
|
|
584
|
+
// ---------------------------------------------------------------------------
|
|
585
|
+
// Wikipedia Search Skill
|
|
586
|
+
// ---------------------------------------------------------------------------
|
|
587
|
+
describe('WikipediaSearchSkill', () => {
|
|
588
|
+
it('should instantiate via createSkill factory', () => {
|
|
589
|
+
const skill = createWikipediaSearchSkill();
|
|
590
|
+
expect(skill).toBeInstanceOf(WikipediaSearchSkill);
|
|
591
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('should return correct manifest name and version', () => {
|
|
595
|
+
const skill = createWikipediaSearchSkill();
|
|
596
|
+
const manifest = skill.getManifest();
|
|
597
|
+
expect(manifest.name).toBe('wikipedia_search');
|
|
598
|
+
expect(manifest.version).toBe('1.0.0');
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it('should return a search_wikipedia tool', () => {
|
|
602
|
+
const skill = createWikipediaSearchSkill();
|
|
603
|
+
const tools = skill.getTools();
|
|
604
|
+
expect(tools).toHaveLength(1);
|
|
605
|
+
expect(tools[0].name).toBe('search_wikipedia');
|
|
606
|
+
expect(tools[0].required).toContain('query');
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('should return non-empty prompt sections', () => {
|
|
610
|
+
const skill = createWikipediaSearchSkill();
|
|
611
|
+
const sections = skill.getPromptSections();
|
|
612
|
+
expect(sections.length).toBeGreaterThan(0);
|
|
613
|
+
expect(sections[0].title).toBe('Wikipedia Search');
|
|
614
|
+
expect(sections[0].bullets).toBeDefined();
|
|
615
|
+
expect(sections[0].bullets!.length).toBeGreaterThan(0);
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
it('should have no requiredEnvVars (free API)', () => {
|
|
619
|
+
const skill = createWikipediaSearchSkill();
|
|
620
|
+
const manifest = skill.getManifest();
|
|
621
|
+
expect(manifest.requiredEnvVars).toBeUndefined();
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
// ---------------------------------------------------------------------------
|
|
626
|
+
// Google Maps Skill
|
|
627
|
+
// ---------------------------------------------------------------------------
|
|
628
|
+
describe('GoogleMapsSkill', () => {
|
|
629
|
+
it('should instantiate via createSkill factory', () => {
|
|
630
|
+
const skill = createGoogleMapsSkill();
|
|
631
|
+
expect(skill).toBeInstanceOf(GoogleMapsSkill);
|
|
632
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('should return correct manifest name and version', () => {
|
|
636
|
+
const skill = createGoogleMapsSkill();
|
|
637
|
+
const manifest = skill.getManifest();
|
|
638
|
+
expect(manifest.name).toBe('google_maps');
|
|
639
|
+
expect(manifest.version).toBe('1.0.0');
|
|
640
|
+
expect(manifest.requiredEnvVars).toContain('GOOGLE_MAPS_API_KEY');
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it('should return get_directions and find_place tools', () => {
|
|
644
|
+
const skill = createGoogleMapsSkill();
|
|
645
|
+
const tools = skill.getTools();
|
|
646
|
+
expect(tools).toHaveLength(2);
|
|
647
|
+
const names = tools.map((t) => t.name);
|
|
648
|
+
expect(names).toContain('get_directions');
|
|
649
|
+
expect(names).toContain('find_place');
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('should return error from get_directions when API key is not set', async () => {
|
|
653
|
+
const origKey = process.env['GOOGLE_MAPS_API_KEY'];
|
|
654
|
+
delete process.env['GOOGLE_MAPS_API_KEY'];
|
|
655
|
+
const skill = createGoogleMapsSkill();
|
|
656
|
+
const dirTool = skill.getTools().find((t) => t.name === 'get_directions')!;
|
|
657
|
+
const result = await dirTool.handler(
|
|
658
|
+
{ origin: 'New York', destination: 'Boston' },
|
|
659
|
+
{},
|
|
660
|
+
) as SwaigFunctionResult;
|
|
661
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
662
|
+
expect(result.response).toContain('GOOGLE_MAPS_API_KEY');
|
|
663
|
+
expect(result.response).toContain('not configured');
|
|
664
|
+
if (origKey !== undefined) process.env['GOOGLE_MAPS_API_KEY'] = origKey;
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it('should return error from find_place when API key is not set', async () => {
|
|
668
|
+
const origKey = process.env['GOOGLE_MAPS_API_KEY'];
|
|
669
|
+
delete process.env['GOOGLE_MAPS_API_KEY'];
|
|
670
|
+
const skill = createGoogleMapsSkill();
|
|
671
|
+
const placeTool = skill.getTools().find((t) => t.name === 'find_place')!;
|
|
672
|
+
const result = await placeTool.handler(
|
|
673
|
+
{ query: 'pizza near me' },
|
|
674
|
+
{},
|
|
675
|
+
) as SwaigFunctionResult;
|
|
676
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
677
|
+
expect(result.response).toContain('GOOGLE_MAPS_API_KEY');
|
|
678
|
+
if (origKey !== undefined) process.env['GOOGLE_MAPS_API_KEY'] = origKey;
|
|
679
|
+
});
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// ---------------------------------------------------------------------------
|
|
683
|
+
// DataSphere Skill
|
|
684
|
+
// ---------------------------------------------------------------------------
|
|
685
|
+
describe('DataSphereSkill', () => {
|
|
686
|
+
it('should instantiate via createSkill factory', () => {
|
|
687
|
+
const skill = createDataSphereSkill();
|
|
688
|
+
expect(skill).toBeInstanceOf(DataSphereSkill);
|
|
689
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('should return correct manifest name and version', () => {
|
|
693
|
+
const skill = createDataSphereSkill();
|
|
694
|
+
const manifest = skill.getManifest();
|
|
695
|
+
expect(manifest.name).toBe('datasphere');
|
|
696
|
+
expect(manifest.version).toBe('1.0.0');
|
|
697
|
+
expect(manifest.requiredEnvVars).toContain('SIGNALWIRE_PROJECT_ID');
|
|
698
|
+
expect(manifest.requiredEnvVars).toContain('SIGNALWIRE_TOKEN');
|
|
699
|
+
expect(manifest.requiredEnvVars).toContain('SIGNALWIRE_SPACE');
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
it('should return a search_datasphere tool', () => {
|
|
703
|
+
const skill = createDataSphereSkill();
|
|
704
|
+
const tools = skill.getTools();
|
|
705
|
+
expect(tools).toHaveLength(1);
|
|
706
|
+
expect(tools[0].name).toBe('search_datasphere');
|
|
707
|
+
expect(tools[0].required).toContain('query');
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
it('should return non-empty prompt sections', () => {
|
|
711
|
+
const skill = createDataSphereSkill();
|
|
712
|
+
const sections = skill.getPromptSections();
|
|
713
|
+
expect(sections.length).toBeGreaterThan(0);
|
|
714
|
+
expect(sections[0].title).toContain('DataSphere');
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
it('should return error when env vars are not set', async () => {
|
|
718
|
+
const origPid = process.env['SIGNALWIRE_PROJECT_ID'];
|
|
719
|
+
const origToken = process.env['SIGNALWIRE_TOKEN'];
|
|
720
|
+
const origSpace = process.env['SIGNALWIRE_SPACE'];
|
|
721
|
+
delete process.env['SIGNALWIRE_PROJECT_ID'];
|
|
722
|
+
delete process.env['SIGNALWIRE_TOKEN'];
|
|
723
|
+
delete process.env['SIGNALWIRE_SPACE'];
|
|
724
|
+
const skill = createDataSphereSkill();
|
|
725
|
+
const handler = skill.getTools()[0].handler;
|
|
726
|
+
const result = await handler({ query: 'test' }, {}) as SwaigFunctionResult;
|
|
727
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
728
|
+
expect(result.response).toContain('not configured');
|
|
729
|
+
expect(result.response).toContain('SIGNALWIRE_PROJECT_ID');
|
|
730
|
+
if (origPid !== undefined) process.env['SIGNALWIRE_PROJECT_ID'] = origPid;
|
|
731
|
+
if (origToken !== undefined) process.env['SIGNALWIRE_TOKEN'] = origToken;
|
|
732
|
+
if (origSpace !== undefined) process.env['SIGNALWIRE_SPACE'] = origSpace;
|
|
733
|
+
});
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
// ---------------------------------------------------------------------------
|
|
737
|
+
// DataSphere Serverless Skill
|
|
738
|
+
// ---------------------------------------------------------------------------
|
|
739
|
+
describe('DataSphereServerlessSkill', () => {
|
|
740
|
+
it('should instantiate via createSkill factory', () => {
|
|
741
|
+
const skill = createDataSphereServerlessSkill();
|
|
742
|
+
expect(skill).toBeInstanceOf(DataSphereServerlessSkill);
|
|
743
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
it('should return correct manifest name and version', () => {
|
|
747
|
+
const skill = createDataSphereServerlessSkill();
|
|
748
|
+
const manifest = skill.getManifest();
|
|
749
|
+
expect(manifest.name).toBe('datasphere_serverless');
|
|
750
|
+
expect(manifest.version).toBe('1.0.0');
|
|
751
|
+
expect(manifest.requiredEnvVars).toContain('SIGNALWIRE_PROJECT_ID');
|
|
752
|
+
expect(manifest.requiredEnvVars).toContain('SIGNALWIRE_TOKEN');
|
|
753
|
+
expect(manifest.requiredEnvVars).toContain('SIGNALWIRE_SPACE');
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
it('should return a search_datasphere tool stub', () => {
|
|
757
|
+
const skill = createDataSphereServerlessSkill();
|
|
758
|
+
const tools = skill.getTools();
|
|
759
|
+
expect(tools).toHaveLength(1);
|
|
760
|
+
expect(tools[0].name).toBe('search_datasphere');
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
it('should return stub handler message indicating DataMap mode', () => {
|
|
764
|
+
const skill = createDataSphereServerlessSkill();
|
|
765
|
+
const handler = skill.getTools()[0].handler;
|
|
766
|
+
const result = handler({}, {}) as SwaigFunctionResult;
|
|
767
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
768
|
+
expect(result.response).toContain('serverless DataMap');
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
it('should expose getDataMapTools returning a data_map function', () => {
|
|
772
|
+
const skill = createDataSphereServerlessSkill();
|
|
773
|
+
const dataMapTools = skill.getDataMapTools();
|
|
774
|
+
expect(dataMapTools).toHaveLength(1);
|
|
775
|
+
expect(dataMapTools[0]).toBeDefined();
|
|
776
|
+
// The DataMap function should have a 'function' key with name
|
|
777
|
+
expect(dataMapTools[0]).toHaveProperty('function');
|
|
778
|
+
});
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
// ---------------------------------------------------------------------------
|
|
782
|
+
// Native Vector Search Skill
|
|
783
|
+
// ---------------------------------------------------------------------------
|
|
784
|
+
describe('NativeVectorSearchSkill', () => {
|
|
785
|
+
const testDocuments = [
|
|
786
|
+
{ id: 'doc1', text: 'TypeScript is a strongly typed programming language that builds on JavaScript.' },
|
|
787
|
+
{ id: 'doc2', text: 'Python is a versatile programming language used for web development and data science.' },
|
|
788
|
+
{ id: 'doc3', text: 'Rust focuses on safety and performance, ideal for systems programming.' },
|
|
789
|
+
{ id: 'doc4', text: 'The weather today is sunny with clear skies and warm temperatures.' },
|
|
790
|
+
];
|
|
791
|
+
|
|
792
|
+
it('should instantiate with documents config via createSkill factory', () => {
|
|
793
|
+
const skill = createNativeVectorSearchSkill({ documents: testDocuments });
|
|
794
|
+
expect(skill).toBeInstanceOf(NativeVectorSearchSkill);
|
|
795
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
it('should return correct manifest name and version', () => {
|
|
799
|
+
const skill = createNativeVectorSearchSkill();
|
|
800
|
+
const manifest = skill.getManifest();
|
|
801
|
+
expect(manifest.name).toBe('native_vector_search');
|
|
802
|
+
expect(manifest.version).toBe('1.0.0');
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it('should return a search_documents tool', () => {
|
|
806
|
+
const skill = createNativeVectorSearchSkill({ documents: testDocuments });
|
|
807
|
+
const tools = skill.getTools();
|
|
808
|
+
expect(tools).toHaveLength(1);
|
|
809
|
+
expect(tools[0].name).toBe('search_documents');
|
|
810
|
+
expect(tools[0].required).toContain('query');
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
it('should execute search and find relevant documents', () => {
|
|
814
|
+
const skill = createNativeVectorSearchSkill({ documents: testDocuments });
|
|
815
|
+
const handler = skill.getTools()[0].handler;
|
|
816
|
+
const result = handler({ query: 'TypeScript programming language' }, {}) as SwaigFunctionResult;
|
|
817
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
818
|
+
expect(result.response).toContain('doc1');
|
|
819
|
+
expect(result.response).toContain('TypeScript');
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('should rank relevant documents higher (scoring works)', () => {
|
|
823
|
+
const skill = createNativeVectorSearchSkill({ documents: testDocuments });
|
|
824
|
+
const handler = skill.getTools()[0].handler;
|
|
825
|
+
const result = handler({ query: 'programming language', top_k: 4 }, {}) as SwaigFunctionResult;
|
|
826
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
827
|
+
// Programming-related docs should appear; weather doc should not (no overlap)
|
|
828
|
+
expect(result.response).toContain('programming');
|
|
829
|
+
// The response should contain "Result 1" indicating at least one result
|
|
830
|
+
expect(result.response).toContain('Result 1');
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
// ---------------------------------------------------------------------------
|
|
835
|
+
// Spider Skill
|
|
836
|
+
// ---------------------------------------------------------------------------
|
|
837
|
+
describe('SpiderSkill', () => {
|
|
838
|
+
it('should instantiate via createSkill factory', () => {
|
|
839
|
+
const skill = createSpiderSkill();
|
|
840
|
+
expect(skill).toBeInstanceOf(SpiderSkill);
|
|
841
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('should return correct manifest name and version', () => {
|
|
845
|
+
const skill = createSpiderSkill();
|
|
846
|
+
const manifest = skill.getManifest();
|
|
847
|
+
expect(manifest.name).toBe('spider');
|
|
848
|
+
expect(manifest.version).toBe('1.0.0');
|
|
849
|
+
expect(manifest.requiredEnvVars).toContain('SPIDER_API_KEY');
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
it('should return a scrape_url tool', () => {
|
|
853
|
+
const skill = createSpiderSkill();
|
|
854
|
+
const tools = skill.getTools();
|
|
855
|
+
expect(tools).toHaveLength(1);
|
|
856
|
+
expect(tools[0].name).toBe('scrape_url');
|
|
857
|
+
expect(tools[0].required).toContain('url');
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
it('should return error when API key is not set', async () => {
|
|
861
|
+
const origKey = process.env['SPIDER_API_KEY'];
|
|
862
|
+
delete process.env['SPIDER_API_KEY'];
|
|
863
|
+
const skill = createSpiderSkill();
|
|
864
|
+
const handler = skill.getTools()[0].handler;
|
|
865
|
+
const result = await handler({ url: 'https://example.com' }, {}) as SwaigFunctionResult;
|
|
866
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
867
|
+
expect(result.response).toContain('SPIDER_API_KEY');
|
|
868
|
+
expect(result.response).toContain('not configured');
|
|
869
|
+
if (origKey !== undefined) process.env['SPIDER_API_KEY'] = origKey;
|
|
870
|
+
});
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// ---------------------------------------------------------------------------
|
|
874
|
+
// Claude Skills Skill
|
|
875
|
+
// ---------------------------------------------------------------------------
|
|
876
|
+
describe('ClaudeSkill', () => {
|
|
877
|
+
it('should instantiate via createSkill factory', () => {
|
|
878
|
+
const skill = createClaudeSkill();
|
|
879
|
+
expect(skill).toBeInstanceOf(ClaudeSkill);
|
|
880
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
it('should return correct manifest name and version', () => {
|
|
884
|
+
const skill = createClaudeSkill();
|
|
885
|
+
const manifest = skill.getManifest();
|
|
886
|
+
expect(manifest.name).toBe('claude_skills');
|
|
887
|
+
expect(manifest.version).toBe('1.0.0');
|
|
888
|
+
expect(manifest.requiredEnvVars).toContain('ANTHROPIC_API_KEY');
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
it('should return an ask_claude tool', () => {
|
|
892
|
+
const skill = createClaudeSkill();
|
|
893
|
+
const tools = skill.getTools();
|
|
894
|
+
expect(tools).toHaveLength(1);
|
|
895
|
+
expect(tools[0].name).toBe('ask_claude');
|
|
896
|
+
expect(tools[0].required).toContain('prompt');
|
|
897
|
+
});
|
|
898
|
+
});
|
|
899
|
+
|
|
900
|
+
// ---------------------------------------------------------------------------
|
|
901
|
+
// MCP Gateway Skill
|
|
902
|
+
// ---------------------------------------------------------------------------
|
|
903
|
+
describe('McpGatewaySkill', () => {
|
|
904
|
+
it('should instantiate via createSkill factory', () => {
|
|
905
|
+
const skill = createMcpGatewaySkill();
|
|
906
|
+
expect(skill).toBeInstanceOf(McpGatewaySkill);
|
|
907
|
+
expect(skill).toBeInstanceOf(SkillBase);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
it('should return correct manifest name and version', () => {
|
|
911
|
+
const skill = createMcpGatewaySkill();
|
|
912
|
+
const manifest = skill.getManifest();
|
|
913
|
+
expect(manifest.name).toBe('mcp_gateway');
|
|
914
|
+
expect(manifest.version).toBe('0.1.0');
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
it('should return not implemented message from handler', () => {
|
|
918
|
+
const skill = createMcpGatewaySkill();
|
|
919
|
+
const handler = skill.getTools()[0].handler;
|
|
920
|
+
const result = handler({ server: 'test', method: 'test' }, {}) as SwaigFunctionResult;
|
|
921
|
+
expect(result).toBeInstanceOf(SwaigFunctionResult);
|
|
922
|
+
expect(result.response).toContain('not yet implemented');
|
|
923
|
+
});
|
|
924
|
+
});
|