opc-agent 1.3.1 → 1.3.2

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 (160) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/CONTRIBUTING.md +75 -75
  3. package/README.md +358 -235
  4. package/README.zh-CN.md +415 -415
  5. package/dist/cli.js +34 -118
  6. package/dist/core/dashboard.d.ts +35 -0
  7. package/dist/core/dashboard.js +157 -0
  8. package/dist/core/priority.d.ts +52 -0
  9. package/dist/core/priority.js +102 -0
  10. package/dist/deploy/hermes.js +22 -22
  11. package/dist/deploy/openclaw.js +40 -31
  12. package/dist/index.d.ts +10 -3
  13. package/dist/index.js +15 -6
  14. package/dist/schema/oad.d.ts +2 -1
  15. package/dist/templates/code-reviewer.d.ts +8 -0
  16. package/dist/templates/code-reviewer.js +9 -5
  17. package/dist/templates/customer-service.d.ts +8 -0
  18. package/dist/templates/customer-service.js +6 -2
  19. package/dist/templates/data-analyst.d.ts +8 -0
  20. package/dist/templates/data-analyst.js +9 -5
  21. package/dist/templates/knowledge-base.d.ts +8 -0
  22. package/dist/templates/knowledge-base.js +6 -2
  23. package/dist/templates/sales-assistant.d.ts +8 -0
  24. package/dist/templates/sales-assistant.js +8 -4
  25. package/dist/templates/teacher.d.ts +8 -0
  26. package/dist/templates/teacher.js +10 -6
  27. package/docs/.vitepress/config.ts +103 -103
  28. package/docs/api/cli.md +48 -48
  29. package/docs/api/oad-schema.md +64 -64
  30. package/docs/api/sdk.md +80 -80
  31. package/docs/guide/concepts.md +51 -51
  32. package/docs/guide/configuration.md +79 -79
  33. package/docs/guide/deployment.md +42 -42
  34. package/docs/guide/getting-started.md +44 -44
  35. package/docs/guide/templates.md +28 -28
  36. package/docs/guide/testing.md +84 -84
  37. package/docs/index.md +27 -27
  38. package/docs/zh/api/cli.md +54 -54
  39. package/docs/zh/api/oad-schema.md +87 -87
  40. package/docs/zh/api/sdk.md +102 -102
  41. package/docs/zh/guide/concepts.md +104 -104
  42. package/docs/zh/guide/configuration.md +135 -135
  43. package/docs/zh/guide/deployment.md +81 -81
  44. package/docs/zh/guide/getting-started.md +82 -82
  45. package/docs/zh/guide/templates.md +84 -84
  46. package/docs/zh/guide/testing.md +88 -88
  47. package/docs/zh/index.md +27 -27
  48. package/examples/customer-service-demo/README.md +90 -90
  49. package/examples/customer-service-demo/oad.yaml +107 -107
  50. package/package.json +1 -1
  51. package/src/analytics/index.ts +66 -66
  52. package/src/channels/discord.ts +192 -192
  53. package/src/channels/email.ts +177 -177
  54. package/src/channels/feishu.ts +236 -236
  55. package/src/channels/index.ts +15 -15
  56. package/src/channels/slack.ts +160 -160
  57. package/src/channels/telegram.ts +90 -90
  58. package/src/channels/voice.ts +106 -106
  59. package/src/channels/webhook.ts +199 -199
  60. package/src/channels/websocket.ts +87 -87
  61. package/src/channels/wechat.ts +149 -149
  62. package/src/cli.ts +32 -124
  63. package/src/core/a2a.ts +143 -143
  64. package/src/core/agent.ts +152 -152
  65. package/src/core/analytics-engine.ts +186 -186
  66. package/src/core/auth.ts +57 -57
  67. package/src/core/cache.ts +141 -141
  68. package/src/core/compose.ts +77 -77
  69. package/src/core/config.ts +14 -14
  70. package/src/core/dashboard.ts +219 -0
  71. package/src/core/errors.ts +148 -148
  72. package/src/core/hitl.ts +138 -138
  73. package/src/core/logger.ts +57 -57
  74. package/src/core/orchestrator.ts +215 -215
  75. package/src/core/performance.ts +187 -187
  76. package/src/core/priority.ts +140 -0
  77. package/src/core/rate-limiter.ts +128 -128
  78. package/src/core/room.ts +109 -109
  79. package/src/core/runtime.ts +152 -152
  80. package/src/core/sandbox.ts +101 -101
  81. package/src/core/security.ts +171 -171
  82. package/src/core/types.ts +68 -68
  83. package/src/core/versioning.ts +106 -106
  84. package/src/core/watch.ts +178 -178
  85. package/src/core/workflow.ts +235 -235
  86. package/src/deploy/hermes.ts +156 -156
  87. package/src/deploy/openclaw.ts +200 -190
  88. package/src/dtv/data.ts +29 -0
  89. package/src/dtv/trust.ts +43 -0
  90. package/src/dtv/value.ts +47 -0
  91. package/src/i18n/index.ts +216 -216
  92. package/src/index.ts +10 -3
  93. package/src/marketplace/index.ts +223 -0
  94. package/src/memory/deepbrain.ts +108 -108
  95. package/src/memory/index.ts +34 -34
  96. package/src/plugins/index.ts +208 -208
  97. package/src/schema/oad.ts +155 -154
  98. package/src/skills/base.ts +16 -16
  99. package/src/skills/document.ts +100 -100
  100. package/src/skills/http.ts +35 -35
  101. package/src/skills/index.ts +27 -27
  102. package/src/skills/scheduler.ts +80 -80
  103. package/src/skills/webhook-trigger.ts +59 -59
  104. package/src/templates/code-reviewer.ts +34 -30
  105. package/src/templates/customer-service.ts +80 -76
  106. package/src/templates/data-analyst.ts +70 -66
  107. package/src/templates/executive-assistant.ts +71 -71
  108. package/src/templates/financial-advisor.ts +60 -60
  109. package/src/templates/knowledge-base.ts +31 -27
  110. package/src/templates/legal-assistant.ts +71 -71
  111. package/src/templates/sales-assistant.ts +79 -75
  112. package/src/templates/teacher.ts +79 -75
  113. package/src/testing/index.ts +181 -181
  114. package/src/tools/calculator.ts +73 -73
  115. package/src/tools/datetime.ts +149 -149
  116. package/src/tools/json-transform.ts +187 -187
  117. package/src/tools/mcp.ts +76 -76
  118. package/src/tools/text-analysis.ts +116 -116
  119. package/templates/Dockerfile +15 -15
  120. package/templates/code-reviewer/README.md +27 -27
  121. package/templates/code-reviewer/oad.yaml +41 -41
  122. package/templates/customer-service/README.md +22 -22
  123. package/templates/customer-service/oad.yaml +36 -36
  124. package/templates/docker-compose.yml +21 -21
  125. package/templates/ecommerce-assistant/README.md +45 -45
  126. package/templates/ecommerce-assistant/oad.yaml +47 -47
  127. package/templates/knowledge-base/README.md +28 -28
  128. package/templates/knowledge-base/oad.yaml +38 -38
  129. package/templates/sales-assistant/README.md +26 -26
  130. package/templates/sales-assistant/oad.yaml +43 -43
  131. package/templates/tech-support/README.md +43 -43
  132. package/templates/tech-support/oad.yaml +45 -45
  133. package/tests/a2a.test.ts +66 -66
  134. package/tests/agent.test.ts +72 -72
  135. package/tests/analytics.test.ts +50 -50
  136. package/tests/channel.test.ts +39 -39
  137. package/tests/e2e.test.ts +134 -134
  138. package/tests/errors.test.ts +83 -83
  139. package/tests/hitl.test.ts +71 -71
  140. package/tests/i18n.test.ts +41 -41
  141. package/tests/mcp.test.ts +54 -54
  142. package/tests/oad.test.ts +68 -68
  143. package/tests/performance.test.ts +115 -115
  144. package/tests/plugin.test.ts +74 -74
  145. package/tests/room.test.ts +106 -106
  146. package/tests/runtime.test.ts +42 -42
  147. package/tests/sandbox.test.ts +46 -46
  148. package/tests/security.test.ts +60 -60
  149. package/tests/templates.test.ts +77 -77
  150. package/tests/v070.test.ts +76 -76
  151. package/tests/versioning.test.ts +75 -75
  152. package/tests/voice.test.ts +61 -61
  153. package/tests/webhook.test.ts +29 -29
  154. package/tests/workflow.test.ts +143 -143
  155. package/tsconfig.json +19 -19
  156. package/vitest.config.ts +9 -9
  157. package/.github/workflows/ci.yml +0 -24
  158. package/dist/traces/index.d.ts +0 -49
  159. package/dist/traces/index.js +0 -102
  160. package/src/traces/index.ts +0 -132
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
+ });