opc-agent 1.3.2 → 1.4.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 (186) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +14 -0
  3. package/.github/PULL_REQUEST_TEMPLATE.md +13 -0
  4. package/.github/workflows/ci.yml +24 -0
  5. package/CHANGELOG.md +23 -63
  6. package/CONTRIBUTING.md +21 -60
  7. package/README.md +235 -358
  8. package/README.zh-CN.md +415 -415
  9. package/dist/channels/slack.js +93 -10
  10. package/dist/channels/web.d.ts +10 -0
  11. package/dist/channels/web.js +33 -2
  12. package/dist/cli.js +255 -60
  13. package/dist/core/runtime.d.ts +4 -0
  14. package/dist/core/runtime.js +27 -0
  15. package/dist/deploy/hermes.js +22 -22
  16. package/dist/deploy/openclaw.js +31 -40
  17. package/dist/index.d.ts +3 -10
  18. package/dist/index.js +6 -15
  19. package/dist/providers/index.d.ts +1 -1
  20. package/dist/providers/index.js +7 -1
  21. package/dist/schema/oad.d.ts +1 -2
  22. package/dist/templates/code-reviewer.d.ts +0 -8
  23. package/dist/templates/code-reviewer.js +5 -9
  24. package/dist/templates/customer-service.d.ts +0 -8
  25. package/dist/templates/customer-service.js +2 -6
  26. package/dist/templates/data-analyst.d.ts +0 -8
  27. package/dist/templates/data-analyst.js +5 -9
  28. package/dist/templates/knowledge-base.d.ts +0 -8
  29. package/dist/templates/knowledge-base.js +2 -6
  30. package/dist/templates/sales-assistant.d.ts +0 -8
  31. package/dist/templates/sales-assistant.js +4 -8
  32. package/dist/templates/teacher.d.ts +0 -8
  33. package/dist/templates/teacher.js +6 -10
  34. package/dist/traces/index.d.ts +49 -0
  35. package/dist/traces/index.js +102 -0
  36. package/docs/.vitepress/config.ts +103 -103
  37. package/docs/api/cli.md +48 -48
  38. package/docs/api/oad-schema.md +64 -64
  39. package/docs/api/sdk.md +80 -80
  40. package/docs/guide/concepts.md +51 -51
  41. package/docs/guide/configuration.md +79 -79
  42. package/docs/guide/deployment.md +42 -42
  43. package/docs/guide/getting-started.md +44 -44
  44. package/docs/guide/templates.md +28 -28
  45. package/docs/guide/testing.md +84 -84
  46. package/docs/index.md +27 -27
  47. package/docs/zh/api/cli.md +54 -54
  48. package/docs/zh/api/oad-schema.md +87 -87
  49. package/docs/zh/api/sdk.md +102 -102
  50. package/docs/zh/guide/concepts.md +104 -104
  51. package/docs/zh/guide/configuration.md +135 -135
  52. package/docs/zh/guide/deployment.md +81 -81
  53. package/docs/zh/guide/getting-started.md +82 -82
  54. package/docs/zh/guide/templates.md +84 -84
  55. package/docs/zh/guide/testing.md +88 -88
  56. package/docs/zh/index.md +27 -27
  57. package/examples/README.md +22 -0
  58. package/examples/basic-agent.ts +90 -0
  59. package/examples/brain-integration.ts +71 -0
  60. package/examples/customer-service-demo/README.md +90 -90
  61. package/examples/customer-service-demo/oad.yaml +107 -107
  62. package/examples/multi-channel.ts +74 -0
  63. package/package.json +1 -1
  64. package/src/analytics/index.ts +66 -66
  65. package/src/channels/discord.ts +192 -192
  66. package/src/channels/email.ts +177 -177
  67. package/src/channels/feishu.ts +236 -236
  68. package/src/channels/index.ts +15 -15
  69. package/src/channels/slack.ts +217 -160
  70. package/src/channels/telegram.ts +90 -90
  71. package/src/channels/voice.ts +106 -106
  72. package/src/channels/web.ts +38 -2
  73. package/src/channels/webhook.ts +199 -199
  74. package/src/channels/websocket.ts +87 -87
  75. package/src/channels/wechat.ts +149 -149
  76. package/src/cli.ts +282 -58
  77. package/src/core/a2a.ts +143 -143
  78. package/src/core/agent.ts +152 -152
  79. package/src/core/analytics-engine.ts +186 -186
  80. package/src/core/auth.ts +57 -57
  81. package/src/core/cache.ts +141 -141
  82. package/src/core/compose.ts +77 -77
  83. package/src/core/config.ts +14 -14
  84. package/src/core/errors.ts +148 -148
  85. package/src/core/hitl.ts +138 -138
  86. package/src/core/logger.ts +57 -57
  87. package/src/core/orchestrator.ts +215 -215
  88. package/src/core/performance.ts +187 -187
  89. package/src/core/rate-limiter.ts +128 -128
  90. package/src/core/room.ts +109 -109
  91. package/src/core/runtime.ts +183 -152
  92. package/src/core/sandbox.ts +101 -101
  93. package/src/core/security.ts +171 -171
  94. package/src/core/types.ts +68 -68
  95. package/src/core/versioning.ts +106 -106
  96. package/src/core/watch.ts +178 -178
  97. package/src/core/workflow.ts +235 -235
  98. package/src/deploy/hermes.ts +156 -156
  99. package/src/deploy/openclaw.ts +190 -200
  100. package/src/i18n/index.ts +216 -216
  101. package/src/index.ts +3 -10
  102. package/src/memory/deepbrain.ts +108 -108
  103. package/src/memory/index.ts +34 -34
  104. package/src/plugins/index.ts +208 -208
  105. package/src/providers/index.ts +9 -1
  106. package/src/schema/oad.ts +154 -155
  107. package/src/skills/base.ts +16 -16
  108. package/src/skills/document.ts +100 -100
  109. package/src/skills/http.ts +35 -35
  110. package/src/skills/index.ts +27 -27
  111. package/src/skills/scheduler.ts +80 -80
  112. package/src/skills/webhook-trigger.ts +59 -59
  113. package/src/templates/code-reviewer.ts +30 -34
  114. package/src/templates/customer-service.ts +76 -80
  115. package/src/templates/data-analyst.ts +66 -70
  116. package/src/templates/executive-assistant.ts +71 -71
  117. package/src/templates/financial-advisor.ts +60 -60
  118. package/src/templates/knowledge-base.ts +27 -31
  119. package/src/templates/legal-assistant.ts +71 -71
  120. package/src/templates/sales-assistant.ts +75 -79
  121. package/src/templates/teacher.ts +75 -79
  122. package/src/testing/index.ts +181 -181
  123. package/src/tools/calculator.ts +73 -73
  124. package/src/tools/datetime.ts +149 -149
  125. package/src/tools/json-transform.ts +187 -187
  126. package/src/tools/mcp.ts +76 -76
  127. package/src/tools/text-analysis.ts +116 -116
  128. package/src/traces/index.ts +132 -0
  129. package/templates/Dockerfile +15 -15
  130. package/templates/code-reviewer/README.md +27 -27
  131. package/templates/code-reviewer/oad.yaml +41 -41
  132. package/templates/customer-service/README.md +22 -22
  133. package/templates/customer-service/oad.yaml +36 -36
  134. package/templates/docker-compose.yml +21 -21
  135. package/templates/ecommerce-assistant/README.md +45 -45
  136. package/templates/ecommerce-assistant/oad.yaml +47 -47
  137. package/templates/knowledge-base/README.md +28 -28
  138. package/templates/knowledge-base/oad.yaml +38 -38
  139. package/templates/sales-assistant/README.md +26 -26
  140. package/templates/sales-assistant/oad.yaml +43 -43
  141. package/templates/tech-support/README.md +43 -43
  142. package/templates/tech-support/oad.yaml +45 -45
  143. package/test-agent/Dockerfile +9 -0
  144. package/test-agent/README.md +50 -0
  145. package/test-agent/agent.yaml +23 -0
  146. package/test-agent/docker-compose.yml +11 -0
  147. package/test-agent/oad.yaml +31 -0
  148. package/test-agent/package-lock.json +1492 -0
  149. package/test-agent/package.json +18 -0
  150. package/test-agent/src/index.ts +24 -0
  151. package/test-agent/src/skills/echo.ts +15 -0
  152. package/test-agent/tsconfig.json +25 -0
  153. package/tests/a2a.test.ts +66 -66
  154. package/tests/agent.test.ts +72 -72
  155. package/tests/analytics.test.ts +50 -50
  156. package/tests/channel.test.ts +39 -39
  157. package/tests/e2e.test.ts +134 -134
  158. package/tests/errors.test.ts +83 -83
  159. package/tests/hitl.test.ts +71 -71
  160. package/tests/i18n.test.ts +41 -41
  161. package/tests/mcp.test.ts +54 -54
  162. package/tests/oad.test.ts +68 -68
  163. package/tests/performance.test.ts +115 -115
  164. package/tests/plugin.test.ts +74 -74
  165. package/tests/room.test.ts +106 -106
  166. package/tests/runtime.test.ts +42 -42
  167. package/tests/sandbox.test.ts +46 -46
  168. package/tests/security.test.ts +60 -60
  169. package/tests/templates.test.ts +77 -77
  170. package/tests/v070.test.ts +76 -76
  171. package/tests/versioning.test.ts +75 -75
  172. package/tests/voice.test.ts +61 -61
  173. package/tests/webhook.test.ts +29 -29
  174. package/tests/workflow.test.ts +143 -143
  175. package/tsconfig.json +19 -19
  176. package/vitest.config.ts +9 -9
  177. package/dist/core/dashboard.d.ts +0 -35
  178. package/dist/core/dashboard.js +0 -157
  179. package/dist/core/priority.d.ts +0 -52
  180. package/dist/core/priority.js +0 -102
  181. package/src/core/dashboard.ts +0 -219
  182. package/src/core/priority.ts +0 -140
  183. package/src/dtv/data.ts +0 -29
  184. package/src/dtv/trust.ts +0 -43
  185. package/src/dtv/value.ts +0 -47
  186. package/src/marketplace/index.ts +0 -223
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "test-agent",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "start": "opc run",
7
+ "dev": "opc dev",
8
+ "chat": "opc chat",
9
+ "build": "tsc"
10
+ },
11
+ "dependencies": {
12
+ "opc-agent": "^1.3.0"
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.5.0",
16
+ "tsx": "^4.0.0"
17
+ }
18
+ }
@@ -0,0 +1,24 @@
1
+ import { AgentRuntime } from 'opc-agent';
2
+ import { EchoSkill } from './skills/echo';
3
+
4
+ async function main() {
5
+ const runtime = new AgentRuntime();
6
+
7
+ // Load OAD config
8
+ await runtime.loadConfig('./agent.yaml');
9
+
10
+ // Initialize agent with channels, memory, etc.
11
+ const agent = await runtime.initialize();
12
+
13
+ // Register custom skills
14
+ runtime.registerSkill(new EchoSkill());
15
+
16
+ // Start serving
17
+ await runtime.start();
18
+
19
+ console.log('🤖 Agent is running!');
20
+ console.log(' Web UI: http://localhost:3000');
21
+ console.log(' Press Ctrl+C to stop');
22
+ }
23
+
24
+ main().catch(console.error);
@@ -0,0 +1,15 @@
1
+ import { BaseSkill } from 'opc-agent';
2
+ import type { AgentContext, Message, SkillResult } from 'opc-agent';
3
+
4
+ export class EchoSkill extends BaseSkill {
5
+ name = 'echo';
6
+ description = 'Echo back the message (test skill)';
7
+
8
+ async execute(context: AgentContext, message: Message): Promise<SkillResult> {
9
+ if (message.content.toLowerCase().startsWith('/echo ')) {
10
+ const text = message.content.slice(6);
11
+ return this.match(`🔊 Echo: ${text}`);
12
+ }
13
+ return this.noMatch();
14
+ }
15
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "commonjs",
5
+ "lib": [
6
+ "ES2022"
7
+ ],
8
+ "outDir": "dist",
9
+ "rootDir": "src",
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "resolveJsonModule": true,
15
+ "declaration": true,
16
+ "sourceMap": true
17
+ },
18
+ "include": [
19
+ "src/**/*"
20
+ ],
21
+ "exclude": [
22
+ "node_modules",
23
+ "dist"
24
+ ]
25
+ }
package/tests/a2a.test.ts CHANGED
@@ -1,66 +1,66 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import { AgentRegistry } from '../src/core/a2a';
3
- import { BaseAgent } from '../src/core/agent';
4
- import { InMemoryStore } from '../src/memory';
5
-
6
- function createTestAgent(name: string): BaseAgent {
7
- const agent = new BaseAgent({ name, model: 'test', systemPrompt: `I am ${name}` }, new InMemoryStore());
8
- return agent;
9
- }
10
-
11
- describe('AgentRegistry (A2A)', () => {
12
- let registry: AgentRegistry;
13
-
14
- beforeEach(() => {
15
- registry = new AgentRegistry();
16
- });
17
-
18
- it('should register and discover agents', () => {
19
- const agent = createTestAgent('agent-1');
20
- registry.register(agent, [{ name: 'summarize', description: 'Summarize text' }]);
21
-
22
- const all = registry.discover();
23
- expect(all).toHaveLength(1);
24
- expect(all[0].agentName).toBe('agent-1');
25
- });
26
-
27
- it('should discover by capability', () => {
28
- const a1 = createTestAgent('a1');
29
- const a2 = createTestAgent('a2');
30
- registry.register(a1, [{ name: 'summarize', description: 'Summarize' }]);
31
- registry.register(a2, [{ name: 'translate', description: 'Translate' }]);
32
-
33
- const found = registry.discover('translate');
34
- expect(found).toHaveLength(1);
35
- expect(found[0].agentName).toBe('a2');
36
- });
37
-
38
- it('should unregister agents', () => {
39
- const agent = createTestAgent('temp');
40
- registry.register(agent, []);
41
- registry.unregister('temp');
42
- expect(registry.discover()).toHaveLength(0);
43
- });
44
-
45
- it('should handle request to unknown agent', async () => {
46
- const response = await registry.call('a1', 'unknown', 'test', 'hello');
47
- expect(response.status).toBe('error');
48
- });
49
-
50
- it('should send A2A request and get response', async () => {
51
- const agent = createTestAgent('responder');
52
- await agent.init();
53
- registry.register(agent, [{ name: 'chat', description: 'Chat' }]);
54
-
55
- const response = await registry.call('caller', 'responder', 'chat', 'hello');
56
- expect(response.status).toBe('success');
57
- expect(response.payload).toBeDefined();
58
- });
59
-
60
- it('should get agent by name', () => {
61
- const agent = createTestAgent('finder');
62
- registry.register(agent, []);
63
- expect(registry.getAgent('finder')).toBeDefined();
64
- expect(registry.getAgent('none')).toBeUndefined();
65
- });
66
- });
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { AgentRegistry } from '../src/core/a2a';
3
+ import { BaseAgent } from '../src/core/agent';
4
+ import { InMemoryStore } from '../src/memory';
5
+
6
+ function createTestAgent(name: string): BaseAgent {
7
+ const agent = new BaseAgent({ name, model: 'test', systemPrompt: `I am ${name}` }, new InMemoryStore());
8
+ return agent;
9
+ }
10
+
11
+ describe('AgentRegistry (A2A)', () => {
12
+ let registry: AgentRegistry;
13
+
14
+ beforeEach(() => {
15
+ registry = new AgentRegistry();
16
+ });
17
+
18
+ it('should register and discover agents', () => {
19
+ const agent = createTestAgent('agent-1');
20
+ registry.register(agent, [{ name: 'summarize', description: 'Summarize text' }]);
21
+
22
+ const all = registry.discover();
23
+ expect(all).toHaveLength(1);
24
+ expect(all[0].agentName).toBe('agent-1');
25
+ });
26
+
27
+ it('should discover by capability', () => {
28
+ const a1 = createTestAgent('a1');
29
+ const a2 = createTestAgent('a2');
30
+ registry.register(a1, [{ name: 'summarize', description: 'Summarize' }]);
31
+ registry.register(a2, [{ name: 'translate', description: 'Translate' }]);
32
+
33
+ const found = registry.discover('translate');
34
+ expect(found).toHaveLength(1);
35
+ expect(found[0].agentName).toBe('a2');
36
+ });
37
+
38
+ it('should unregister agents', () => {
39
+ const agent = createTestAgent('temp');
40
+ registry.register(agent, []);
41
+ registry.unregister('temp');
42
+ expect(registry.discover()).toHaveLength(0);
43
+ });
44
+
45
+ it('should handle request to unknown agent', async () => {
46
+ const response = await registry.call('a1', 'unknown', 'test', 'hello');
47
+ expect(response.status).toBe('error');
48
+ });
49
+
50
+ it('should send A2A request and get response', async () => {
51
+ const agent = createTestAgent('responder');
52
+ await agent.init();
53
+ registry.register(agent, [{ name: 'chat', description: 'Chat' }]);
54
+
55
+ const response = await registry.call('caller', 'responder', 'chat', 'hello');
56
+ expect(response.status).toBe('success');
57
+ expect(response.payload).toBeDefined();
58
+ });
59
+
60
+ it('should get agent by name', () => {
61
+ const agent = createTestAgent('finder');
62
+ registry.register(agent, []);
63
+ expect(registry.getAgent('finder')).toBeDefined();
64
+ expect(registry.getAgent('none')).toBeUndefined();
65
+ });
66
+ });
@@ -1,72 +1,72 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { BaseAgent } from '../src/core/agent';
3
-
4
- describe('BaseAgent', () => {
5
- it('should start in init state', () => {
6
- const agent = new BaseAgent({ name: 'test' });
7
- expect(agent.state).toBe('init');
8
- expect(agent.name).toBe('test');
9
- });
10
-
11
- it('should transition through lifecycle', async () => {
12
- const agent = new BaseAgent({ name: 'test' });
13
- const states: string[] = [];
14
- agent.on('state:change', (_from, to) => states.push(to));
15
-
16
- await agent.init();
17
- expect(agent.state).toBe('ready');
18
-
19
- await agent.start();
20
- expect(agent.state).toBe('running');
21
-
22
- await agent.stop();
23
- expect(agent.state).toBe('stopped');
24
-
25
- expect(states).toEqual(['ready', 'running', 'stopped']);
26
- });
27
-
28
- it('should not start if not ready', async () => {
29
- const agent = new BaseAgent({ name: 'test' });
30
- await expect(agent.start()).rejects.toThrow('Cannot start agent in state: init');
31
- });
32
-
33
- it('should handle messages with stub provider', async () => {
34
- const agent = new BaseAgent({ name: 'test', systemPrompt: 'Be helpful' });
35
- await agent.init();
36
-
37
- const response = await agent.handleMessage({
38
- id: 'msg_1',
39
- role: 'user',
40
- content: 'Hello',
41
- timestamp: Date.now(),
42
- });
43
-
44
- expect(response.role).toBe('assistant');
45
- expect(response.content).toContain('Hello');
46
- });
47
-
48
- it('should use skills before LLM fallback', async () => {
49
- const agent = new BaseAgent({ name: 'test' });
50
- await agent.init();
51
-
52
- agent.registerSkill({
53
- name: 'greeter',
54
- description: 'Greets users',
55
- execute: async (_ctx, msg) => {
56
- if (msg.content.toLowerCase().includes('hello')) {
57
- return { handled: true, response: 'Hi there!', confidence: 1.0 };
58
- }
59
- return { handled: false, confidence: 0 };
60
- },
61
- });
62
-
63
- const response = await agent.handleMessage({
64
- id: 'msg_1',
65
- role: 'user',
66
- content: 'Hello!',
67
- timestamp: Date.now(),
68
- });
69
-
70
- expect(response.content).toBe('Hi there!');
71
- });
72
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { BaseAgent } from '../src/core/agent';
3
+
4
+ describe('BaseAgent', () => {
5
+ it('should start in init state', () => {
6
+ const agent = new BaseAgent({ name: 'test' });
7
+ expect(agent.state).toBe('init');
8
+ expect(agent.name).toBe('test');
9
+ });
10
+
11
+ it('should transition through lifecycle', async () => {
12
+ const agent = new BaseAgent({ name: 'test' });
13
+ const states: string[] = [];
14
+ agent.on('state:change', (_from, to) => states.push(to));
15
+
16
+ await agent.init();
17
+ expect(agent.state).toBe('ready');
18
+
19
+ await agent.start();
20
+ expect(agent.state).toBe('running');
21
+
22
+ await agent.stop();
23
+ expect(agent.state).toBe('stopped');
24
+
25
+ expect(states).toEqual(['ready', 'running', 'stopped']);
26
+ });
27
+
28
+ it('should not start if not ready', async () => {
29
+ const agent = new BaseAgent({ name: 'test' });
30
+ await expect(agent.start()).rejects.toThrow('Cannot start agent in state: init');
31
+ });
32
+
33
+ it('should handle messages with stub provider', async () => {
34
+ const agent = new BaseAgent({ name: 'test', systemPrompt: 'Be helpful' });
35
+ await agent.init();
36
+
37
+ const response = await agent.handleMessage({
38
+ id: 'msg_1',
39
+ role: 'user',
40
+ content: 'Hello',
41
+ timestamp: Date.now(),
42
+ });
43
+
44
+ expect(response.role).toBe('assistant');
45
+ expect(response.content).toContain('Hello');
46
+ });
47
+
48
+ it('should use skills before LLM fallback', async () => {
49
+ const agent = new BaseAgent({ name: 'test' });
50
+ await agent.init();
51
+
52
+ agent.registerSkill({
53
+ name: 'greeter',
54
+ description: 'Greets users',
55
+ execute: async (_ctx, msg) => {
56
+ if (msg.content.toLowerCase().includes('hello')) {
57
+ return { handled: true, response: 'Hi there!', confidence: 1.0 };
58
+ }
59
+ return { handled: false, confidence: 0 };
60
+ },
61
+ });
62
+
63
+ const response = await agent.handleMessage({
64
+ id: 'msg_1',
65
+ role: 'user',
66
+ content: 'Hello!',
67
+ timestamp: Date.now(),
68
+ });
69
+
70
+ expect(response.content).toBe('Hi there!');
71
+ });
72
+ });
@@ -1,50 +1,50 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { Analytics } from '../src/analytics';
3
-
4
- describe('Analytics', () => {
5
- it('should track messages', () => {
6
- const analytics = new Analytics();
7
- analytics.recordMessage(100);
8
- analytics.recordMessage(200);
9
- const snap = analytics.getSnapshot();
10
- expect(snap.messagesProcessed).toBe(2);
11
- expect(snap.avgResponseTimeMs).toBe(150);
12
- });
13
-
14
- it('should track skill usage', () => {
15
- const analytics = new Analytics();
16
- analytics.recordSkillUsage('faq');
17
- analytics.recordSkillUsage('faq');
18
- analytics.recordSkillUsage('handoff');
19
- const snap = analytics.getSnapshot();
20
- expect(snap.skillUsage['faq']).toBe(2);
21
- expect(snap.skillUsage['handoff']).toBe(1);
22
- });
23
-
24
- it('should track errors', () => {
25
- const analytics = new Analytics();
26
- analytics.recordError();
27
- analytics.recordError();
28
- expect(analytics.getSnapshot().errorCount).toBe(2);
29
- });
30
-
31
- it('should track token usage', () => {
32
- const analytics = new Analytics();
33
- analytics.recordTokens(100, 50);
34
- analytics.recordTokens(200, 100);
35
- const snap = analytics.getSnapshot();
36
- expect(snap.tokenUsage.input).toBe(300);
37
- expect(snap.tokenUsage.output).toBe(150);
38
- expect(snap.tokenUsage.total).toBe(450);
39
- });
40
-
41
- it('should reset analytics', () => {
42
- const analytics = new Analytics();
43
- analytics.recordMessage(100);
44
- analytics.recordError();
45
- analytics.reset();
46
- const snap = analytics.getSnapshot();
47
- expect(snap.messagesProcessed).toBe(0);
48
- expect(snap.errorCount).toBe(0);
49
- });
50
- });
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Analytics } from '../src/analytics';
3
+
4
+ describe('Analytics', () => {
5
+ it('should track messages', () => {
6
+ const analytics = new Analytics();
7
+ analytics.recordMessage(100);
8
+ analytics.recordMessage(200);
9
+ const snap = analytics.getSnapshot();
10
+ expect(snap.messagesProcessed).toBe(2);
11
+ expect(snap.avgResponseTimeMs).toBe(150);
12
+ });
13
+
14
+ it('should track skill usage', () => {
15
+ const analytics = new Analytics();
16
+ analytics.recordSkillUsage('faq');
17
+ analytics.recordSkillUsage('faq');
18
+ analytics.recordSkillUsage('handoff');
19
+ const snap = analytics.getSnapshot();
20
+ expect(snap.skillUsage['faq']).toBe(2);
21
+ expect(snap.skillUsage['handoff']).toBe(1);
22
+ });
23
+
24
+ it('should track errors', () => {
25
+ const analytics = new Analytics();
26
+ analytics.recordError();
27
+ analytics.recordError();
28
+ expect(analytics.getSnapshot().errorCount).toBe(2);
29
+ });
30
+
31
+ it('should track token usage', () => {
32
+ const analytics = new Analytics();
33
+ analytics.recordTokens(100, 50);
34
+ analytics.recordTokens(200, 100);
35
+ const snap = analytics.getSnapshot();
36
+ expect(snap.tokenUsage.input).toBe(300);
37
+ expect(snap.tokenUsage.output).toBe(150);
38
+ expect(snap.tokenUsage.total).toBe(450);
39
+ });
40
+
41
+ it('should reset analytics', () => {
42
+ const analytics = new Analytics();
43
+ analytics.recordMessage(100);
44
+ analytics.recordError();
45
+ analytics.reset();
46
+ const snap = analytics.getSnapshot();
47
+ expect(snap.messagesProcessed).toBe(0);
48
+ expect(snap.errorCount).toBe(0);
49
+ });
50
+ });
@@ -1,39 +1,39 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import { WebChannel } from '../src/channels/web';
3
-
4
- describe('WebChannel', () => {
5
- let channel: WebChannel | null = null;
6
-
7
- afterEach(async () => {
8
- if (channel) {
9
- await channel.stop();
10
- channel = null;
11
- }
12
- });
13
-
14
- it('should create with default port', () => {
15
- channel = new WebChannel();
16
- expect(channel.type).toBe('web');
17
- });
18
-
19
- it('should start and stop', async () => {
20
- channel = new WebChannel(0); // random port
21
- channel.onMessage(async (msg) => ({
22
- id: 'resp_1',
23
- role: 'assistant',
24
- content: `Echo: ${msg.content}`,
25
- timestamp: Date.now(),
26
- }));
27
- await channel.start();
28
- await channel.stop();
29
- channel = null;
30
- });
31
-
32
- it('should handle health check', async () => {
33
- channel = new WebChannel(0);
34
- await channel.start();
35
- // Channel is running — health endpoint is available
36
- await channel.stop();
37
- channel = null;
38
- });
39
- });
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { WebChannel } from '../src/channels/web';
3
+
4
+ describe('WebChannel', () => {
5
+ let channel: WebChannel | null = null;
6
+
7
+ afterEach(async () => {
8
+ if (channel) {
9
+ await channel.stop();
10
+ channel = null;
11
+ }
12
+ });
13
+
14
+ it('should create with default port', () => {
15
+ channel = new WebChannel();
16
+ expect(channel.type).toBe('web');
17
+ });
18
+
19
+ it('should start and stop', async () => {
20
+ channel = new WebChannel(0); // random port
21
+ channel.onMessage(async (msg) => ({
22
+ id: 'resp_1',
23
+ role: 'assistant',
24
+ content: `Echo: ${msg.content}`,
25
+ timestamp: Date.now(),
26
+ }));
27
+ await channel.start();
28
+ await channel.stop();
29
+ channel = null;
30
+ });
31
+
32
+ it('should handle health check', async () => {
33
+ channel = new WebChannel(0);
34
+ await channel.start();
35
+ // Channel is running — health endpoint is available
36
+ await channel.stop();
37
+ channel = null;
38
+ });
39
+ });