opc-agent 1.3.2 → 2.0.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/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +14 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- package/.github/workflows/ci.yml +24 -0
- package/CHANGELOG.md +48 -63
- package/CONTRIBUTING.md +21 -60
- package/README.md +284 -348
- package/README.zh-CN.md +415 -415
- package/dist/channels/slack.js +93 -10
- package/dist/channels/telegram.d.ts +30 -9
- package/dist/channels/telegram.js +125 -33
- package/dist/channels/web.d.ts +10 -0
- package/dist/channels/web.js +33 -2
- package/dist/cli.js +667 -65
- package/dist/core/agent.d.ts +23 -0
- package/dist/core/agent.js +120 -3
- package/dist/core/runtime.d.ts +5 -0
- package/dist/core/runtime.js +71 -0
- package/dist/core/scheduler.d.ts +52 -0
- package/dist/core/scheduler.js +168 -0
- package/dist/core/subagent.d.ts +28 -0
- package/dist/core/subagent.js +65 -0
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.js +134 -0
- package/dist/deploy/hermes.js +22 -22
- package/dist/deploy/openclaw.js +31 -40
- package/dist/index.d.ts +10 -10
- package/dist/index.js +22 -15
- package/dist/providers/index.d.ts +6 -2
- package/dist/providers/index.js +22 -9
- package/dist/schema/oad.d.ts +180 -6
- package/dist/schema/oad.js +12 -1
- package/dist/skills/auto-learn.d.ts +28 -0
- package/dist/skills/auto-learn.js +257 -0
- package/dist/templates/code-reviewer.d.ts +0 -8
- package/dist/templates/code-reviewer.js +5 -9
- package/dist/templates/customer-service.d.ts +0 -8
- package/dist/templates/customer-service.js +2 -6
- package/dist/templates/data-analyst.d.ts +0 -8
- package/dist/templates/data-analyst.js +5 -9
- package/dist/templates/knowledge-base.d.ts +0 -8
- package/dist/templates/knowledge-base.js +2 -6
- package/dist/templates/sales-assistant.d.ts +0 -8
- package/dist/templates/sales-assistant.js +4 -8
- package/dist/templates/teacher.d.ts +0 -8
- package/dist/templates/teacher.js +6 -10
- package/dist/tools/builtin/datetime.d.ts +3 -0
- package/dist/tools/builtin/datetime.js +44 -0
- package/dist/tools/builtin/file.d.ts +3 -0
- package/dist/tools/builtin/file.js +151 -0
- package/dist/tools/builtin/index.d.ts +15 -0
- package/dist/tools/builtin/index.js +30 -0
- package/dist/tools/builtin/shell.d.ts +3 -0
- package/dist/tools/builtin/shell.js +43 -0
- package/dist/tools/builtin/web.d.ts +3 -0
- package/dist/tools/builtin/web.js +37 -0
- package/dist/tools/mcp-client.d.ts +24 -0
- package/dist/tools/mcp-client.js +119 -0
- package/dist/traces/index.d.ts +49 -0
- package/dist/traces/index.js +102 -0
- package/docs/.vitepress/config.ts +103 -103
- package/docs/api/cli.md +48 -48
- package/docs/api/oad-schema.md +64 -64
- package/docs/api/sdk.md +80 -80
- package/docs/guide/concepts.md +51 -51
- package/docs/guide/configuration.md +79 -79
- package/docs/guide/deployment.md +42 -42
- package/docs/guide/getting-started.md +44 -44
- package/docs/guide/templates.md +28 -28
- package/docs/guide/testing.md +84 -84
- package/docs/index.md +27 -27
- package/docs/zh/api/cli.md +54 -54
- package/docs/zh/api/oad-schema.md +87 -87
- package/docs/zh/api/sdk.md +102 -102
- package/docs/zh/guide/concepts.md +104 -104
- package/docs/zh/guide/configuration.md +135 -135
- package/docs/zh/guide/deployment.md +81 -81
- package/docs/zh/guide/getting-started.md +82 -82
- package/docs/zh/guide/templates.md +84 -84
- package/docs/zh/guide/testing.md +88 -88
- package/docs/zh/index.md +27 -27
- package/examples/README.md +22 -0
- package/examples/basic-agent.ts +90 -0
- package/examples/brain-integration.ts +71 -0
- package/examples/customer-service-demo/README.md +90 -90
- package/examples/customer-service-demo/oad.yaml +107 -107
- package/examples/multi-channel.ts +74 -0
- package/package.json +1 -1
- package/src/analytics/index.ts +66 -66
- package/src/channels/discord.ts +192 -192
- package/src/channels/email.ts +177 -177
- package/src/channels/feishu.ts +236 -236
- package/src/channels/index.ts +15 -15
- package/src/channels/slack.ts +217 -160
- package/src/channels/telegram.ts +155 -33
- package/src/channels/voice.ts +106 -106
- package/src/channels/web.ts +38 -2
- package/src/channels/webhook.ts +199 -199
- package/src/channels/websocket.ts +87 -87
- package/src/channels/wechat.ts +149 -149
- package/src/cli.ts +697 -63
- package/src/core/a2a.ts +143 -143
- package/src/core/agent.ts +146 -3
- package/src/core/analytics-engine.ts +186 -186
- package/src/core/auth.ts +57 -57
- package/src/core/cache.ts +141 -141
- package/src/core/compose.ts +77 -77
- package/src/core/config.ts +14 -14
- package/src/core/errors.ts +148 -148
- package/src/core/hitl.ts +138 -138
- package/src/core/logger.ts +57 -57
- package/src/core/orchestrator.ts +215 -215
- package/src/core/performance.ts +187 -187
- package/src/core/rate-limiter.ts +128 -128
- package/src/core/room.ts +109 -109
- package/src/core/runtime.ts +230 -152
- package/src/core/sandbox.ts +101 -101
- package/src/core/scheduler.ts +187 -0
- package/src/core/security.ts +171 -171
- package/src/core/subagent.ts +98 -0
- package/src/core/types.ts +68 -68
- package/src/core/versioning.ts +106 -106
- package/src/core/watch.ts +178 -178
- package/src/core/workflow.ts +235 -235
- package/src/daemon.ts +96 -0
- package/src/deploy/hermes.ts +156 -156
- package/src/deploy/openclaw.ts +190 -200
- package/src/i18n/index.ts +216 -216
- package/src/index.ts +14 -10
- package/src/memory/deepbrain.ts +108 -108
- package/src/memory/index.ts +34 -34
- package/src/plugins/index.ts +208 -208
- package/src/providers/index.ts +354 -331
- package/src/schema/oad.ts +14 -2
- package/src/skills/auto-learn.ts +262 -0
- package/src/skills/base.ts +16 -16
- package/src/skills/document.ts +100 -100
- package/src/skills/http.ts +35 -35
- package/src/skills/index.ts +27 -27
- package/src/skills/scheduler.ts +80 -80
- package/src/skills/webhook-trigger.ts +59 -59
- package/src/templates/code-reviewer.ts +30 -34
- package/src/templates/customer-service.ts +76 -80
- package/src/templates/data-analyst.ts +66 -70
- package/src/templates/executive-assistant.ts +71 -71
- package/src/templates/financial-advisor.ts +60 -60
- package/src/templates/knowledge-base.ts +27 -31
- package/src/templates/legal-assistant.ts +71 -71
- package/src/templates/sales-assistant.ts +75 -79
- package/src/templates/teacher.ts +75 -79
- package/src/testing/index.ts +181 -181
- package/src/tools/builtin/datetime.ts +41 -0
- package/src/tools/builtin/file.ts +107 -0
- package/src/tools/builtin/index.ts +28 -0
- package/src/tools/builtin/shell.ts +43 -0
- package/src/tools/builtin/web.ts +35 -0
- package/src/tools/calculator.ts +73 -73
- package/src/tools/datetime.ts +149 -149
- package/src/tools/json-transform.ts +187 -187
- package/src/tools/mcp-client.ts +131 -0
- package/src/tools/mcp.ts +76 -76
- package/src/tools/text-analysis.ts +116 -116
- package/src/traces/index.ts +132 -0
- package/templates/Dockerfile +15 -15
- package/templates/code-reviewer/README.md +27 -27
- package/templates/code-reviewer/oad.yaml +41 -41
- package/templates/customer-service/README.md +22 -22
- package/templates/customer-service/oad.yaml +36 -36
- package/templates/docker-compose.yml +21 -21
- package/templates/ecommerce-assistant/README.md +45 -45
- package/templates/ecommerce-assistant/oad.yaml +47 -47
- package/templates/knowledge-base/README.md +28 -28
- package/templates/knowledge-base/oad.yaml +38 -38
- package/templates/sales-assistant/README.md +26 -26
- package/templates/sales-assistant/oad.yaml +43 -43
- package/templates/tech-support/README.md +43 -43
- package/templates/tech-support/oad.yaml +45 -45
- package/test-agent/Dockerfile +9 -0
- package/test-agent/README.md +50 -0
- package/test-agent/agent.yaml +23 -0
- package/test-agent/docker-compose.yml +11 -0
- package/test-agent/oad.yaml +31 -0
- package/test-agent/package-lock.json +1492 -0
- package/test-agent/package.json +18 -0
- package/test-agent/src/index.ts +24 -0
- package/test-agent/src/skills/echo.ts +15 -0
- package/test-agent/tsconfig.json +25 -0
- package/tests/a2a.test.ts +66 -66
- package/tests/agent.test.ts +72 -72
- package/tests/analytics.test.ts +50 -50
- package/tests/auto-learn.test.ts +105 -0
- package/tests/builtin-tools.test.ts +83 -0
- package/tests/channel.test.ts +39 -39
- package/tests/cli.test.ts +46 -0
- package/tests/e2e.test.ts +134 -134
- package/tests/errors.test.ts +83 -83
- package/tests/hitl.test.ts +71 -71
- package/tests/i18n.test.ts +41 -41
- package/tests/mcp.test.ts +54 -54
- package/tests/oad.test.ts +68 -68
- package/tests/performance.test.ts +115 -115
- package/tests/plugin.test.ts +74 -74
- package/tests/room.test.ts +106 -106
- package/tests/runtime.test.ts +42 -42
- package/tests/sandbox.test.ts +46 -46
- package/tests/security.test.ts +60 -60
- package/tests/subagent.test.ts +130 -0
- package/tests/telegram-discord.test.ts +60 -0
- package/tests/templates.test.ts +77 -77
- package/tests/v070.test.ts +76 -76
- package/tests/versioning.test.ts +75 -75
- package/tests/voice.test.ts +61 -61
- package/tests/webhook.test.ts +29 -29
- package/tests/workflow.test.ts +143 -143
- package/tsconfig.json +19 -19
- package/vitest.config.ts +9 -9
- package/dist/core/dashboard.d.ts +0 -35
- package/dist/core/dashboard.js +0 -157
- package/dist/core/priority.d.ts +0 -52
- package/dist/core/priority.js +0 -102
- package/src/core/dashboard.ts +0 -219
- package/src/core/priority.ts +0 -140
- package/src/dtv/data.ts +0 -29
- package/src/dtv/trust.ts +0 -43
- package/src/dtv/value.ts +0 -47
- package/src/marketplace/index.ts +0 -223
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { ConnectionPool, RequestBatcher, LazyLoader } from '../src/core/performance';
|
|
3
|
-
|
|
4
|
-
describe('ConnectionPool', () => {
|
|
5
|
-
it('should acquire and release connections', () => {
|
|
6
|
-
const pool = new ConnectionPool(3);
|
|
7
|
-
const conn = pool.acquire('openai');
|
|
8
|
-
expect(conn.inUse).toBe(true);
|
|
9
|
-
pool.release(conn.id);
|
|
10
|
-
const stats = pool.getStats();
|
|
11
|
-
expect(stats['openai'].total).toBe(1);
|
|
12
|
-
expect(stats['openai'].inUse).toBe(0);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should reuse released connections', () => {
|
|
16
|
-
const pool = new ConnectionPool(2);
|
|
17
|
-
const c1 = pool.acquire('openai');
|
|
18
|
-
pool.release(c1.id);
|
|
19
|
-
const c2 = pool.acquire('openai');
|
|
20
|
-
expect(c2.id).toBe(c1.id);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should drain all connections', () => {
|
|
24
|
-
const pool = new ConnectionPool();
|
|
25
|
-
pool.acquire('openai');
|
|
26
|
-
pool.acquire('deepseek');
|
|
27
|
-
pool.drain();
|
|
28
|
-
expect(pool.getStats()).toEqual({});
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
describe('RequestBatcher', () => {
|
|
33
|
-
it('should batch requests', async () => {
|
|
34
|
-
const batcher = new RequestBatcher<string>(
|
|
35
|
-
async (batch) => batch.map(s => s.toUpperCase()),
|
|
36
|
-
2,
|
|
37
|
-
10,
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
const [r1, r2] = await Promise.all([
|
|
41
|
-
batcher.add('hello'),
|
|
42
|
-
batcher.add('world'),
|
|
43
|
-
]);
|
|
44
|
-
expect(r1).toBe('HELLO');
|
|
45
|
-
expect(r2).toBe('WORLD');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should flush on timer', async () => {
|
|
49
|
-
const batcher = new RequestBatcher<number>(
|
|
50
|
-
async (batch) => batch.map(n => n * 2),
|
|
51
|
-
10,
|
|
52
|
-
20,
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const result = await batcher.add(5);
|
|
56
|
-
expect(result).toBe(10);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should track pending count', () => {
|
|
60
|
-
const batcher = new RequestBatcher<string>(
|
|
61
|
-
async (batch) => batch,
|
|
62
|
-
100,
|
|
63
|
-
10000,
|
|
64
|
-
);
|
|
65
|
-
batcher.add('a');
|
|
66
|
-
batcher.add('b');
|
|
67
|
-
expect(batcher.pending).toBe(2);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
describe('LazyLoader', () => {
|
|
72
|
-
it('should lazily load items', async () => {
|
|
73
|
-
const loader = new LazyLoader<string>();
|
|
74
|
-
let loadCount = 0;
|
|
75
|
-
loader.register('greeting', async () => { loadCount++; return 'hello'; });
|
|
76
|
-
|
|
77
|
-
expect(loader.isLoaded('greeting')).toBe(false);
|
|
78
|
-
const val = await loader.get('greeting');
|
|
79
|
-
expect(val).toBe('hello');
|
|
80
|
-
expect(loader.isLoaded('greeting')).toBe(true);
|
|
81
|
-
|
|
82
|
-
// Second call should use cache
|
|
83
|
-
await loader.get('greeting');
|
|
84
|
-
expect(loadCount).toBe(1);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
it('should throw for unregistered items', async () => {
|
|
88
|
-
const loader = new LazyLoader();
|
|
89
|
-
await expect(loader.get('unknown')).rejects.toThrow('No loader registered');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('should evict and reload', async () => {
|
|
93
|
-
const loader = new LazyLoader<number>();
|
|
94
|
-
let count = 0;
|
|
95
|
-
loader.register('counter', async () => ++count);
|
|
96
|
-
|
|
97
|
-
await loader.get('counter');
|
|
98
|
-
expect(loader.loadedCount).toBe(1);
|
|
99
|
-
loader.evict('counter');
|
|
100
|
-
expect(loader.loadedCount).toBe(0);
|
|
101
|
-
await loader.get('counter');
|
|
102
|
-
expect(count).toBe(2);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should clear all', async () => {
|
|
106
|
-
const loader = new LazyLoader<string>();
|
|
107
|
-
loader.register('a', async () => 'a');
|
|
108
|
-
loader.register('b', async () => 'b');
|
|
109
|
-
await loader.get('a');
|
|
110
|
-
await loader.get('b');
|
|
111
|
-
loader.clear();
|
|
112
|
-
expect(loader.loadedCount).toBe(0);
|
|
113
|
-
expect(loader.registeredCount).toBe(2);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ConnectionPool, RequestBatcher, LazyLoader } from '../src/core/performance';
|
|
3
|
+
|
|
4
|
+
describe('ConnectionPool', () => {
|
|
5
|
+
it('should acquire and release connections', () => {
|
|
6
|
+
const pool = new ConnectionPool(3);
|
|
7
|
+
const conn = pool.acquire('openai');
|
|
8
|
+
expect(conn.inUse).toBe(true);
|
|
9
|
+
pool.release(conn.id);
|
|
10
|
+
const stats = pool.getStats();
|
|
11
|
+
expect(stats['openai'].total).toBe(1);
|
|
12
|
+
expect(stats['openai'].inUse).toBe(0);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should reuse released connections', () => {
|
|
16
|
+
const pool = new ConnectionPool(2);
|
|
17
|
+
const c1 = pool.acquire('openai');
|
|
18
|
+
pool.release(c1.id);
|
|
19
|
+
const c2 = pool.acquire('openai');
|
|
20
|
+
expect(c2.id).toBe(c1.id);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should drain all connections', () => {
|
|
24
|
+
const pool = new ConnectionPool();
|
|
25
|
+
pool.acquire('openai');
|
|
26
|
+
pool.acquire('deepseek');
|
|
27
|
+
pool.drain();
|
|
28
|
+
expect(pool.getStats()).toEqual({});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('RequestBatcher', () => {
|
|
33
|
+
it('should batch requests', async () => {
|
|
34
|
+
const batcher = new RequestBatcher<string>(
|
|
35
|
+
async (batch) => batch.map(s => s.toUpperCase()),
|
|
36
|
+
2,
|
|
37
|
+
10,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const [r1, r2] = await Promise.all([
|
|
41
|
+
batcher.add('hello'),
|
|
42
|
+
batcher.add('world'),
|
|
43
|
+
]);
|
|
44
|
+
expect(r1).toBe('HELLO');
|
|
45
|
+
expect(r2).toBe('WORLD');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should flush on timer', async () => {
|
|
49
|
+
const batcher = new RequestBatcher<number>(
|
|
50
|
+
async (batch) => batch.map(n => n * 2),
|
|
51
|
+
10,
|
|
52
|
+
20,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const result = await batcher.add(5);
|
|
56
|
+
expect(result).toBe(10);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should track pending count', () => {
|
|
60
|
+
const batcher = new RequestBatcher<string>(
|
|
61
|
+
async (batch) => batch,
|
|
62
|
+
100,
|
|
63
|
+
10000,
|
|
64
|
+
);
|
|
65
|
+
batcher.add('a');
|
|
66
|
+
batcher.add('b');
|
|
67
|
+
expect(batcher.pending).toBe(2);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('LazyLoader', () => {
|
|
72
|
+
it('should lazily load items', async () => {
|
|
73
|
+
const loader = new LazyLoader<string>();
|
|
74
|
+
let loadCount = 0;
|
|
75
|
+
loader.register('greeting', async () => { loadCount++; return 'hello'; });
|
|
76
|
+
|
|
77
|
+
expect(loader.isLoaded('greeting')).toBe(false);
|
|
78
|
+
const val = await loader.get('greeting');
|
|
79
|
+
expect(val).toBe('hello');
|
|
80
|
+
expect(loader.isLoaded('greeting')).toBe(true);
|
|
81
|
+
|
|
82
|
+
// Second call should use cache
|
|
83
|
+
await loader.get('greeting');
|
|
84
|
+
expect(loadCount).toBe(1);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should throw for unregistered items', async () => {
|
|
88
|
+
const loader = new LazyLoader();
|
|
89
|
+
await expect(loader.get('unknown')).rejects.toThrow('No loader registered');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should evict and reload', async () => {
|
|
93
|
+
const loader = new LazyLoader<number>();
|
|
94
|
+
let count = 0;
|
|
95
|
+
loader.register('counter', async () => ++count);
|
|
96
|
+
|
|
97
|
+
await loader.get('counter');
|
|
98
|
+
expect(loader.loadedCount).toBe(1);
|
|
99
|
+
loader.evict('counter');
|
|
100
|
+
expect(loader.loadedCount).toBe(0);
|
|
101
|
+
await loader.get('counter');
|
|
102
|
+
expect(count).toBe(2);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should clear all', async () => {
|
|
106
|
+
const loader = new LazyLoader<string>();
|
|
107
|
+
loader.register('a', async () => 'a');
|
|
108
|
+
loader.register('b', async () => 'b');
|
|
109
|
+
await loader.get('a');
|
|
110
|
+
await loader.get('b');
|
|
111
|
+
loader.clear();
|
|
112
|
+
expect(loader.loadedCount).toBe(0);
|
|
113
|
+
expect(loader.registeredCount).toBe(2);
|
|
114
|
+
});
|
|
115
|
+
});
|
package/tests/plugin.test.ts
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { PluginManager } from '../src/plugins';
|
|
3
|
-
import type { IPlugin } from '../src/plugins';
|
|
4
|
-
|
|
5
|
-
describe('Plugin System', () => {
|
|
6
|
-
it('should register and list plugins', () => {
|
|
7
|
-
const pm = new PluginManager();
|
|
8
|
-
const plugin: IPlugin = { name: 'test-plugin', version: '1.0.0', description: 'Test' };
|
|
9
|
-
pm.register(plugin);
|
|
10
|
-
expect(pm.has('test-plugin')).toBe(true);
|
|
11
|
-
expect(pm.list().length).toBe(1);
|
|
12
|
-
expect(pm.list()[0].name).toBe('test-plugin');
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('should run lifecycle hooks', async () => {
|
|
16
|
-
const pm = new PluginManager();
|
|
17
|
-
const calls: string[] = [];
|
|
18
|
-
|
|
19
|
-
pm.register({
|
|
20
|
-
name: 'hook-plugin',
|
|
21
|
-
version: '1.0.0',
|
|
22
|
-
hooks: {
|
|
23
|
-
beforeInit: async () => { calls.push('beforeInit'); },
|
|
24
|
-
afterInit: async () => { calls.push('afterInit'); },
|
|
25
|
-
beforeShutdown: async () => { calls.push('beforeShutdown'); },
|
|
26
|
-
},
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
await pm.runHook('beforeInit');
|
|
30
|
-
await pm.runHook('afterInit');
|
|
31
|
-
await pm.runHook('beforeShutdown');
|
|
32
|
-
expect(calls).toEqual(['beforeInit', 'afterInit', 'beforeShutdown']);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should collect skills from plugins', () => {
|
|
36
|
-
const pm = new PluginManager();
|
|
37
|
-
pm.register({
|
|
38
|
-
name: 'skill-plugin',
|
|
39
|
-
version: '1.0.0',
|
|
40
|
-
skills: [{
|
|
41
|
-
name: 'test-skill',
|
|
42
|
-
description: 'Test',
|
|
43
|
-
execute: async () => ({ handled: false, confidence: 0 }),
|
|
44
|
-
}],
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
expect(pm.getAllSkills().length).toBe(1);
|
|
48
|
-
expect(pm.getAllSkills()[0].name).toBe('test-skill');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should unregister plugins', () => {
|
|
52
|
-
const pm = new PluginManager();
|
|
53
|
-
pm.register({ name: 'temp', version: '1.0.0' });
|
|
54
|
-
pm.unregister('temp');
|
|
55
|
-
expect(pm.has('temp')).toBe(false);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('should run hooks from multiple plugins in order', async () => {
|
|
59
|
-
const pm = new PluginManager();
|
|
60
|
-
const order: string[] = [];
|
|
61
|
-
|
|
62
|
-
pm.register({
|
|
63
|
-
name: 'p1', version: '1.0.0',
|
|
64
|
-
hooks: { beforeInit: async () => { order.push('p1'); } },
|
|
65
|
-
});
|
|
66
|
-
pm.register({
|
|
67
|
-
name: 'p2', version: '1.0.0',
|
|
68
|
-
hooks: { beforeInit: async () => { order.push('p2'); } },
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
await pm.runHook('beforeInit');
|
|
72
|
-
expect(order).toEqual(['p1', 'p2']);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { PluginManager } from '../src/plugins';
|
|
3
|
+
import type { IPlugin } from '../src/plugins';
|
|
4
|
+
|
|
5
|
+
describe('Plugin System', () => {
|
|
6
|
+
it('should register and list plugins', () => {
|
|
7
|
+
const pm = new PluginManager();
|
|
8
|
+
const plugin: IPlugin = { name: 'test-plugin', version: '1.0.0', description: 'Test' };
|
|
9
|
+
pm.register(plugin);
|
|
10
|
+
expect(pm.has('test-plugin')).toBe(true);
|
|
11
|
+
expect(pm.list().length).toBe(1);
|
|
12
|
+
expect(pm.list()[0].name).toBe('test-plugin');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should run lifecycle hooks', async () => {
|
|
16
|
+
const pm = new PluginManager();
|
|
17
|
+
const calls: string[] = [];
|
|
18
|
+
|
|
19
|
+
pm.register({
|
|
20
|
+
name: 'hook-plugin',
|
|
21
|
+
version: '1.0.0',
|
|
22
|
+
hooks: {
|
|
23
|
+
beforeInit: async () => { calls.push('beforeInit'); },
|
|
24
|
+
afterInit: async () => { calls.push('afterInit'); },
|
|
25
|
+
beforeShutdown: async () => { calls.push('beforeShutdown'); },
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
await pm.runHook('beforeInit');
|
|
30
|
+
await pm.runHook('afterInit');
|
|
31
|
+
await pm.runHook('beforeShutdown');
|
|
32
|
+
expect(calls).toEqual(['beforeInit', 'afterInit', 'beforeShutdown']);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should collect skills from plugins', () => {
|
|
36
|
+
const pm = new PluginManager();
|
|
37
|
+
pm.register({
|
|
38
|
+
name: 'skill-plugin',
|
|
39
|
+
version: '1.0.0',
|
|
40
|
+
skills: [{
|
|
41
|
+
name: 'test-skill',
|
|
42
|
+
description: 'Test',
|
|
43
|
+
execute: async () => ({ handled: false, confidence: 0 }),
|
|
44
|
+
}],
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
expect(pm.getAllSkills().length).toBe(1);
|
|
48
|
+
expect(pm.getAllSkills()[0].name).toBe('test-skill');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should unregister plugins', () => {
|
|
52
|
+
const pm = new PluginManager();
|
|
53
|
+
pm.register({ name: 'temp', version: '1.0.0' });
|
|
54
|
+
pm.unregister('temp');
|
|
55
|
+
expect(pm.has('temp')).toBe(false);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should run hooks from multiple plugins in order', async () => {
|
|
59
|
+
const pm = new PluginManager();
|
|
60
|
+
const order: string[] = [];
|
|
61
|
+
|
|
62
|
+
pm.register({
|
|
63
|
+
name: 'p1', version: '1.0.0',
|
|
64
|
+
hooks: { beforeInit: async () => { order.push('p1'); } },
|
|
65
|
+
});
|
|
66
|
+
pm.register({
|
|
67
|
+
name: 'p2', version: '1.0.0',
|
|
68
|
+
hooks: { beforeInit: async () => { order.push('p2'); } },
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await pm.runHook('beforeInit');
|
|
72
|
+
expect(order).toEqual(['p1', 'p2']);
|
|
73
|
+
});
|
|
74
|
+
});
|
package/tests/room.test.ts
CHANGED
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { Room } from '../src/core/room';
|
|
3
|
-
import { BaseAgent } from '../src/core/agent';
|
|
4
|
-
|
|
5
|
-
function makeAgent(name: string): BaseAgent {
|
|
6
|
-
const agent = new BaseAgent({ name });
|
|
7
|
-
// Synchronously set to ready by calling init
|
|
8
|
-
return agent;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
describe('Room System', () => {
|
|
12
|
-
it('should create a room with a name', () => {
|
|
13
|
-
const room = new Room('test-room');
|
|
14
|
-
expect(room.name).toBe('test-room');
|
|
15
|
-
expect(room.getAgents()).toEqual([]);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('should add and remove agents', () => {
|
|
19
|
-
const room = new Room('office');
|
|
20
|
-
const agent = makeAgent('agent-1');
|
|
21
|
-
room.addAgent(agent);
|
|
22
|
-
expect(room.getAgents()).toEqual(['agent-1']);
|
|
23
|
-
room.removeAgent('agent-1');
|
|
24
|
-
expect(room.getAgents()).toEqual([]);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('should emit events on join/leave', () => {
|
|
28
|
-
const room = new Room('office');
|
|
29
|
-
const events: string[] = [];
|
|
30
|
-
room.on('agent:join', (name) => events.push(`join:${name}`));
|
|
31
|
-
room.on('agent:leave', (name) => events.push(`leave:${name}`));
|
|
32
|
-
|
|
33
|
-
const agent = makeAgent('a1');
|
|
34
|
-
room.addAgent(agent);
|
|
35
|
-
room.removeAgent('a1');
|
|
36
|
-
expect(events).toEqual(['join:a1', 'leave:a1']);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should support topic subscriptions', () => {
|
|
40
|
-
const room = new Room('office');
|
|
41
|
-
room.subscribe('agent-1', 'alerts');
|
|
42
|
-
room.subscribe('agent-2', 'alerts');
|
|
43
|
-
expect(room.getSubscribers('alerts')).toEqual(['agent-1', 'agent-2']);
|
|
44
|
-
room.unsubscribe('agent-1', 'alerts');
|
|
45
|
-
expect(room.getSubscribers('alerts')).toEqual(['agent-2']);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should broadcast to all agents except sender', async () => {
|
|
49
|
-
const room = new Room('office');
|
|
50
|
-
const a1 = makeAgent('a1');
|
|
51
|
-
const a2 = makeAgent('a2');
|
|
52
|
-
await a1.init();
|
|
53
|
-
await a2.init();
|
|
54
|
-
room.addAgent(a1);
|
|
55
|
-
room.addAgent(a2);
|
|
56
|
-
|
|
57
|
-
const responses = await room.broadcast('a1', 'Hello everyone');
|
|
58
|
-
expect(responses.length).toBe(1); // only a2 responds
|
|
59
|
-
expect(responses[0].role).toBe('assistant');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should send direct messages', async () => {
|
|
63
|
-
const room = new Room('office');
|
|
64
|
-
const a1 = makeAgent('a1');
|
|
65
|
-
const a2 = makeAgent('a2');
|
|
66
|
-
await a1.init();
|
|
67
|
-
await a2.init();
|
|
68
|
-
room.addAgent(a1);
|
|
69
|
-
room.addAgent(a2);
|
|
70
|
-
|
|
71
|
-
const responses = await room.send({
|
|
72
|
-
from: 'a1',
|
|
73
|
-
to: 'a2',
|
|
74
|
-
message: { id: 'm1', role: 'user', content: 'Hi a2', timestamp: Date.now() },
|
|
75
|
-
});
|
|
76
|
-
expect(responses.length).toBe(1);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should publish to topic subscribers only', async () => {
|
|
80
|
-
const room = new Room('office');
|
|
81
|
-
const a1 = makeAgent('a1');
|
|
82
|
-
const a2 = makeAgent('a2');
|
|
83
|
-
const a3 = makeAgent('a3');
|
|
84
|
-
await a1.init();
|
|
85
|
-
await a2.init();
|
|
86
|
-
await a3.init();
|
|
87
|
-
room.addAgent(a1);
|
|
88
|
-
room.addAgent(a2);
|
|
89
|
-
room.addAgent(a3);
|
|
90
|
-
|
|
91
|
-
room.subscribe('a2', 'alerts');
|
|
92
|
-
// a3 not subscribed
|
|
93
|
-
const responses = await room.publishToTopic('a1', 'alerts', 'Alert!');
|
|
94
|
-
expect(responses.length).toBe(1);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('should remove agent from subscriptions on leave', () => {
|
|
98
|
-
const room = new Room('office');
|
|
99
|
-
room.subscribe('a1', 'topic1');
|
|
100
|
-
room.subscribe('a1', 'topic2');
|
|
101
|
-
expect(room.getSubscribers('topic1')).toContain('a1');
|
|
102
|
-
room.removeAgent('a1');
|
|
103
|
-
expect(room.getSubscribers('topic1')).not.toContain('a1');
|
|
104
|
-
expect(room.getSubscribers('topic2')).not.toContain('a1');
|
|
105
|
-
});
|
|
106
|
-
});
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { Room } from '../src/core/room';
|
|
3
|
+
import { BaseAgent } from '../src/core/agent';
|
|
4
|
+
|
|
5
|
+
function makeAgent(name: string): BaseAgent {
|
|
6
|
+
const agent = new BaseAgent({ name });
|
|
7
|
+
// Synchronously set to ready by calling init
|
|
8
|
+
return agent;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('Room System', () => {
|
|
12
|
+
it('should create a room with a name', () => {
|
|
13
|
+
const room = new Room('test-room');
|
|
14
|
+
expect(room.name).toBe('test-room');
|
|
15
|
+
expect(room.getAgents()).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should add and remove agents', () => {
|
|
19
|
+
const room = new Room('office');
|
|
20
|
+
const agent = makeAgent('agent-1');
|
|
21
|
+
room.addAgent(agent);
|
|
22
|
+
expect(room.getAgents()).toEqual(['agent-1']);
|
|
23
|
+
room.removeAgent('agent-1');
|
|
24
|
+
expect(room.getAgents()).toEqual([]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should emit events on join/leave', () => {
|
|
28
|
+
const room = new Room('office');
|
|
29
|
+
const events: string[] = [];
|
|
30
|
+
room.on('agent:join', (name) => events.push(`join:${name}`));
|
|
31
|
+
room.on('agent:leave', (name) => events.push(`leave:${name}`));
|
|
32
|
+
|
|
33
|
+
const agent = makeAgent('a1');
|
|
34
|
+
room.addAgent(agent);
|
|
35
|
+
room.removeAgent('a1');
|
|
36
|
+
expect(events).toEqual(['join:a1', 'leave:a1']);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should support topic subscriptions', () => {
|
|
40
|
+
const room = new Room('office');
|
|
41
|
+
room.subscribe('agent-1', 'alerts');
|
|
42
|
+
room.subscribe('agent-2', 'alerts');
|
|
43
|
+
expect(room.getSubscribers('alerts')).toEqual(['agent-1', 'agent-2']);
|
|
44
|
+
room.unsubscribe('agent-1', 'alerts');
|
|
45
|
+
expect(room.getSubscribers('alerts')).toEqual(['agent-2']);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('should broadcast to all agents except sender', async () => {
|
|
49
|
+
const room = new Room('office');
|
|
50
|
+
const a1 = makeAgent('a1');
|
|
51
|
+
const a2 = makeAgent('a2');
|
|
52
|
+
await a1.init();
|
|
53
|
+
await a2.init();
|
|
54
|
+
room.addAgent(a1);
|
|
55
|
+
room.addAgent(a2);
|
|
56
|
+
|
|
57
|
+
const responses = await room.broadcast('a1', 'Hello everyone');
|
|
58
|
+
expect(responses.length).toBe(1); // only a2 responds
|
|
59
|
+
expect(responses[0].role).toBe('assistant');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should send direct messages', async () => {
|
|
63
|
+
const room = new Room('office');
|
|
64
|
+
const a1 = makeAgent('a1');
|
|
65
|
+
const a2 = makeAgent('a2');
|
|
66
|
+
await a1.init();
|
|
67
|
+
await a2.init();
|
|
68
|
+
room.addAgent(a1);
|
|
69
|
+
room.addAgent(a2);
|
|
70
|
+
|
|
71
|
+
const responses = await room.send({
|
|
72
|
+
from: 'a1',
|
|
73
|
+
to: 'a2',
|
|
74
|
+
message: { id: 'm1', role: 'user', content: 'Hi a2', timestamp: Date.now() },
|
|
75
|
+
});
|
|
76
|
+
expect(responses.length).toBe(1);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should publish to topic subscribers only', async () => {
|
|
80
|
+
const room = new Room('office');
|
|
81
|
+
const a1 = makeAgent('a1');
|
|
82
|
+
const a2 = makeAgent('a2');
|
|
83
|
+
const a3 = makeAgent('a3');
|
|
84
|
+
await a1.init();
|
|
85
|
+
await a2.init();
|
|
86
|
+
await a3.init();
|
|
87
|
+
room.addAgent(a1);
|
|
88
|
+
room.addAgent(a2);
|
|
89
|
+
room.addAgent(a3);
|
|
90
|
+
|
|
91
|
+
room.subscribe('a2', 'alerts');
|
|
92
|
+
// a3 not subscribed
|
|
93
|
+
const responses = await room.publishToTopic('a1', 'alerts', 'Alert!');
|
|
94
|
+
expect(responses.length).toBe(1);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('should remove agent from subscriptions on leave', () => {
|
|
98
|
+
const room = new Room('office');
|
|
99
|
+
room.subscribe('a1', 'topic1');
|
|
100
|
+
room.subscribe('a1', 'topic2');
|
|
101
|
+
expect(room.getSubscribers('topic1')).toContain('a1');
|
|
102
|
+
room.removeAgent('a1');
|
|
103
|
+
expect(room.getSubscribers('topic1')).not.toContain('a1');
|
|
104
|
+
expect(room.getSubscribers('topic2')).not.toContain('a1');
|
|
105
|
+
});
|
|
106
|
+
});
|