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
package/tests/setup.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { SkillBase, type SkillManifest, type SkillToolDefinition } from '../../src/skills/SkillBase.js';
|
|
3
|
+
import { SwaigFunctionResult } from '../../src/SwaigFunctionResult.js';
|
|
4
|
+
|
|
5
|
+
class TestSkill extends SkillBase {
|
|
6
|
+
constructor(config?: Record<string, unknown>) {
|
|
7
|
+
super('test_skill', config);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
getManifest(): SkillManifest {
|
|
11
|
+
return {
|
|
12
|
+
name: 'test_skill',
|
|
13
|
+
description: 'A test skill',
|
|
14
|
+
version: '1.0.0',
|
|
15
|
+
tags: ['test'],
|
|
16
|
+
requiredEnvVars: ['TEST_API_KEY'],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
getTools(): SkillToolDefinition[] {
|
|
21
|
+
return [{
|
|
22
|
+
name: 'test_tool',
|
|
23
|
+
description: 'A test tool',
|
|
24
|
+
parameters: { query: { type: 'string' } },
|
|
25
|
+
handler: (args) => new SwaigFunctionResult(`Result: ${args['query']}`),
|
|
26
|
+
}];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
getPromptSections() {
|
|
30
|
+
return [{ title: 'Test Section', body: 'Test body' }];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
getHints() {
|
|
34
|
+
return ['test hint'];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getGlobalData() {
|
|
38
|
+
return { test_key: 'test_value' };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
describe('SkillBase', () => {
|
|
43
|
+
it('has a skill name and instance ID', () => {
|
|
44
|
+
const skill = new TestSkill();
|
|
45
|
+
expect(skill.skillName).toBe('test_skill');
|
|
46
|
+
expect(skill.instanceId).toContain('test_skill-');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('returns manifest', () => {
|
|
50
|
+
const skill = new TestSkill();
|
|
51
|
+
const manifest = skill.getManifest();
|
|
52
|
+
expect(manifest.name).toBe('test_skill');
|
|
53
|
+
expect(manifest.version).toBe('1.0.0');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('returns tools', () => {
|
|
57
|
+
const skill = new TestSkill();
|
|
58
|
+
const tools = skill.getTools();
|
|
59
|
+
expect(tools.length).toBe(1);
|
|
60
|
+
expect(tools[0].name).toBe('test_tool');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('returns prompt sections', () => {
|
|
64
|
+
const skill = new TestSkill();
|
|
65
|
+
expect(skill.getPromptSections()).toHaveLength(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('returns hints', () => {
|
|
69
|
+
const skill = new TestSkill();
|
|
70
|
+
expect(skill.getHints()).toEqual(['test hint']);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('returns global data', () => {
|
|
74
|
+
const skill = new TestSkill();
|
|
75
|
+
expect(skill.getGlobalData()).toEqual({ test_key: 'test_value' });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('validates missing env vars', () => {
|
|
79
|
+
const saved = process.env['TEST_API_KEY'];
|
|
80
|
+
delete process.env['TEST_API_KEY'];
|
|
81
|
+
try {
|
|
82
|
+
const skill = new TestSkill();
|
|
83
|
+
const missing = skill.validateEnvVars();
|
|
84
|
+
expect(missing).toContain('TEST_API_KEY');
|
|
85
|
+
} finally {
|
|
86
|
+
if (saved) process.env['TEST_API_KEY'] = saved;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('validates present env vars', () => {
|
|
91
|
+
process.env['TEST_API_KEY'] = 'test-key';
|
|
92
|
+
try {
|
|
93
|
+
const skill = new TestSkill();
|
|
94
|
+
expect(skill.validateEnvVars()).toHaveLength(0);
|
|
95
|
+
} finally {
|
|
96
|
+
delete process.env['TEST_API_KEY'];
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('initialized state', () => {
|
|
101
|
+
const skill = new TestSkill();
|
|
102
|
+
expect(skill.isInitialized()).toBe(false);
|
|
103
|
+
skill.markInitialized();
|
|
104
|
+
expect(skill.isInitialized()).toBe(true);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('getConfig with default', () => {
|
|
108
|
+
const skill = new TestSkill({ apiUrl: 'https://example.com' });
|
|
109
|
+
expect(skill.getConfig('apiUrl')).toBe('https://example.com');
|
|
110
|
+
expect(skill.getConfig('missing', 'default')).toBe('default');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('setup and cleanup are no-op by default', async () => {
|
|
114
|
+
const skill = new TestSkill();
|
|
115
|
+
await expect(skill.setup()).resolves.toBeUndefined();
|
|
116
|
+
await expect(skill.cleanup()).resolves.toBeUndefined();
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { SkillManager } from '../../src/skills/SkillManager.js';
|
|
3
|
+
import { SkillBase, type SkillManifest, type SkillToolDefinition } from '../../src/skills/SkillBase.js';
|
|
4
|
+
import { SwaigFunctionResult } from '../../src/SwaigFunctionResult.js';
|
|
5
|
+
import { suppressAllLogs } from '../../src/Logger.js';
|
|
6
|
+
|
|
7
|
+
class MockSkill extends SkillBase {
|
|
8
|
+
setupCalled = false;
|
|
9
|
+
cleanupCalled = false;
|
|
10
|
+
|
|
11
|
+
constructor(name = 'mock_skill', config?: Record<string, unknown>) {
|
|
12
|
+
super(name, config);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
getManifest(): SkillManifest {
|
|
16
|
+
return { name: this.skillName, description: 'Mock', version: '1.0.0' };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getTools(): SkillToolDefinition[] {
|
|
20
|
+
return [{
|
|
21
|
+
name: `${this.skillName}_tool`,
|
|
22
|
+
description: 'Mock tool',
|
|
23
|
+
handler: () => new SwaigFunctionResult('mock result'),
|
|
24
|
+
}];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getPromptSections() {
|
|
28
|
+
return [{ title: `${this.skillName} Section`, body: 'Mock body' }];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getHints() {
|
|
32
|
+
return [`${this.skillName} hint`];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getGlobalData() {
|
|
36
|
+
return { [`${this.skillName}_data`]: true };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async setup() {
|
|
40
|
+
this.setupCalled = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async cleanup() {
|
|
44
|
+
this.cleanupCalled = true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe('SkillManager', () => {
|
|
49
|
+
let manager: SkillManager;
|
|
50
|
+
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
suppressAllLogs(true);
|
|
53
|
+
manager = new SkillManager();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('adds a skill', async () => {
|
|
57
|
+
const skill = new MockSkill();
|
|
58
|
+
await manager.addSkill(skill);
|
|
59
|
+
expect(manager.size).toBe(1);
|
|
60
|
+
expect(skill.setupCalled).toBe(true);
|
|
61
|
+
expect(skill.isInitialized()).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('rejects duplicate skill instances', async () => {
|
|
65
|
+
const skill = new MockSkill();
|
|
66
|
+
await manager.addSkill(skill);
|
|
67
|
+
await expect(manager.addSkill(skill)).rejects.toThrow('already loaded');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('removes a skill by instance ID', async () => {
|
|
71
|
+
const skill = new MockSkill();
|
|
72
|
+
await manager.addSkill(skill);
|
|
73
|
+
const removed = await manager.removeSkill(skill.instanceId);
|
|
74
|
+
expect(removed).toBe(true);
|
|
75
|
+
expect(skill.cleanupCalled).toBe(true);
|
|
76
|
+
expect(manager.size).toBe(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('returns false when removing non-existent skill', async () => {
|
|
80
|
+
expect(await manager.removeSkill('nope')).toBe(false);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('removes skills by name', async () => {
|
|
84
|
+
const s1 = new MockSkill('weather');
|
|
85
|
+
const s2 = new MockSkill('weather');
|
|
86
|
+
const s3 = new MockSkill('math');
|
|
87
|
+
await manager.addSkill(s1);
|
|
88
|
+
await manager.addSkill(s2);
|
|
89
|
+
await manager.addSkill(s3);
|
|
90
|
+
const count = await manager.removeSkillByName('weather');
|
|
91
|
+
expect(count).toBe(2);
|
|
92
|
+
expect(manager.size).toBe(1);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('hasSkill checks by name', async () => {
|
|
96
|
+
const skill = new MockSkill('datetime');
|
|
97
|
+
await manager.addSkill(skill);
|
|
98
|
+
expect(manager.hasSkill('datetime')).toBe(true);
|
|
99
|
+
expect(manager.hasSkill('missing')).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('getSkill by instance ID', async () => {
|
|
103
|
+
const skill = new MockSkill();
|
|
104
|
+
await manager.addSkill(skill);
|
|
105
|
+
expect(manager.getSkill(skill.instanceId)).toBe(skill);
|
|
106
|
+
expect(manager.getSkill('nope')).toBeUndefined();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('listSkills returns all loaded skills', async () => {
|
|
110
|
+
await manager.addSkill(new MockSkill('a'));
|
|
111
|
+
await manager.addSkill(new MockSkill('b'));
|
|
112
|
+
const list = manager.listSkills();
|
|
113
|
+
expect(list).toHaveLength(2);
|
|
114
|
+
expect(list.map(s => s.name)).toContain('a');
|
|
115
|
+
expect(list.map(s => s.name)).toContain('b');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('getAllTools aggregates tools from all skills', async () => {
|
|
119
|
+
await manager.addSkill(new MockSkill('a'));
|
|
120
|
+
await manager.addSkill(new MockSkill('b'));
|
|
121
|
+
const tools = manager.getAllTools();
|
|
122
|
+
expect(tools).toHaveLength(2);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('getAllPromptSections aggregates sections', async () => {
|
|
126
|
+
await manager.addSkill(new MockSkill('a'));
|
|
127
|
+
await manager.addSkill(new MockSkill('b'));
|
|
128
|
+
expect(manager.getAllPromptSections()).toHaveLength(2);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('getAllHints aggregates hints', async () => {
|
|
132
|
+
await manager.addSkill(new MockSkill('a'));
|
|
133
|
+
await manager.addSkill(new MockSkill('b'));
|
|
134
|
+
expect(manager.getAllHints()).toHaveLength(2);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('getMergedGlobalData merges all skill data', async () => {
|
|
138
|
+
await manager.addSkill(new MockSkill('a'));
|
|
139
|
+
await manager.addSkill(new MockSkill('b'));
|
|
140
|
+
const data = manager.getMergedGlobalData();
|
|
141
|
+
expect(data['a_data']).toBe(true);
|
|
142
|
+
expect(data['b_data']).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('clear removes all skills and calls cleanup', async () => {
|
|
146
|
+
const s1 = new MockSkill('a');
|
|
147
|
+
const s2 = new MockSkill('b');
|
|
148
|
+
await manager.addSkill(s1);
|
|
149
|
+
await manager.addSkill(s2);
|
|
150
|
+
await manager.clear();
|
|
151
|
+
expect(manager.size).toBe(0);
|
|
152
|
+
expect(s1.cleanupCalled).toBe(true);
|
|
153
|
+
expect(s2.cleanupCalled).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('supports multi-instance of same skill', async () => {
|
|
157
|
+
const s1 = new MockSkill('weather');
|
|
158
|
+
const s2 = new MockSkill('weather');
|
|
159
|
+
await manager.addSkill(s1);
|
|
160
|
+
await manager.addSkill(s2);
|
|
161
|
+
expect(manager.size).toBe(2);
|
|
162
|
+
expect(manager.hasSkill('weather')).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { SkillRegistry } from '../../src/skills/SkillRegistry.js';
|
|
3
|
+
import { SkillBase, type SkillManifest, type SkillToolDefinition } from '../../src/skills/SkillBase.js';
|
|
4
|
+
import { SwaigFunctionResult } from '../../src/SwaigFunctionResult.js';
|
|
5
|
+
import { suppressAllLogs } from '../../src/Logger.js';
|
|
6
|
+
|
|
7
|
+
class SimpleSkill extends SkillBase {
|
|
8
|
+
constructor(config?: Record<string, unknown>) {
|
|
9
|
+
super('simple', config);
|
|
10
|
+
}
|
|
11
|
+
getManifest(): SkillManifest {
|
|
12
|
+
return { name: 'simple', description: 'Simple skill', version: '1.0.0' };
|
|
13
|
+
}
|
|
14
|
+
getTools(): SkillToolDefinition[] {
|
|
15
|
+
return [{
|
|
16
|
+
name: 'simple_tool',
|
|
17
|
+
description: 'Simple tool',
|
|
18
|
+
handler: () => new SwaigFunctionResult('ok'),
|
|
19
|
+
}];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('SkillRegistry', () => {
|
|
24
|
+
let registry: SkillRegistry;
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
suppressAllLogs(true);
|
|
28
|
+
SkillRegistry.resetInstance();
|
|
29
|
+
registry = new SkillRegistry();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('registers and creates skills', () => {
|
|
33
|
+
registry.register('simple', (config) => new SimpleSkill(config));
|
|
34
|
+
const skill = registry.create('simple');
|
|
35
|
+
expect(skill).not.toBeNull();
|
|
36
|
+
expect(skill!.skillName).toBe('simple');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('returns null for unregistered skill', () => {
|
|
40
|
+
expect(registry.create('missing')).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('has() checks registration', () => {
|
|
44
|
+
registry.register('simple', () => new SimpleSkill());
|
|
45
|
+
expect(registry.has('simple')).toBe(true);
|
|
46
|
+
expect(registry.has('missing')).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('unregister removes skill', () => {
|
|
50
|
+
registry.register('simple', () => new SimpleSkill());
|
|
51
|
+
expect(registry.unregister('simple')).toBe(true);
|
|
52
|
+
expect(registry.has('simple')).toBe(false);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('listRegistered returns all names', () => {
|
|
56
|
+
registry.register('a', () => new SimpleSkill());
|
|
57
|
+
registry.register('b', () => new SimpleSkill());
|
|
58
|
+
const names = registry.listRegistered();
|
|
59
|
+
expect(names).toContain('a');
|
|
60
|
+
expect(names).toContain('b');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('registers with manifest', () => {
|
|
64
|
+
const manifest: SkillManifest = { name: 'simple', description: 'Test', version: '1.0.0' };
|
|
65
|
+
registry.register('simple', () => new SimpleSkill(), manifest);
|
|
66
|
+
expect(registry.getManifest('simple')).toEqual(manifest);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('listRegisteredWithManifests includes manifests', () => {
|
|
70
|
+
const manifest: SkillManifest = { name: 'simple', description: 'Test', version: '1.0.0' };
|
|
71
|
+
registry.register('simple', () => new SimpleSkill(), manifest);
|
|
72
|
+
const list = registry.listRegisteredWithManifests();
|
|
73
|
+
expect(list).toHaveLength(1);
|
|
74
|
+
expect(list[0].manifest).toEqual(manifest);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('singleton instance', () => {
|
|
78
|
+
const inst1 = SkillRegistry.getInstance();
|
|
79
|
+
const inst2 = SkillRegistry.getInstance();
|
|
80
|
+
expect(inst1).toBe(inst2);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('resetInstance creates new singleton', () => {
|
|
84
|
+
const inst1 = SkillRegistry.getInstance();
|
|
85
|
+
SkillRegistry.resetInstance();
|
|
86
|
+
const inst2 = SkillRegistry.getInstance();
|
|
87
|
+
expect(inst1).not.toBe(inst2);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('size tracks registrations', () => {
|
|
91
|
+
expect(registry.size).toBe(0);
|
|
92
|
+
registry.register('a', () => new SimpleSkill());
|
|
93
|
+
expect(registry.size).toBe(1);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('clear removes all registrations', () => {
|
|
97
|
+
registry.register('a', () => new SimpleSkill());
|
|
98
|
+
registry.register('b', () => new SimpleSkill());
|
|
99
|
+
registry.clear();
|
|
100
|
+
expect(registry.size).toBe(0);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('addSearchPath adds paths', () => {
|
|
104
|
+
registry.addSearchPath('/custom/skills');
|
|
105
|
+
expect(registry.getSearchPaths()).toContain('/custom/skills');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('addSearchPath deduplicates', () => {
|
|
109
|
+
registry.addSearchPath('/skills');
|
|
110
|
+
registry.addSearchPath('/skills');
|
|
111
|
+
expect(registry.getSearchPaths().filter(p => p === '/skills')).toHaveLength(1);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('passes config to factory', () => {
|
|
115
|
+
registry.register('simple', (config) => new SimpleSkill(config));
|
|
116
|
+
const skill = registry.create('simple', { key: 'val' });
|
|
117
|
+
expect(skill!.getConfig('key')).toBe('val');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('discoverFromDirectory handles non-existent dir', async () => {
|
|
121
|
+
const found = await registry.discoverFromDirectory('/nonexistent/path');
|
|
122
|
+
expect(found).toHaveLength(0);
|
|
123
|
+
});
|
|
124
|
+
});
|