opc-agent 4.1.0 → 4.1.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.
- package/.github/ISSUE_TEMPLATE/bug_report.md +20 -20
- package/.github/ISSUE_TEMPLATE/feature_request.md +14 -14
- package/.github/PULL_REQUEST_TEMPLATE.md +13 -13
- package/CHANGELOG.md +48 -48
- package/CONTRIBUTING.md +36 -36
- package/README.zh-CN.md +497 -497
- package/USABILITY-ISSUES.md +73 -0
- package/dist/channels/web.js +8 -2
- package/dist/channels/wechat.js +6 -6
- package/dist/cli.js +200 -85
- package/dist/core/runtime.js +37 -15
- package/dist/deploy/index.js +56 -56
- package/dist/doctor.d.ts +1 -0
- package/dist/doctor.js +105 -10
- package/dist/memory/deepbrain.d.ts +1 -1
- package/dist/memory/deepbrain.js +95 -4
- package/dist/scheduler/cron-engine.js +3 -36
- package/dist/studio/server.js +30 -1
- package/dist/studio-ui/index.html +230 -10
- package/dist/ui/components.js +105 -105
- package/examples/README.md +22 -22
- package/examples/basic-agent.ts +90 -90
- package/examples/brain-integration.ts +71 -71
- package/examples/multi-channel.ts +74 -74
- package/fix-sidebar.mjs +188 -188
- package/install.ps1 +154 -154
- package/install.sh +164 -164
- package/package.json +1 -1
- package/scripts/install.ps1 +31 -31
- package/scripts/install.sh +40 -40
- package/serve-studio.js +13 -13
- package/serve-test.js +25 -25
- package/src/channels/dingtalk.ts +46 -46
- package/src/channels/email.ts +351 -351
- package/src/channels/feishu.ts +349 -349
- package/src/channels/googlechat.ts +42 -42
- package/src/channels/imessage.ts +31 -31
- package/src/channels/irc.ts +82 -82
- package/src/channels/line.ts +32 -32
- package/src/channels/matrix.ts +33 -33
- package/src/channels/mattermost.ts +57 -57
- package/src/channels/msteams.ts +32 -32
- package/src/channels/nostr.ts +32 -32
- package/src/channels/qq.ts +33 -33
- package/src/channels/signal.ts +32 -32
- package/src/channels/sms.ts +33 -33
- package/src/channels/telegram.ts +616 -616
- package/src/channels/twitch.ts +65 -65
- package/src/channels/voice-call.ts +100 -100
- package/src/channels/web.ts +8 -2
- package/src/channels/websocket.ts +399 -399
- package/src/channels/wechat.ts +329 -329
- package/src/channels/whatsapp.ts +32 -32
- package/src/cli/chat.ts +99 -99
- package/src/cli/setup.ts +314 -314
- package/src/cli.ts +195 -92
- package/src/core/agent.ts +476 -476
- package/src/core/api-server.ts +277 -277
- package/src/core/audio.ts +98 -98
- package/src/core/collaboration.ts +275 -275
- package/src/core/context-discovery.ts +85 -85
- package/src/core/context-refs.ts +140 -140
- package/src/core/gateway.ts +106 -106
- package/src/core/heartbeat.ts +51 -51
- package/src/core/hooks.ts +105 -105
- package/src/core/ide-bridge.ts +133 -133
- package/src/core/node-network.ts +86 -86
- package/src/core/profiles.ts +122 -122
- package/src/core/runtime.ts +25 -0
- package/src/core/scheduler.ts +187 -187
- package/src/core/session-manager.ts +137 -137
- package/src/core/subagent.ts +98 -98
- package/src/core/vision.ts +180 -180
- package/src/core/workflow-graph.ts +365 -365
- package/src/daemon.ts +96 -96
- package/src/deploy/index.ts +255 -255
- package/src/doctor.ts +98 -11
- package/src/eval/index.ts +211 -211
- package/src/eval/suites/basic.json +16 -16
- package/src/eval/suites/memory.json +12 -12
- package/src/eval/suites/safety.json +14 -14
- package/src/hub/brain-seed.ts +54 -54
- package/src/hub/client.ts +60 -60
- package/src/mcp/servers/calculator-mcp.ts +65 -65
- package/src/mcp/servers/crypto-mcp.ts +73 -73
- package/src/mcp/servers/database-mcp.ts +72 -72
- package/src/mcp/servers/datetime-mcp.ts +69 -69
- package/src/mcp/servers/filesystem.ts +66 -66
- package/src/mcp/servers/github-mcp.ts +58 -58
- package/src/mcp/servers/index.ts +63 -63
- package/src/mcp/servers/json-mcp.ts +102 -102
- package/src/mcp/servers/memory-mcp.ts +56 -56
- package/src/mcp/servers/regex-mcp.ts +53 -53
- package/src/mcp/servers/web-mcp.ts +49 -49
- package/src/memory/context-compressor.ts +189 -189
- package/src/memory/deepbrain.ts +99 -5
- package/src/memory/seed-loader.ts +212 -212
- package/src/memory/user-profiler.ts +215 -215
- package/src/plugins/content-filter.ts +23 -23
- package/src/plugins/logger.ts +18 -18
- package/src/plugins/rate-limiter.ts +38 -38
- package/src/protocols/a2a/client.ts +132 -132
- package/src/protocols/a2a/index.ts +8 -8
- package/src/protocols/a2a/server.ts +333 -333
- package/src/protocols/a2a/types.ts +88 -88
- package/src/protocols/a2a/utils.ts +50 -50
- package/src/protocols/agui/client.ts +83 -83
- package/src/protocols/agui/index.ts +4 -4
- package/src/protocols/agui/server.ts +218 -218
- package/src/protocols/agui/types.ts +153 -153
- package/src/protocols/index.ts +2 -2
- package/src/protocols/mcp/agent-tools.ts +134 -134
- package/src/protocols/mcp/index.ts +8 -8
- package/src/protocols/mcp/server.ts +262 -262
- package/src/protocols/mcp/types.ts +69 -69
- package/src/providers/index.ts +632 -632
- package/src/publish/index.ts +376 -376
- package/src/scheduler/cron-engine.ts +191 -191
- package/src/scheduler/index.ts +2 -2
- package/src/schema/oad.ts +217 -217
- package/src/security/approval.ts +131 -131
- package/src/security/approvals.ts +143 -143
- package/src/security/elevated.ts +105 -105
- package/src/security/guardrails.ts +248 -248
- package/src/security/index.ts +9 -9
- package/src/security/keys.ts +87 -87
- package/src/security/secrets.ts +129 -129
- package/src/skills/builtin/index.ts +408 -408
- package/src/skills/marketplace.ts +113 -113
- package/src/skills/types.ts +42 -42
- package/src/studio/server.ts +31 -1
- package/src/studio/templates-data.ts +178 -178
- package/src/studio-ui/index.html +230 -10
- package/src/telemetry/index.ts +324 -324
- package/src/tools/builtin/browser.ts +299 -299
- package/src/tools/builtin/datetime.ts +41 -41
- package/src/tools/builtin/file.ts +107 -107
- package/src/tools/builtin/home-assistant.ts +116 -116
- package/src/tools/builtin/rl-tools.ts +243 -243
- package/src/tools/builtin/shell.ts +43 -43
- package/src/tools/builtin/vision.ts +64 -64
- package/src/tools/builtin/web-search.ts +126 -126
- package/src/tools/builtin/web.ts +35 -35
- package/src/tools/document-processor.ts +213 -213
- package/src/tools/image-generator.ts +150 -150
- package/src/tools/integrations/calendar.ts +73 -73
- package/src/tools/integrations/code-exec.ts +39 -39
- package/src/tools/integrations/csv-analyzer.ts +92 -92
- package/src/tools/integrations/database.ts +44 -44
- package/src/tools/integrations/email-send.ts +76 -76
- package/src/tools/integrations/git-tool.ts +42 -42
- package/src/tools/integrations/github-tool.ts +76 -76
- package/src/tools/integrations/image-gen.ts +56 -56
- package/src/tools/integrations/index.ts +92 -92
- package/src/tools/integrations/jira.ts +83 -83
- package/src/tools/integrations/notion.ts +71 -71
- package/src/tools/integrations/npm-tool.ts +48 -48
- package/src/tools/integrations/pdf-reader.ts +58 -58
- package/src/tools/integrations/slack.ts +65 -65
- package/src/tools/integrations/summarizer.ts +49 -49
- package/src/tools/integrations/translator.ts +48 -48
- package/src/tools/integrations/trello.ts +60 -60
- package/src/tools/integrations/vector-search.ts +42 -42
- package/src/tools/integrations/web-scraper.ts +47 -47
- package/src/tools/integrations/web-search.ts +58 -58
- package/src/tools/integrations/webhook.ts +38 -38
- package/src/tools/mcp-client.ts +131 -131
- package/src/tools/web-scraper.ts +179 -179
- package/src/tools/web-search.ts +180 -180
- package/src/ui/components.ts +127 -127
- package/srv-out.txt +1 -1
- package/templates/ecommerce-assistant/README.md +45 -45
- package/templates/ecommerce-assistant/oad.yaml +47 -47
- package/templates/tech-support/README.md +43 -43
- package/templates/tech-support/oad.yaml +45 -45
- package/test-agent/Dockerfile +9 -9
- package/test-agent/README.md +50 -50
- package/test-agent/agent.yaml +23 -23
- package/test-agent/docker-compose.yml +11 -11
- package/test-agent/oad.yaml +31 -31
- package/test-agent/package-lock.json +1492 -1492
- package/test-agent/package.json +17 -17
- package/test-agent/src/index.ts +24 -24
- package/test-agent/src/skills/echo.ts +15 -15
- package/test-agent/tsconfig.json +24 -24
- package/test-full.js +43 -43
- package/test-sidebar.js +22 -22
- package/test-studio3.js +75 -75
- package/test-studio4.js +41 -41
- package/tests/a2a-protocol.test.ts +285 -285
- package/tests/agui-protocol.test.ts +246 -246
- package/tests/api-server.test.ts +148 -148
- package/tests/approvals.test.ts +89 -89
- package/tests/audio.test.ts +40 -40
- package/tests/brain-seed-extended.test.ts +490 -490
- package/tests/brain-seed.test.ts +239 -239
- package/tests/browser.test.ts +179 -179
- package/tests/channels/discord.test.ts +79 -79
- package/tests/channels/email.test.ts +148 -148
- package/tests/channels/feishu.test.ts +123 -123
- package/tests/channels/telegram.test.ts +129 -129
- package/tests/channels/websocket.test.ts +53 -53
- package/tests/channels/wechat.test.ts +170 -170
- package/tests/channels-extra.test.ts +45 -45
- package/tests/chat-cli.test.ts +160 -160
- package/tests/cli.test.ts +46 -46
- package/tests/context-compressor.test.ts +172 -172
- package/tests/context-refs.test.ts +121 -121
- package/tests/cron-engine.test.ts +101 -101
- package/tests/daemon.test.ts +135 -135
- package/tests/deepbrain-wire.test.ts +234 -234
- package/tests/deploy-and-dag.test.ts +196 -196
- package/tests/doctor.test.ts +38 -38
- package/tests/document-processor.test.ts +69 -69
- package/tests/e2e-nocode.test.ts +442 -442
- package/tests/elevated.test.ts +69 -69
- package/tests/eval.test.ts +173 -173
- package/tests/gateway.test.ts +63 -63
- package/tests/guardrails.test.ts +177 -177
- package/tests/home-assistant.test.ts +40 -40
- package/tests/hooks.test.ts +79 -79
- package/tests/ide-bridge.test.ts +38 -38
- package/tests/image-generator.test.ts +84 -84
- package/tests/init-role.test.ts +124 -124
- package/tests/integrations.test.ts +249 -249
- package/tests/mcp-client.test.ts +92 -92
- package/tests/mcp-server.test.ts +178 -178
- package/tests/mcp-servers.test.ts +260 -260
- package/tests/node-network.test.ts +74 -74
- package/tests/plugin-a2a-enhanced.test.ts +230 -230
- package/tests/profiles.test.ts +61 -61
- package/tests/publish.test.ts +231 -231
- package/tests/rl-tools.test.ts +93 -93
- package/tests/sandbox-manager.test.ts +46 -46
- package/tests/scheduler.test.ts +200 -200
- package/tests/secrets.test.ts +107 -107
- package/tests/security-enhanced.test.ts +233 -233
- package/tests/settings-api.test.ts +148 -148
- package/tests/setup.test.ts +73 -73
- package/tests/subagent.test.ts +193 -193
- package/tests/telegram-discord.test.ts +60 -60
- package/tests/telemetry.test.ts +186 -186
- package/tests/user-profiler.test.ts +169 -169
- package/tests/v090-features.test.ts +254 -254
- package/tests/vision.test.ts +61 -61
- package/tests/voice-call.test.ts +47 -47
- package/tests/voice-enhanced.test.ts +169 -169
- package/tests/voice-interaction.test.ts +38 -38
- package/tests/web-search.test.ts +155 -155
- package/tests/workflow-graph.test.ts +279 -279
- package/tutorial/customer-service-agent/README.md +612 -612
- package/tutorial/customer-service-agent/SOUL.md +26 -26
- package/tutorial/customer-service-agent/agent.yaml +63 -63
- package/tutorial/customer-service-agent/package.json +19 -19
- package/tutorial/customer-service-agent/src/index.ts +69 -69
- package/tutorial/customer-service-agent/src/skills/faq.ts +27 -27
- package/tutorial/customer-service-agent/src/skills/ticket.ts +22 -22
- package/tutorial/customer-service-agent/tsconfig.json +14 -14
package/tests/subagent.test.ts
CHANGED
|
@@ -1,193 +1,193 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { SubAgentManager } from '../src/core/subagent';
|
|
3
|
-
import { BaseAgent } from '../src/core/agent';
|
|
4
|
-
|
|
5
|
-
// Mock the provider so we don't need real API keys
|
|
6
|
-
vi.mock('../src/providers', () => ({
|
|
7
|
-
createProvider: vi.fn(() => ({
|
|
8
|
-
name: 'mock',
|
|
9
|
-
chat: vi.fn().mockResolvedValue('mock response'),
|
|
10
|
-
chatStream: vi.fn(),
|
|
11
|
-
})),
|
|
12
|
-
SUPPORTED_PROVIDERS: ['openai'],
|
|
13
|
-
}));
|
|
14
|
-
|
|
15
|
-
describe('SubAgentManager', () => {
|
|
16
|
-
let manager: SubAgentManager;
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
manager = new SubAgentManager();
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('should spawn a sub-agent and return result', async () => {
|
|
23
|
-
const result = await manager.spawn({
|
|
24
|
-
name: 'test-agent',
|
|
25
|
-
task: 'Hello',
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
expect(result.name).toBe('test-agent');
|
|
29
|
-
expect(result.status).toBe('completed');
|
|
30
|
-
expect(result.result).toBe('mock response');
|
|
31
|
-
expect(result.duration).toBeGreaterThanOrEqual(0);
|
|
32
|
-
expect(result.id).toMatch(/^sub_/);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('should track agents in list', async () => {
|
|
36
|
-
await manager.spawn({ name: 'agent-1', task: 'task 1' });
|
|
37
|
-
await manager.spawn({ name: 'agent-2', task: 'task 2' });
|
|
38
|
-
|
|
39
|
-
const list = manager.list();
|
|
40
|
-
expect(list).toHaveLength(2);
|
|
41
|
-
expect(list[0].status).toBe('completed');
|
|
42
|
-
expect(list[1].status).toBe('completed');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('should spawn parallel agents', async () => {
|
|
46
|
-
const results = await manager.spawnParallel([
|
|
47
|
-
{ name: 'p1', task: 'task 1' },
|
|
48
|
-
{ name: 'p2', task: 'task 2' },
|
|
49
|
-
{ name: 'p3', task: 'task 3' },
|
|
50
|
-
]);
|
|
51
|
-
|
|
52
|
-
expect(results).toHaveLength(3);
|
|
53
|
-
results.forEach((r) => {
|
|
54
|
-
expect(r.status).toBe('completed');
|
|
55
|
-
expect(r.result).toBe('mock response');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should kill a sub-agent', async () => {
|
|
60
|
-
const result = await manager.spawn({ name: 'killable', task: 'task' });
|
|
61
|
-
|
|
62
|
-
expect(manager.kill(result.id)).toBe(true);
|
|
63
|
-
const list = manager.list();
|
|
64
|
-
const killed = list.find((a) => a.id === result.id);
|
|
65
|
-
expect(killed?.status).toBe('killed');
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should return false when killing non-existent agent', () => {
|
|
69
|
-
expect(manager.kill('non-existent')).toBe(false);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it('should handle timeout', async () => {
|
|
73
|
-
const { createProvider } = await import('../src/providers');
|
|
74
|
-
(createProvider as ReturnType<typeof vi.fn>).mockReturnValueOnce({
|
|
75
|
-
name: 'mock-slow',
|
|
76
|
-
chat: () => new Promise((resolve) => setTimeout(() => resolve('late'), 5000)),
|
|
77
|
-
chatStream: vi.fn(),
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const result = await manager.spawn({
|
|
81
|
-
name: 'slow-agent',
|
|
82
|
-
task: 'slow task',
|
|
83
|
-
timeout: 50,
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
expect(result.status).toBe('timeout');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('should handle failed agents', async () => {
|
|
90
|
-
const { createProvider } = await import('../src/providers');
|
|
91
|
-
(createProvider as ReturnType<typeof vi.fn>).mockReturnValueOnce({
|
|
92
|
-
name: 'mock-fail',
|
|
93
|
-
chat: () => Promise.reject(new Error('provider error')),
|
|
94
|
-
chatStream: vi.fn(),
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const result = await manager.spawn({
|
|
98
|
-
name: 'fail-agent',
|
|
99
|
-
task: 'fail task',
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
expect(result.status).toBe('failed');
|
|
103
|
-
expect(result.result).toBe('provider error');
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('should list empty when no agents spawned', () => {
|
|
107
|
-
expect(manager.list()).toEqual([]);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('should use isolated memory by default', async () => {
|
|
111
|
-
const result = await manager.spawn({
|
|
112
|
-
name: 'isolated',
|
|
113
|
-
task: 'test',
|
|
114
|
-
isolated: true,
|
|
115
|
-
});
|
|
116
|
-
expect(result.status).toBe('completed');
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
it('should spawn with custom systemPrompt', async () => {
|
|
120
|
-
const result = await manager.spawn({
|
|
121
|
-
name: 'custom-prompt',
|
|
122
|
-
task: 'test',
|
|
123
|
-
systemPrompt: 'You are a custom assistant',
|
|
124
|
-
});
|
|
125
|
-
expect(result.status).toBe('completed');
|
|
126
|
-
expect(result.name).toBe('custom-prompt');
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should spawn with custom model', async () => {
|
|
130
|
-
const result = await manager.spawn({
|
|
131
|
-
name: 'custom-model',
|
|
132
|
-
task: 'test',
|
|
133
|
-
model: 'gpt-4',
|
|
134
|
-
});
|
|
135
|
-
expect(result.status).toBe('completed');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it('spawnParallel returns all results even with mix', async () => {
|
|
139
|
-
const results = await manager.spawnParallel([
|
|
140
|
-
{ name: 'a', task: 't1' },
|
|
141
|
-
{ name: 'b', task: 't2' },
|
|
142
|
-
]);
|
|
143
|
-
expect(results).toHaveLength(2);
|
|
144
|
-
expect(results.every(r => r.result === 'mock response')).toBe(true);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('kill returns false for unknown id', () => {
|
|
148
|
-
expect(manager.kill('unknown-id-xyz')).toBe(false);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it('list shows correct status after completion', async () => {
|
|
152
|
-
await manager.spawn({ name: 'done', task: 'task' });
|
|
153
|
-
const list = manager.list();
|
|
154
|
-
expect(list.every(a => a.status === 'completed')).toBe(true);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('sub-agent name is preserved in result', async () => {
|
|
158
|
-
const result = await manager.spawn({ name: 'my-special-name', task: 'x' });
|
|
159
|
-
expect(result.name).toBe('my-special-name');
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
it('multiple sequential spawns tracked correctly', async () => {
|
|
163
|
-
await manager.spawn({ name: 's1', task: 't' });
|
|
164
|
-
await manager.spawn({ name: 's2', task: 't' });
|
|
165
|
-
await manager.spawn({ name: 's3', task: 't' });
|
|
166
|
-
const list = manager.list();
|
|
167
|
-
expect(list).toHaveLength(3);
|
|
168
|
-
expect(new Set(list.map(a => a.id)).size).toBe(3);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
it('each spawn gets unique id', async () => {
|
|
172
|
-
const r1 = await manager.spawn({ name: 'x', task: 't' });
|
|
173
|
-
const r2 = await manager.spawn({ name: 'x', task: 't' });
|
|
174
|
-
expect(r1.id).not.toBe(r2.id);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
it('result duration is non-negative', async () => {
|
|
178
|
-
const result = await manager.spawn({ name: 'dur', task: 'test' });
|
|
179
|
-
expect(result.duration).toBeGreaterThanOrEqual(0);
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('BaseAgent subagent methods', () => {
|
|
184
|
-
it('should have spawnSubAgent method', () => {
|
|
185
|
-
const agent = new BaseAgent({ name: 'parent' });
|
|
186
|
-
expect(typeof agent.spawnSubAgent).toBe('function');
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('should have spawnParallel method', () => {
|
|
190
|
-
const agent = new BaseAgent({ name: 'parent' });
|
|
191
|
-
expect(typeof agent.spawnParallel).toBe('function');
|
|
192
|
-
});
|
|
193
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { SubAgentManager } from '../src/core/subagent';
|
|
3
|
+
import { BaseAgent } from '../src/core/agent';
|
|
4
|
+
|
|
5
|
+
// Mock the provider so we don't need real API keys
|
|
6
|
+
vi.mock('../src/providers', () => ({
|
|
7
|
+
createProvider: vi.fn(() => ({
|
|
8
|
+
name: 'mock',
|
|
9
|
+
chat: vi.fn().mockResolvedValue('mock response'),
|
|
10
|
+
chatStream: vi.fn(),
|
|
11
|
+
})),
|
|
12
|
+
SUPPORTED_PROVIDERS: ['openai'],
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
describe('SubAgentManager', () => {
|
|
16
|
+
let manager: SubAgentManager;
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
manager = new SubAgentManager();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should spawn a sub-agent and return result', async () => {
|
|
23
|
+
const result = await manager.spawn({
|
|
24
|
+
name: 'test-agent',
|
|
25
|
+
task: 'Hello',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(result.name).toBe('test-agent');
|
|
29
|
+
expect(result.status).toBe('completed');
|
|
30
|
+
expect(result.result).toBe('mock response');
|
|
31
|
+
expect(result.duration).toBeGreaterThanOrEqual(0);
|
|
32
|
+
expect(result.id).toMatch(/^sub_/);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should track agents in list', async () => {
|
|
36
|
+
await manager.spawn({ name: 'agent-1', task: 'task 1' });
|
|
37
|
+
await manager.spawn({ name: 'agent-2', task: 'task 2' });
|
|
38
|
+
|
|
39
|
+
const list = manager.list();
|
|
40
|
+
expect(list).toHaveLength(2);
|
|
41
|
+
expect(list[0].status).toBe('completed');
|
|
42
|
+
expect(list[1].status).toBe('completed');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should spawn parallel agents', async () => {
|
|
46
|
+
const results = await manager.spawnParallel([
|
|
47
|
+
{ name: 'p1', task: 'task 1' },
|
|
48
|
+
{ name: 'p2', task: 'task 2' },
|
|
49
|
+
{ name: 'p3', task: 'task 3' },
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
expect(results).toHaveLength(3);
|
|
53
|
+
results.forEach((r) => {
|
|
54
|
+
expect(r.status).toBe('completed');
|
|
55
|
+
expect(r.result).toBe('mock response');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should kill a sub-agent', async () => {
|
|
60
|
+
const result = await manager.spawn({ name: 'killable', task: 'task' });
|
|
61
|
+
|
|
62
|
+
expect(manager.kill(result.id)).toBe(true);
|
|
63
|
+
const list = manager.list();
|
|
64
|
+
const killed = list.find((a) => a.id === result.id);
|
|
65
|
+
expect(killed?.status).toBe('killed');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should return false when killing non-existent agent', () => {
|
|
69
|
+
expect(manager.kill('non-existent')).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle timeout', async () => {
|
|
73
|
+
const { createProvider } = await import('../src/providers');
|
|
74
|
+
(createProvider as ReturnType<typeof vi.fn>).mockReturnValueOnce({
|
|
75
|
+
name: 'mock-slow',
|
|
76
|
+
chat: () => new Promise((resolve) => setTimeout(() => resolve('late'), 5000)),
|
|
77
|
+
chatStream: vi.fn(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const result = await manager.spawn({
|
|
81
|
+
name: 'slow-agent',
|
|
82
|
+
task: 'slow task',
|
|
83
|
+
timeout: 50,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(result.status).toBe('timeout');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should handle failed agents', async () => {
|
|
90
|
+
const { createProvider } = await import('../src/providers');
|
|
91
|
+
(createProvider as ReturnType<typeof vi.fn>).mockReturnValueOnce({
|
|
92
|
+
name: 'mock-fail',
|
|
93
|
+
chat: () => Promise.reject(new Error('provider error')),
|
|
94
|
+
chatStream: vi.fn(),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const result = await manager.spawn({
|
|
98
|
+
name: 'fail-agent',
|
|
99
|
+
task: 'fail task',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(result.status).toBe('failed');
|
|
103
|
+
expect(result.result).toBe('provider error');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should list empty when no agents spawned', () => {
|
|
107
|
+
expect(manager.list()).toEqual([]);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('should use isolated memory by default', async () => {
|
|
111
|
+
const result = await manager.spawn({
|
|
112
|
+
name: 'isolated',
|
|
113
|
+
task: 'test',
|
|
114
|
+
isolated: true,
|
|
115
|
+
});
|
|
116
|
+
expect(result.status).toBe('completed');
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should spawn with custom systemPrompt', async () => {
|
|
120
|
+
const result = await manager.spawn({
|
|
121
|
+
name: 'custom-prompt',
|
|
122
|
+
task: 'test',
|
|
123
|
+
systemPrompt: 'You are a custom assistant',
|
|
124
|
+
});
|
|
125
|
+
expect(result.status).toBe('completed');
|
|
126
|
+
expect(result.name).toBe('custom-prompt');
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should spawn with custom model', async () => {
|
|
130
|
+
const result = await manager.spawn({
|
|
131
|
+
name: 'custom-model',
|
|
132
|
+
task: 'test',
|
|
133
|
+
model: 'gpt-4',
|
|
134
|
+
});
|
|
135
|
+
expect(result.status).toBe('completed');
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('spawnParallel returns all results even with mix', async () => {
|
|
139
|
+
const results = await manager.spawnParallel([
|
|
140
|
+
{ name: 'a', task: 't1' },
|
|
141
|
+
{ name: 'b', task: 't2' },
|
|
142
|
+
]);
|
|
143
|
+
expect(results).toHaveLength(2);
|
|
144
|
+
expect(results.every(r => r.result === 'mock response')).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('kill returns false for unknown id', () => {
|
|
148
|
+
expect(manager.kill('unknown-id-xyz')).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('list shows correct status after completion', async () => {
|
|
152
|
+
await manager.spawn({ name: 'done', task: 'task' });
|
|
153
|
+
const list = manager.list();
|
|
154
|
+
expect(list.every(a => a.status === 'completed')).toBe(true);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('sub-agent name is preserved in result', async () => {
|
|
158
|
+
const result = await manager.spawn({ name: 'my-special-name', task: 'x' });
|
|
159
|
+
expect(result.name).toBe('my-special-name');
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('multiple sequential spawns tracked correctly', async () => {
|
|
163
|
+
await manager.spawn({ name: 's1', task: 't' });
|
|
164
|
+
await manager.spawn({ name: 's2', task: 't' });
|
|
165
|
+
await manager.spawn({ name: 's3', task: 't' });
|
|
166
|
+
const list = manager.list();
|
|
167
|
+
expect(list).toHaveLength(3);
|
|
168
|
+
expect(new Set(list.map(a => a.id)).size).toBe(3);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('each spawn gets unique id', async () => {
|
|
172
|
+
const r1 = await manager.spawn({ name: 'x', task: 't' });
|
|
173
|
+
const r2 = await manager.spawn({ name: 'x', task: 't' });
|
|
174
|
+
expect(r1.id).not.toBe(r2.id);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('result duration is non-negative', async () => {
|
|
178
|
+
const result = await manager.spawn({ name: 'dur', task: 'test' });
|
|
179
|
+
expect(result.duration).toBeGreaterThanOrEqual(0);
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('BaseAgent subagent methods', () => {
|
|
184
|
+
it('should have spawnSubAgent method', () => {
|
|
185
|
+
const agent = new BaseAgent({ name: 'parent' });
|
|
186
|
+
expect(typeof agent.spawnSubAgent).toBe('function');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should have spawnParallel method', () => {
|
|
190
|
+
const agent = new BaseAgent({ name: 'parent' });
|
|
191
|
+
expect(typeof agent.spawnParallel).toBe('function');
|
|
192
|
+
});
|
|
193
|
+
});
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { TelegramChannel } from '../src/channels/telegram';
|
|
3
|
-
import { DiscordChannel } from '../src/channels/discord';
|
|
4
|
-
|
|
5
|
-
describe('TelegramChannel', () => {
|
|
6
|
-
it('should create with default config (polling mode)', () => {
|
|
7
|
-
const channel = new TelegramChannel({ token: 'test-token' });
|
|
8
|
-
expect(channel.type).toBe('telegram');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
it('should create with webhook mode', () => {
|
|
12
|
-
const channel = new TelegramChannel({
|
|
13
|
-
token: 'test-token',
|
|
14
|
-
mode: 'webhook',
|
|
15
|
-
webhookUrl: 'https://example.com',
|
|
16
|
-
port: 4000,
|
|
17
|
-
});
|
|
18
|
-
expect(channel.type).toBe('telegram');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('should warn and return if no token on start', async () => {
|
|
22
|
-
const channel = new TelegramChannel({ token: '' });
|
|
23
|
-
// Should not throw, just warn
|
|
24
|
-
await channel.start();
|
|
25
|
-
await channel.stop();
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('should support onMessage handler', () => {
|
|
29
|
-
const channel = new TelegramChannel({ token: 'test-token' });
|
|
30
|
-
const handler = async (msg: any) => ({ ...msg, role: 'assistant' as const });
|
|
31
|
-
channel.onMessage(handler);
|
|
32
|
-
// No throw = success
|
|
33
|
-
expect(channel.type).toBe('telegram');
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe('DiscordChannel', () => {
|
|
38
|
-
it('should create with default config', () => {
|
|
39
|
-
const channel = new DiscordChannel({ botToken: 'test-token' });
|
|
40
|
-
expect(channel.type).toBe('discord');
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('should warn and return if no token on start', async () => {
|
|
44
|
-
const channel = new DiscordChannel({ botToken: '' });
|
|
45
|
-
await channel.start();
|
|
46
|
-
await channel.stop();
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should support onMessage handler', () => {
|
|
50
|
-
const channel = new DiscordChannel({ botToken: 'test-token' });
|
|
51
|
-
const handler = async (msg: any) => ({ ...msg, role: 'assistant' as const });
|
|
52
|
-
channel.onMessage(handler);
|
|
53
|
-
expect(channel.type).toBe('discord');
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it('should handle stop gracefully when not started', async () => {
|
|
57
|
-
const channel = new DiscordChannel({ botToken: 'test-token' });
|
|
58
|
-
await channel.stop(); // Should not throw
|
|
59
|
-
});
|
|
60
|
-
});
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { TelegramChannel } from '../src/channels/telegram';
|
|
3
|
+
import { DiscordChannel } from '../src/channels/discord';
|
|
4
|
+
|
|
5
|
+
describe('TelegramChannel', () => {
|
|
6
|
+
it('should create with default config (polling mode)', () => {
|
|
7
|
+
const channel = new TelegramChannel({ token: 'test-token' });
|
|
8
|
+
expect(channel.type).toBe('telegram');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('should create with webhook mode', () => {
|
|
12
|
+
const channel = new TelegramChannel({
|
|
13
|
+
token: 'test-token',
|
|
14
|
+
mode: 'webhook',
|
|
15
|
+
webhookUrl: 'https://example.com',
|
|
16
|
+
port: 4000,
|
|
17
|
+
});
|
|
18
|
+
expect(channel.type).toBe('telegram');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should warn and return if no token on start', async () => {
|
|
22
|
+
const channel = new TelegramChannel({ token: '' });
|
|
23
|
+
// Should not throw, just warn
|
|
24
|
+
await channel.start();
|
|
25
|
+
await channel.stop();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('should support onMessage handler', () => {
|
|
29
|
+
const channel = new TelegramChannel({ token: 'test-token' });
|
|
30
|
+
const handler = async (msg: any) => ({ ...msg, role: 'assistant' as const });
|
|
31
|
+
channel.onMessage(handler);
|
|
32
|
+
// No throw = success
|
|
33
|
+
expect(channel.type).toBe('telegram');
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('DiscordChannel', () => {
|
|
38
|
+
it('should create with default config', () => {
|
|
39
|
+
const channel = new DiscordChannel({ botToken: 'test-token' });
|
|
40
|
+
expect(channel.type).toBe('discord');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should warn and return if no token on start', async () => {
|
|
44
|
+
const channel = new DiscordChannel({ botToken: '' });
|
|
45
|
+
await channel.start();
|
|
46
|
+
await channel.stop();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should support onMessage handler', () => {
|
|
50
|
+
const channel = new DiscordChannel({ botToken: 'test-token' });
|
|
51
|
+
const handler = async (msg: any) => ({ ...msg, role: 'assistant' as const });
|
|
52
|
+
channel.onMessage(handler);
|
|
53
|
+
expect(channel.type).toBe('discord');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should handle stop gracefully when not started', async () => {
|
|
57
|
+
const channel = new DiscordChannel({ botToken: 'test-token' });
|
|
58
|
+
await channel.stop(); // Should not throw
|
|
59
|
+
});
|
|
60
|
+
});
|