@newsails/veil-cli 1.0.1
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/.veil/agents/analyst/AGENT.md +21 -0
- package/.veil/agents/analyst/agent.json +23 -0
- package/.veil/agents/assistant/AGENT.md +15 -0
- package/.veil/agents/assistant/agent.json +19 -0
- package/.veil/agents/coder/AGENT.md +18 -0
- package/.veil/agents/coder/agent.json +19 -0
- package/.veil/agents/hello/AGENT.md +5 -0
- package/.veil/agents/hello/agent.json +13 -0
- package/.veil/agents/writer/AGENT.md +12 -0
- package/.veil/agents/writer/agent.json +17 -0
- package/.veil/memory/MEMORY.md +343 -0
- package/.veil/memory/agents/analyst/MEMORY.md +55 -0
- package/.veil/memory/agents/hello/MEMORY.md +12 -0
- package/.veil/runtime.pid +1 -0
- package/.veil/settings.json +10 -0
- package/.veil-studio/studio.db +0 -0
- package/.veil-studio/studio.db-shm +0 -0
- package/.veil-studio/studio.db-wal +0 -0
- package/PLAN/01-vision.md +26 -0
- package/PLAN/02-tech-stack.md +94 -0
- package/PLAN/03-agents.md +232 -0
- package/PLAN/04-runtime.md +171 -0
- package/PLAN/05-tools.md +211 -0
- package/PLAN/06-communication.md +243 -0
- package/PLAN/07-storage.md +218 -0
- package/PLAN/08-api-cli.md +153 -0
- package/PLAN/09-permissions.md +108 -0
- package/PLAN/10-ably.md +105 -0
- package/PLAN/11-file-formats.md +442 -0
- package/PLAN/12-folder-structure.md +205 -0
- package/PLAN/13-operations.md +212 -0
- package/PLAN/README.md +23 -0
- package/README.md +128 -0
- package/REPORT.md +174 -0
- package/TODO.md +45 -0
- package/ai-tests/FRONTEND_PROMPT.md +220 -0
- package/ai-tests/Research & Planning.md +814 -0
- package/ai-tests/prompt-001-basic-api.md +230 -0
- package/ai-tests/prompt-002-basic-flows.md +230 -0
- package/ai-tests/prompt-003-agent-behaviors.md +220 -0
- package/api/middleware.js +60 -0
- package/api/routes/agents.js +193 -0
- package/api/routes/chat.js +93 -0
- package/api/routes/completions.js +122 -0
- package/api/routes/daemons.js +80 -0
- package/api/routes/memory.js +169 -0
- package/api/routes/models.js +40 -0
- package/api/routes/remote-methods.js +74 -0
- package/api/routes/sessions.js +208 -0
- package/api/routes/settings.js +108 -0
- package/api/routes/system.js +50 -0
- package/api/routes/tasks.js +270 -0
- package/api/server.js +120 -0
- package/cli/formatter.js +70 -0
- package/cli/index.js +443 -0
- package/cli/parser.js +113 -0
- package/config/config.json +10 -0
- package/config/models.json +6826 -0
- package/core/agent.js +329 -0
- package/core/cancel.js +38 -0
- package/core/compaction.js +176 -0
- package/core/events.js +13 -0
- package/core/loop.js +564 -0
- package/core/memory.js +51 -0
- package/core/prompt.js +185 -0
- package/core/queue.js +96 -0
- package/core/registry.js +291 -0
- package/core/remote-methods.js +124 -0
- package/core/router.js +386 -0
- package/core/running-sessions.js +18 -0
- package/docs/api/01-system.md +84 -0
- package/docs/api/02-agents.md +374 -0
- package/docs/api/03-chat.md +269 -0
- package/docs/api/04-tasks.md +470 -0
- package/docs/api/05-sessions.md +444 -0
- package/docs/api/06-daemons.md +142 -0
- package/docs/api/07-memory.md +186 -0
- package/docs/api/08-settings.md +133 -0
- package/docs/api/09-models.md +119 -0
- package/docs/api/09-websocket.md +350 -0
- package/docs/api/10-completions.md +134 -0
- package/docs/api/README.md +116 -0
- package/docs/guide/01-quickstart.md +220 -0
- package/docs/guide/02-folder-structure.md +185 -0
- package/docs/guide/03-configuration.md +252 -0
- package/docs/guide/04-agents.md +267 -0
- package/docs/guide/05-cli.md +290 -0
- package/docs/guide/06-tools.md +643 -0
- package/docs/guide/07-permissions.md +236 -0
- package/docs/guide/08-memory.md +139 -0
- package/docs/guide/09-multi-agent.md +271 -0
- package/docs/guide/10-daemons.md +226 -0
- package/docs/guide/README.md +53 -0
- package/docs/index.html +623 -0
- package/examples/README.md +151 -0
- package/examples/agents/assistant/AGENT.md +31 -0
- package/examples/agents/assistant/SOUL.md +9 -0
- package/examples/agents/assistant/agent.json +74 -0
- package/examples/agents/hello/AGENT.md +15 -0
- package/examples/agents/hello/agent.json +14 -0
- package/examples/agents/monitor/AGENT.md +51 -0
- package/examples/agents/monitor/agent.json +33 -0
- package/examples/agents/monitor/heartbeats/monitor.md +24 -0
- package/examples/agents/orchestrator/AGENT.md +70 -0
- package/examples/agents/orchestrator/agent.json +30 -0
- package/examples/agents/researcher/AGENT.md +52 -0
- package/examples/agents/researcher/agent.json +49 -0
- package/examples/agents/researcher/skills/web-research.md +28 -0
- package/examples/skills/code-review.md +72 -0
- package/examples/skills/summarise.md +59 -0
- package/examples/skills/web-research.md +42 -0
- package/examples/tools/word-count/index.js +27 -0
- package/examples/tools/word-count/tool.json +18 -0
- package/infrastructure/database.js +563 -0
- package/infrastructure/scheduler.js +122 -0
- package/llm/client.js +206 -0
- package/migrations/001-initial.sql +121 -0
- package/migrations/002-debuggability.sql +13 -0
- package/migrations/003-drop-orphaned-columns.sql +72 -0
- package/migrations/004-session-message-token-fields.sql +78 -0
- package/migrations/005-session-thinking.sql +5 -0
- package/package.json +30 -0
- package/schemas/agent.json +143 -0
- package/schemas/settings.json +111 -0
- package/scripts/fetch-models.js +93 -0
- package/session-debug-scenario.md +248 -0
- package/settings/fields.js +52 -0
- package/system-prompts/base-core.md +7 -0
- package/system-prompts/environment.md +13 -0
- package/system-prompts/reminders/anti-drift.md +6 -0
- package/system-prompts/reminders/stall-recovery.md +10 -0
- package/system-prompts/safety-rules.md +25 -0
- package/system-prompts/task-heuristics.md +27 -0
- package/test/client.js +71 -0
- package/test/integration/01-health.test.js +25 -0
- package/test/integration/02-agents.test.js +80 -0
- package/test/integration/03-chat-hello.test.js +48 -0
- package/test/integration/04-chat-multiturn.test.js +61 -0
- package/test/integration/05-chat-writer.test.js +48 -0
- package/test/integration/06-task-basic.test.js +68 -0
- package/test/integration/07-task-tools.test.js +74 -0
- package/test/integration/08-task-code-analysis.test.js +69 -0
- package/test/integration/09-memory-analyst.test.js +63 -0
- package/test/integration/10-task-advanced.test.js +85 -0
- package/test/integration/11-sessions-advanced.test.js +84 -0
- package/test/integration/12-assistant-chat-tools.test.js +75 -0
- package/test/integration/13-edge-cases.test.js +99 -0
- package/test/integration/14-cancel.test.js +62 -0
- package/test/integration/15-debug.test.js +106 -0
- package/test/integration/16-memory-api.test.js +83 -0
- package/test/integration/17-settings-api.test.js +41 -0
- package/test/integration/18-tool-search-activation.test.js +119 -0
- package/test/results/.gitkeep +0 -0
- package/test/runner.js +206 -0
- package/test/smoke.js +216 -0
- package/tools/agent_message.js +85 -0
- package/tools/agent_send.js +80 -0
- package/tools/agent_spawn.js +44 -0
- package/tools/bash.js +49 -0
- package/tools/edit_file.js +41 -0
- package/tools/glob.js +64 -0
- package/tools/grep.js +82 -0
- package/tools/list_dir.js +63 -0
- package/tools/log_write.js +31 -0
- package/tools/memory_read.js +38 -0
- package/tools/memory_search.js +65 -0
- package/tools/memory_write.js +42 -0
- package/tools/read_file.js +48 -0
- package/tools/sleep.js +22 -0
- package/tools/task_create.js +41 -0
- package/tools/task_respond.js +37 -0
- package/tools/task_spawn.js +64 -0
- package/tools/task_status.js +39 -0
- package/tools/task_subscribe.js +37 -0
- package/tools/todo_read.js +26 -0
- package/tools/todo_write.js +38 -0
- package/tools/tool_activate.js +24 -0
- package/tools/tool_search.js +24 -0
- package/tools/web_fetch.js +50 -0
- package/tools/web_search.js +52 -0
- package/tools/write_file.js +28 -0
- package/ui/api.js +190 -0
- package/ui/app.js +281 -0
- package/ui/index.html +382 -0
- package/ui/views/agents.js +377 -0
- package/ui/views/chat.js +610 -0
- package/ui/views/connection.js +96 -0
- package/ui/views/daemons.js +129 -0
- package/ui/views/feed.js +194 -0
- package/ui/views/memory.js +263 -0
- package/ui/views/models.js +146 -0
- package/ui/views/sessions.js +314 -0
- package/ui/views/settings.js +142 -0
- package/ui/views/tasks.js +415 -0
- package/utils/context.js +49 -0
- package/utils/id.js +16 -0
- package/utils/models.js +88 -0
- package/utils/paths.js +213 -0
- package/utils/settings.js +172 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 1: Health & Status endpoints ────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
await test('GET /health returns 200 with status ok', async () => {
|
|
6
|
+
const res = await client.health();
|
|
7
|
+
assert.strictEqual(res.status, 200, `Expected 200, got ${res.status}: ${JSON.stringify(res.body)}`);
|
|
8
|
+
assert.strictEqual(res.body.status, 'ok');
|
|
9
|
+
assert(res.body.version, 'version missing');
|
|
10
|
+
assert(typeof res.body.uptime === 'number', 'uptime should be a number');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
await test('GET /status returns server info', async () => {
|
|
14
|
+
const res = await client.status();
|
|
15
|
+
assert.strictEqual(res.status, 200);
|
|
16
|
+
assert.strictEqual(res.body.status, 'ok');
|
|
17
|
+
assert(typeof res.body.agents === 'number', 'agents count missing');
|
|
18
|
+
assert(typeof res.body.activeSessions === 'number');
|
|
19
|
+
assert(typeof res.body.pendingTasks === 'number');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await test('GET /nonexistent returns 404', async () => {
|
|
23
|
+
const res = await fetch('http://localhost:5050/nonexistent-route-xyz');
|
|
24
|
+
assert.strictEqual(res.status, 404, `Expected 404, got ${res.status}`);
|
|
25
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 2: Agent management endpoints ───────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
await test('GET /agents returns array with expected agents', async () => {
|
|
6
|
+
const res = await client.agents.list();
|
|
7
|
+
assert.strictEqual(res.status, 200, `Expected 200, got ${res.status}: ${JSON.stringify(res.body)}`);
|
|
8
|
+
assert(Array.isArray(res.body.agents), 'agents should be an array');
|
|
9
|
+
const names = res.body.agents.map(a => a.name);
|
|
10
|
+
assert(names.includes('hello'), `hello agent missing — found: ${names.join(', ')}`);
|
|
11
|
+
assert(names.includes('assistant'), `assistant agent missing`);
|
|
12
|
+
assert(names.includes('coder'), `coder agent missing`);
|
|
13
|
+
assert(names.includes('writer'), `writer agent missing`);
|
|
14
|
+
assert(names.includes('analyst'), `analyst agent missing`);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
await test('GET /agents/:name returns agent details for hello', async () => {
|
|
18
|
+
const res = await client.agents.get('hello');
|
|
19
|
+
assert.strictEqual(res.status, 200, `Expected 200, got ${res.status}: ${JSON.stringify(res.body)}`);
|
|
20
|
+
assert.strictEqual(res.body.agent.name, 'hello');
|
|
21
|
+
assert(res.body.agent.model, 'model should be set');
|
|
22
|
+
assert(res.body.agent.modes, 'modes should be present');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await test('GET /agents/:name returns 404 for unknown agent', async () => {
|
|
26
|
+
const res = await client.agents.get('does-not-exist-xyz');
|
|
27
|
+
assert.strictEqual(res.status, 404, `Expected 404, got ${res.status}`);
|
|
28
|
+
assert(res.body.error, 'error body missing');
|
|
29
|
+
assert.strictEqual(res.body.error.code, 'AGENT_NOT_FOUND');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
await test('GET /agents/coder has task mode enabled', async () => {
|
|
33
|
+
const res = await client.agents.get('coder');
|
|
34
|
+
assert.strictEqual(res.status, 200);
|
|
35
|
+
assert(res.body.agent.modes?.task?.enabled, 'coder should have task mode enabled');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await test('GET /agents/hello has chat mode enabled', async () => {
|
|
39
|
+
const res = await client.agents.get('hello');
|
|
40
|
+
assert.strictEqual(res.status, 200);
|
|
41
|
+
assert(res.body.agent.modes?.chat?.enabled, 'hello should have chat mode enabled');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
await test('GET /agents/:name/sessions returns sessions scoped to agent', async () => {
|
|
45
|
+
const res = await fetch('http://localhost:5050/agents/hello/sessions');
|
|
46
|
+
assert.strictEqual(res.status, 200);
|
|
47
|
+
const body = await res.json();
|
|
48
|
+
assert.strictEqual(body.agentName, 'hello');
|
|
49
|
+
assert(Array.isArray(body.sessions), 'sessions should be array');
|
|
50
|
+
body.sessions.forEach(s => {
|
|
51
|
+
assert.strictEqual(s.agent_name, 'hello', `all sessions should belong to hello, got: ${s.agent_name}`);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await test('GET /agents/:name/tasks returns tasks scoped to agent', async () => {
|
|
56
|
+
const res = await fetch('http://localhost:5050/agents/hello/tasks');
|
|
57
|
+
assert.strictEqual(res.status, 200);
|
|
58
|
+
const body = await res.json();
|
|
59
|
+
assert.strictEqual(body.agentName, 'hello');
|
|
60
|
+
assert(Array.isArray(body.tasks), 'tasks should be array');
|
|
61
|
+
body.tasks.forEach(t => {
|
|
62
|
+
assert.strictEqual(t.agent_name, 'hello', `all tasks should belong to hello, got: ${t.agent_name}`);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await test('GET /agents/:name/skills returns skills listing', async () => {
|
|
67
|
+
const res = await fetch('http://localhost:5050/agents/hello/skills');
|
|
68
|
+
assert.strictEqual(res.status, 200);
|
|
69
|
+
const body = await res.json();
|
|
70
|
+
assert.strictEqual(body.agentName, 'hello');
|
|
71
|
+
assert(Array.isArray(body.skills), 'skills should be array');
|
|
72
|
+
assert(Array.isArray(body.customTools), 'customTools should be array');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
await test('GET /agents/:name/skills returns 404 for unknown agent', async () => {
|
|
76
|
+
const res = await fetch('http://localhost:5050/agents/does-not-exist-xyz/skills');
|
|
77
|
+
assert.strictEqual(res.status, 404);
|
|
78
|
+
const body = await res.json();
|
|
79
|
+
assert.strictEqual(body.error.code, 'AGENT_NOT_FOUND');
|
|
80
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 3: Basic chat with hello agent (no tools) ───────────────────────────
|
|
4
|
+
|
|
5
|
+
await test('POST /agents/hello/chat returns a response', async () => {
|
|
6
|
+
const res = await client.chat.send('hello', 'Say exactly: HELLO_TEST_OK');
|
|
7
|
+
assert.strictEqual(res.status, 200, `Expected 200, got ${res.status}: ${JSON.stringify(res.body)}`);
|
|
8
|
+
assert(res.body.sessionId, 'sessionId missing');
|
|
9
|
+
assert(res.body.message, 'message missing');
|
|
10
|
+
assert.strictEqual(res.body.message.role, 'assistant');
|
|
11
|
+
assert(res.body.message.content, 'content missing');
|
|
12
|
+
assert(res.body.message.content.length > 0, 'content is empty');
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
await test('Chat response contains non-empty text', async () => {
|
|
16
|
+
const res = await client.chat.send('hello', 'What is 2 + 2? Reply with just the number.');
|
|
17
|
+
assert.strictEqual(res.status, 200, JSON.stringify(res.body));
|
|
18
|
+
const content = res.body.message.content;
|
|
19
|
+
assert(content.includes('4'), `Expected answer to contain "4", got: ${content}`);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
await test('Chat with hello agent returns sessionId', async () => {
|
|
23
|
+
const res = await client.chat.send('hello', 'Hello!');
|
|
24
|
+
assert.strictEqual(res.status, 200);
|
|
25
|
+
const { sessionId } = res.body;
|
|
26
|
+
assert(sessionId, 'sessionId missing');
|
|
27
|
+
assert(sessionId.startsWith('sess_'), `sessionId should start with sess_, got: ${sessionId}`);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
await test('Chat with unknown agent returns 404', async () => {
|
|
31
|
+
const res = await client.chat.send('nonexistent-agent-xyz', 'Hello');
|
|
32
|
+
assert.strictEqual(res.status, 404);
|
|
33
|
+
assert.strictEqual(res.body.error.code, 'AGENT_NOT_FOUND');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
await test('Chat with empty message returns 400', async () => {
|
|
37
|
+
const res = await client.chat.send('hello', '');
|
|
38
|
+
assert.strictEqual(res.status, 400);
|
|
39
|
+
assert.strictEqual(res.body.error.code, 'VALIDATION_ERROR');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
await test('Chat returns tokenUsage object', async () => {
|
|
43
|
+
const res = await client.chat.send('hello', 'Ping!');
|
|
44
|
+
assert.strictEqual(res.status, 200);
|
|
45
|
+
assert(res.body.tokenUsage, 'tokenUsage missing');
|
|
46
|
+
assert(typeof res.body.tokenUsage.input === 'number', 'tokenUsage.input should be a number');
|
|
47
|
+
assert(typeof res.body.tokenUsage.output === 'number', 'tokenUsage.output should be a number');
|
|
48
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 4: Multi-turn chat (session continuity) ──────────────────────────────
|
|
4
|
+
|
|
5
|
+
await test('Multi-turn chat: second message continues same session', async () => {
|
|
6
|
+
// Turn 1
|
|
7
|
+
const res1 = await client.chat.send('hello', 'My name is TestUser42. Remember it.');
|
|
8
|
+
assert.strictEqual(res1.status, 200, JSON.stringify(res1.body));
|
|
9
|
+
const { sessionId } = res1.body;
|
|
10
|
+
assert(sessionId, 'sessionId missing');
|
|
11
|
+
|
|
12
|
+
// Turn 2 — same session
|
|
13
|
+
const res2 = await client.chat.send('hello', 'What is my name?', sessionId);
|
|
14
|
+
assert.strictEqual(res2.status, 200, JSON.stringify(res2.body));
|
|
15
|
+
assert.strictEqual(res2.body.sessionId, sessionId, 'sessionId should match');
|
|
16
|
+
const content = res2.body.message.content;
|
|
17
|
+
assert(
|
|
18
|
+
content.toLowerCase().includes('testuser42') || content.includes('TestUser42'),
|
|
19
|
+
`Expected agent to remember name "TestUser42", got: ${content}`
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
await test('Multi-turn chat: session messages are stored and retrievable', async () => {
|
|
24
|
+
// Start a session
|
|
25
|
+
const res1 = await client.chat.send('hello', 'Count to three: one, two, three.');
|
|
26
|
+
assert.strictEqual(res1.status, 200);
|
|
27
|
+
const { sessionId } = res1.body;
|
|
28
|
+
|
|
29
|
+
// Get session messages
|
|
30
|
+
const msgsRes = await client.sessions.messages(sessionId);
|
|
31
|
+
assert.strictEqual(msgsRes.status, 200, JSON.stringify(msgsRes.body));
|
|
32
|
+
assert(Array.isArray(msgsRes.body.messages), 'messages should be array');
|
|
33
|
+
assert(msgsRes.body.messages.length >= 3, `Expected ≥3 messages (system+user+assistant), got: ${msgsRes.body.messages.length}`);
|
|
34
|
+
|
|
35
|
+
const roles = msgsRes.body.messages.map(m => m.role);
|
|
36
|
+
assert(roles.includes('system'), 'system message missing');
|
|
37
|
+
assert(roles.includes('user'), 'user message missing');
|
|
38
|
+
assert(roles.includes('assistant'), 'assistant message missing');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
await test('Multi-turn chat: using closed session returns 400', async () => {
|
|
42
|
+
// Create a session
|
|
43
|
+
const res1 = await client.chat.send('hello', 'Hi');
|
|
44
|
+
const { sessionId } = res1.body;
|
|
45
|
+
|
|
46
|
+
// Close it
|
|
47
|
+
await client.sessions.list(); // ensure sessions endpoint works
|
|
48
|
+
const delRes = await fetch(`http://localhost:5050/sessions/${sessionId}`, { method: 'DELETE' });
|
|
49
|
+
assert.strictEqual(delRes.status, 200);
|
|
50
|
+
|
|
51
|
+
// Try to use it
|
|
52
|
+
const res2 = await client.chat.send('hello', 'Hello again', sessionId);
|
|
53
|
+
assert.strictEqual(res2.status, 400, `Expected 400 for closed session, got: ${res2.status}`);
|
|
54
|
+
assert.strictEqual(res2.body.error.code, 'SESSION_CLOSED');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
await test('Sessions list endpoint works', async () => {
|
|
58
|
+
const res = await client.sessions.list();
|
|
59
|
+
assert.strictEqual(res.status, 200);
|
|
60
|
+
assert(Array.isArray(res.body.sessions), 'sessions should be array');
|
|
61
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 5: Writer agent chat (creative content generation) ───────────────────
|
|
4
|
+
|
|
5
|
+
await test('Writer agent: generates a short poem on demand', async () => {
|
|
6
|
+
const res = await client.chat.send('writer', 'Write a 4-line poem about a curious robot.');
|
|
7
|
+
assert.strictEqual(res.status, 200, JSON.stringify(res.body));
|
|
8
|
+
const content = res.body.message.content;
|
|
9
|
+
assert(content.length > 50, `Expected a poem (>50 chars), got: ${content}`);
|
|
10
|
+
// A poem should have line breaks
|
|
11
|
+
assert(content.includes('\n'), `Expected line breaks in poem, got: ${content}`);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
await test('Writer agent: writes a short story opening', async () => {
|
|
15
|
+
const res = await client.chat.send('writer', 'Write a 3-sentence opening for a sci-fi story set on Mars.');
|
|
16
|
+
assert.strictEqual(res.status, 200, JSON.stringify(res.body));
|
|
17
|
+
const content = res.body.message.content;
|
|
18
|
+
assert(content.length > 100, `Expected story opening (>100 chars), got: ${content}`);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
await test('Writer agent: summarizes a text', async () => {
|
|
22
|
+
const res = await client.chat.send('writer',
|
|
23
|
+
'Summarize this in one sentence: "The quick brown fox jumps over the lazy dog. This pangram has been used for decades to test typewriters and fonts because it contains every letter of the English alphabet."'
|
|
24
|
+
);
|
|
25
|
+
assert.strictEqual(res.status, 200, JSON.stringify(res.body));
|
|
26
|
+
const content = res.body.message.content;
|
|
27
|
+
assert(content.length > 20, 'Summary too short');
|
|
28
|
+
assert(content.length < 500, 'Summary too long — should be one sentence');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
await test('Writer agent: task mode produces document output', async () => {
|
|
32
|
+
const res = await client.tasks.create('writer',
|
|
33
|
+
'Write a short README for a fictional CLI tool called "quickfetch" that downloads URLs. Keep it under 200 words.'
|
|
34
|
+
);
|
|
35
|
+
assert.strictEqual(res.status, 202, `Expected 202, got ${res.status}: ${JSON.stringify(res.body)}`);
|
|
36
|
+
const { taskId } = res.body;
|
|
37
|
+
assert(taskId, 'taskId missing');
|
|
38
|
+
|
|
39
|
+
const task = await client.pollTask(taskId, { timeout: 90000 });
|
|
40
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
41
|
+
assert(task.output, 'output missing');
|
|
42
|
+
assert(task.output.length > 100, 'README too short');
|
|
43
|
+
// Should mention the tool name
|
|
44
|
+
assert(
|
|
45
|
+
task.output.toLowerCase().includes('quickfetch'),
|
|
46
|
+
`README should mention quickfetch, got: ${task.output.slice(0, 200)}`
|
|
47
|
+
);
|
|
48
|
+
}, { slow: true });
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 6: Task mode basics (hello agent — no tools) ─────────────────────────
|
|
4
|
+
|
|
5
|
+
await test('Task create returns 202 with taskId', async () => {
|
|
6
|
+
const res = await client.tasks.create('hello', 'What is the capital of France? Reply in one word.');
|
|
7
|
+
assert.strictEqual(res.status, 202, `Expected 202, got ${res.status}: ${JSON.stringify(res.body)}`);
|
|
8
|
+
assert(res.body.taskId, 'taskId missing');
|
|
9
|
+
assert.strictEqual(res.body.status, 'pending');
|
|
10
|
+
assert(res.body.taskId.startsWith('task_'), `taskId should start with task_, got: ${res.body.taskId}`);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
await test('Task completes with expected output', async () => {
|
|
14
|
+
const res = await client.tasks.create('hello', 'What is the capital of France? Reply in one word.');
|
|
15
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 60000 });
|
|
16
|
+
assert.strictEqual(task.status, 'finished', `Expected finished, got ${task.status}: ${task.error}`);
|
|
17
|
+
assert(task.output, 'output missing');
|
|
18
|
+
assert(
|
|
19
|
+
task.output.toLowerCase().includes('paris'),
|
|
20
|
+
`Expected "Paris" in output, got: ${task.output}`
|
|
21
|
+
);
|
|
22
|
+
}, { slow: true });
|
|
23
|
+
|
|
24
|
+
await test('GET /tasks/:id returns task details', async () => {
|
|
25
|
+
const createRes = await client.tasks.create('hello', 'Say the word: TASK_GET_TEST');
|
|
26
|
+
const { taskId } = createRes.body;
|
|
27
|
+
|
|
28
|
+
// Poll completion
|
|
29
|
+
const task = await client.pollTask(taskId, { timeout: 60000 });
|
|
30
|
+
|
|
31
|
+
// Now fetch by ID
|
|
32
|
+
const getRes = await client.tasks.get(taskId);
|
|
33
|
+
assert.strictEqual(getRes.status, 200, JSON.stringify(getRes.body));
|
|
34
|
+
assert.strictEqual(getRes.body.task.id, taskId);
|
|
35
|
+
assert(getRes.body.task.agent_name === 'hello', `agent_name should be hello, got: ${getRes.body.task.agent_name}`);
|
|
36
|
+
assert(['finished', 'failed'].includes(getRes.body.task.status));
|
|
37
|
+
}, { slow: true });
|
|
38
|
+
|
|
39
|
+
await test('GET /tasks/:id returns 404 for unknown task', async () => {
|
|
40
|
+
const res = await client.tasks.get('task_nonexistent_xyz_123');
|
|
41
|
+
assert.strictEqual(res.status, 404);
|
|
42
|
+
assert.strictEqual(res.body.error.code, 'TASK_NOT_FOUND');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
await test('GET /tasks returns list', async () => {
|
|
46
|
+
const res = await client.tasks.list();
|
|
47
|
+
assert.strictEqual(res.status, 200);
|
|
48
|
+
assert(Array.isArray(res.body.tasks), 'tasks should be array');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
await test('Task events are recorded', async () => {
|
|
52
|
+
const createRes = await client.tasks.create('hello', 'Say: EVENTS_TEST_OK');
|
|
53
|
+
const { taskId } = createRes.body;
|
|
54
|
+
await client.pollTask(taskId, { timeout: 60000 });
|
|
55
|
+
|
|
56
|
+
const eventsRes = await client.tasks.events(taskId);
|
|
57
|
+
assert.strictEqual(eventsRes.status, 200);
|
|
58
|
+
assert(Array.isArray(eventsRes.body.events), 'events should be array');
|
|
59
|
+
assert(eventsRes.body.events.length > 0, 'should have at least one event');
|
|
60
|
+
const types = eventsRes.body.events.map(e => e.type);
|
|
61
|
+
assert(types.some(t => t.includes('iteration') || t.includes('status')), `Expected iteration/status events, got: ${types.join(', ')}`);
|
|
62
|
+
}, { slow: true });
|
|
63
|
+
|
|
64
|
+
await test('Task create with unknown agent returns 404', async () => {
|
|
65
|
+
const res = await client.tasks.create('agent-does-not-exist-xyz', 'Do something');
|
|
66
|
+
assert.strictEqual(res.status, 404);
|
|
67
|
+
assert.strictEqual(res.body.error.code, 'AGENT_NOT_FOUND');
|
|
68
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 7: Task mode with tools (assistant agent) ────────────────────────────
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const PROJECT_ROOT = path.join(__dirname, '../..');
|
|
7
|
+
|
|
8
|
+
await test('Assistant task: list a directory', async () => {
|
|
9
|
+
const res = await client.tasks.create('assistant',
|
|
10
|
+
`List the files in the directory: ${PROJECT_ROOT}/test. Report back the filenames you see.`
|
|
11
|
+
);
|
|
12
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
13
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 90000 });
|
|
14
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
15
|
+
assert(task.output, 'output missing');
|
|
16
|
+
// Should mention runner.js or client.js
|
|
17
|
+
assert(
|
|
18
|
+
task.output.includes('runner.js') || task.output.includes('client.js') || task.output.includes('integration'),
|
|
19
|
+
`Expected file listing to mention runner.js/client.js/integration, got: ${task.output.slice(0, 500)}`
|
|
20
|
+
);
|
|
21
|
+
}, { slow: true });
|
|
22
|
+
|
|
23
|
+
await test('Assistant task: read a specific file', async () => {
|
|
24
|
+
const res = await client.tasks.create('assistant',
|
|
25
|
+
`Read the file at ${PROJECT_ROOT}/package.json and tell me the "name" field value.`
|
|
26
|
+
);
|
|
27
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
28
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 90000 });
|
|
29
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
30
|
+
assert(task.output, 'output missing');
|
|
31
|
+
assert(
|
|
32
|
+
task.output.includes('veilcli'),
|
|
33
|
+
`Expected name "veilcli" in output, got: ${task.output.slice(0, 500)}`
|
|
34
|
+
);
|
|
35
|
+
}, { slow: true });
|
|
36
|
+
|
|
37
|
+
await test('Assistant task: write and verify a file', async () => {
|
|
38
|
+
const testFile = `${PROJECT_ROOT}/test/results/write-test-${Date.now()}.txt`;
|
|
39
|
+
const res = await client.tasks.create('assistant',
|
|
40
|
+
`Create a file at "${testFile}" with content: "WRITE_TEST_SUCCESS". Then confirm it was written.`
|
|
41
|
+
);
|
|
42
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
43
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 90000 });
|
|
44
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
45
|
+
|
|
46
|
+
// Verify the file was actually created
|
|
47
|
+
const fs = require('fs');
|
|
48
|
+
try {
|
|
49
|
+
const written = fs.existsSync(testFile);
|
|
50
|
+
assert(written, `File was not created at ${testFile}`);
|
|
51
|
+
if (written) fs.unlinkSync(testFile); // cleanup
|
|
52
|
+
} catch (e) {
|
|
53
|
+
// File might be in a different location — just check output mentions success
|
|
54
|
+
assert(
|
|
55
|
+
task.output.toLowerCase().includes('success') ||
|
|
56
|
+
task.output.toLowerCase().includes('written') ||
|
|
57
|
+
task.output.toLowerCase().includes('created'),
|
|
58
|
+
`Expected success confirmation in output, got: ${task.output.slice(0, 500)}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}, { slow: true });
|
|
62
|
+
|
|
63
|
+
await test('Assistant task: grep for a pattern in files', async () => {
|
|
64
|
+
const res = await client.tasks.create('assistant',
|
|
65
|
+
`Search for the word "runner" in files under ${PROJECT_ROOT}/test. Tell me which files contain it and on what lines.`
|
|
66
|
+
);
|
|
67
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
68
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 90000 });
|
|
69
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
70
|
+
assert(
|
|
71
|
+
task.output.includes('runner') || task.output.includes('runner.js'),
|
|
72
|
+
`Expected mention of runner.js in output, got: ${task.output.slice(0, 500)}`
|
|
73
|
+
);
|
|
74
|
+
}, { slow: true });
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 8: Coder agent — read-only code analysis ─────────────────────────────
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const PROJECT_ROOT = path.join(__dirname, '../..');
|
|
7
|
+
|
|
8
|
+
await test('Coder: analyze package.json and identify dependencies', async () => {
|
|
9
|
+
const res = await client.tasks.create('coder',
|
|
10
|
+
`Read ${PROJECT_ROOT}/package.json and list all the production dependencies and their versions.`
|
|
11
|
+
);
|
|
12
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
13
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 120000 });
|
|
14
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
15
|
+
assert(task.output, 'output missing');
|
|
16
|
+
// Should mention known deps
|
|
17
|
+
assert(
|
|
18
|
+
task.output.includes('express') || task.output.includes('better-sqlite3') || task.output.includes('ajv'),
|
|
19
|
+
`Expected known dependencies in output, got: ${task.output.slice(0, 500)}`
|
|
20
|
+
);
|
|
21
|
+
}, { slow: true });
|
|
22
|
+
|
|
23
|
+
await test('Coder: explain what a specific file does', async () => {
|
|
24
|
+
const res = await client.tasks.create('coder',
|
|
25
|
+
`Read ${PROJECT_ROOT}/utils/id.js and explain in 2-3 sentences what it does and how it generates IDs.`
|
|
26
|
+
);
|
|
27
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
28
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 120000 });
|
|
29
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
30
|
+
assert(task.output, 'output missing');
|
|
31
|
+
assert(
|
|
32
|
+
task.output.toLowerCase().includes('crypto') || task.output.toLowerCase().includes('random') || task.output.toLowerCase().includes('id'),
|
|
33
|
+
`Expected explanation mentioning crypto/random/id, got: ${task.output.slice(0, 500)}`
|
|
34
|
+
);
|
|
35
|
+
}, { slow: true });
|
|
36
|
+
|
|
37
|
+
await test('Coder: list project structure', async () => {
|
|
38
|
+
const res = await client.tasks.create('coder',
|
|
39
|
+
`List the top-level files and folders in ${PROJECT_ROOT}. Give me a brief description of each.`
|
|
40
|
+
);
|
|
41
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
42
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 120000 });
|
|
43
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
44
|
+
assert(task.output, 'output missing');
|
|
45
|
+
// Should mention key directories
|
|
46
|
+
assert(
|
|
47
|
+
task.output.includes('tools') || task.output.includes('core') || task.output.includes('cli'),
|
|
48
|
+
`Expected project structure mentions, got: ${task.output.slice(0, 500)}`
|
|
49
|
+
);
|
|
50
|
+
}, { slow: true });
|
|
51
|
+
|
|
52
|
+
await test('Coder: bash tool is denied (read-only agent)', async () => {
|
|
53
|
+
const res = await client.tasks.create('coder',
|
|
54
|
+
`Try to run the bash command "echo BASH_TEST" and report whether it worked.`
|
|
55
|
+
);
|
|
56
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
57
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 60000 });
|
|
58
|
+
// Task should still finish (agent handles denial gracefully), but bash should be blocked
|
|
59
|
+
assert(['finished', 'failed'].includes(task.status));
|
|
60
|
+
if (task.status === 'finished') {
|
|
61
|
+
// The agent should report it couldn't run bash or it was denied
|
|
62
|
+
const output = (task.output || '').toLowerCase();
|
|
63
|
+
assert(
|
|
64
|
+
output.includes('deny') || output.includes('permission') || output.includes('not') ||
|
|
65
|
+
output.includes('cannot') || output.includes("don't") || output.includes('unable') || !output.includes('bash_test'),
|
|
66
|
+
`Expected bash to be denied, but output might show success: ${task.output?.slice(0, 300)}`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}, { slow: true });
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 9: Analyst agent — memory + todo tools in task mode ──────────────────
|
|
4
|
+
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const PROJECT_ROOT = path.join(__dirname, '../..');
|
|
7
|
+
|
|
8
|
+
await test('Analyst: uses todo_write and reads a directory', async () => {
|
|
9
|
+
const res = await client.tasks.create('analyst',
|
|
10
|
+
`Quick task: use todo_write to create a 2-item checklist ("list tools dir", "report findings"), then use list_dir to list ${PROJECT_ROOT}/tools and report the filenames. Keep it brief.`
|
|
11
|
+
);
|
|
12
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
13
|
+
// Give extra buffer: agent maxDurationSeconds=180, so poll at 240s
|
|
14
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 240000 });
|
|
15
|
+
assert(['finished', 'failed'].includes(task.status), `Unexpected status: ${task.status}`);
|
|
16
|
+
if (task.status === 'finished') {
|
|
17
|
+
assert(task.output, 'output missing');
|
|
18
|
+
assert(
|
|
19
|
+
task.output.includes('bash') || task.output.includes('read_file') || task.output.includes('grep') || task.output.includes('.js'),
|
|
20
|
+
`Expected tool files listed, got: ${task.output.slice(0, 500)}`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}, { slow: true });
|
|
24
|
+
|
|
25
|
+
await test('Analyst: uses memory_write to save findings', async () => {
|
|
26
|
+
const res = await client.tasks.create('analyst',
|
|
27
|
+
`Read ${PROJECT_ROOT}/package.json and ${PROJECT_ROOT}/README.md (if it exists). Use memory_write to save key facts about this project (name, version, purpose). Then summarize what you saved.`
|
|
28
|
+
);
|
|
29
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
30
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 120000 });
|
|
31
|
+
// Task should succeed and report what was saved
|
|
32
|
+
assert(['finished', 'failed'].includes(task.status));
|
|
33
|
+
if (task.status === 'finished') {
|
|
34
|
+
assert(task.output, 'output missing');
|
|
35
|
+
// Should mention the package name or memory
|
|
36
|
+
assert(
|
|
37
|
+
task.output.includes('veil') || task.output.includes('memory') || task.output.includes('saved') || task.output.includes('Veil'),
|
|
38
|
+
`Expected memory save mention, got: ${task.output.slice(0, 500)}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
}, { slow: true });
|
|
42
|
+
|
|
43
|
+
await test('Analyst: full project analysis produces comprehensive report', async () => {
|
|
44
|
+
const res = await client.tasks.create('analyst',
|
|
45
|
+
`Perform a complete analysis of the project at ${PROJECT_ROOT}. Steps:
|
|
46
|
+
1. List top-level files and folders
|
|
47
|
+
2. Read package.json to identify the project
|
|
48
|
+
3. Read at least 3 source files from core/ or utils/
|
|
49
|
+
4. Use grep to find how many JS files the project has
|
|
50
|
+
5. Write a structured report covering: what the project does, its architecture, key dependencies, and notable patterns.`
|
|
51
|
+
);
|
|
52
|
+
assert.strictEqual(res.status, 202, JSON.stringify(res.body));
|
|
53
|
+
const task = await client.pollTask(res.body.taskId, { timeout: 180000 });
|
|
54
|
+
assert.strictEqual(task.status, 'finished', `Task failed: ${task.error}`);
|
|
55
|
+
assert(task.output, 'output missing');
|
|
56
|
+
// Comprehensive report should be long
|
|
57
|
+
assert(task.output.length > 300, `Report too short (${task.output.length} chars): ${task.output.slice(0, 200)}`);
|
|
58
|
+
// Should mention key aspects
|
|
59
|
+
assert(
|
|
60
|
+
task.output.toLowerCase().includes('agent') || task.output.toLowerCase().includes('veil'),
|
|
61
|
+
`Expected "agent" or "veil" in report, got: ${task.output.slice(0, 300)}`
|
|
62
|
+
);
|
|
63
|
+
}, { slow: true });
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// ── Test 10: Advanced task operations ─────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
await test('Task cancellation works', async () => {
|
|
6
|
+
// Create a task, immediately cancel it before it can finish
|
|
7
|
+
const res = await client.tasks.create('hello',
|
|
8
|
+
'Count very slowly from 1 to 1000, saying each number with a pause.'
|
|
9
|
+
);
|
|
10
|
+
assert.strictEqual(res.status, 202);
|
|
11
|
+
const { taskId } = res.body;
|
|
12
|
+
|
|
13
|
+
// Give it a moment to start, then cancel
|
|
14
|
+
await new Promise(r => setTimeout(r, 500));
|
|
15
|
+
const cancelRes = await client.tasks.cancel(taskId);
|
|
16
|
+
assert.strictEqual(cancelRes.status, 200, JSON.stringify(cancelRes.body));
|
|
17
|
+
assert.strictEqual(cancelRes.body.status, 'canceled');
|
|
18
|
+
|
|
19
|
+
// Verify final state
|
|
20
|
+
const getRes = await client.tasks.get(taskId);
|
|
21
|
+
assert(['canceled', 'finished'].includes(getRes.body.task.status),
|
|
22
|
+
`Expected canceled or finished (if it finished first), got: ${getRes.body.task.status}`);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await test('Multiple concurrent tasks run independently', async () => {
|
|
26
|
+
// Launch 3 tasks simultaneously
|
|
27
|
+
const [r1, r2, r3] = await Promise.all([
|
|
28
|
+
client.tasks.create('hello', 'What is 10 + 5? Reply with just the number.'),
|
|
29
|
+
client.tasks.create('hello', 'What is 20 + 3? Reply with just the number.'),
|
|
30
|
+
client.tasks.create('hello', 'What is 7 + 8? Reply with just the number.'),
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
assert.strictEqual(r1.status, 202);
|
|
34
|
+
assert.strictEqual(r2.status, 202);
|
|
35
|
+
assert.strictEqual(r3.status, 202);
|
|
36
|
+
|
|
37
|
+
// All different task IDs
|
|
38
|
+
const ids = [r1.body.taskId, r2.body.taskId, r3.body.taskId];
|
|
39
|
+
assert.strictEqual(new Set(ids).size, 3, 'All taskIds should be unique');
|
|
40
|
+
|
|
41
|
+
// Poll all to completion
|
|
42
|
+
const [t1, t2, t3] = await Promise.all([
|
|
43
|
+
client.pollTask(r1.body.taskId, { timeout: 90000 }),
|
|
44
|
+
client.pollTask(r2.body.taskId, { timeout: 90000 }),
|
|
45
|
+
client.pollTask(r3.body.taskId, { timeout: 90000 }),
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
assert.strictEqual(t1.status, 'finished', `Task 1 failed: ${t1.error}`);
|
|
49
|
+
assert.strictEqual(t2.status, 'finished', `Task 2 failed: ${t2.error}`);
|
|
50
|
+
assert.strictEqual(t3.status, 'finished', `Task 3 failed: ${t3.error}`);
|
|
51
|
+
|
|
52
|
+
assert(String(t1.output || '').includes('15'), `Task 1: expected 15 in output, got: ${t1.output}`);
|
|
53
|
+
assert(String(t2.output || '').includes('23'), `Task 2: expected 23 in output, got: ${t2.output}`);
|
|
54
|
+
assert(String(t3.output || '').includes('15'), `Task 3: expected 15 in output, got: ${t3.output}`);
|
|
55
|
+
}, { slow: true });
|
|
56
|
+
|
|
57
|
+
await test('Task list filtering by agent works', async () => {
|
|
58
|
+
// Create a task for hello agent
|
|
59
|
+
await client.tasks.create('hello', 'Say: FILTER_TEST');
|
|
60
|
+
// Give it a moment
|
|
61
|
+
await new Promise(r => setTimeout(r, 300));
|
|
62
|
+
|
|
63
|
+
const res = await client.tasks.list({ agentName: 'hello', limit: 5 });
|
|
64
|
+
assert.strictEqual(res.status, 200);
|
|
65
|
+
const allHello = res.body.tasks.every(t => t.agent_name === 'hello');
|
|
66
|
+
assert(allHello, 'All tasks should be from hello agent');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await test('Task list filtering by status works', async () => {
|
|
70
|
+
const res = await client.tasks.list({ status: 'finished', limit: 10 });
|
|
71
|
+
assert.strictEqual(res.status, 200);
|
|
72
|
+
const allFinished = res.body.tasks.every(t => t.status === 'finished');
|
|
73
|
+
assert(allFinished, 'All returned tasks should be finished');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
await test('Task respond returns 400 when task is not waiting', async () => {
|
|
77
|
+
// Create a task that completes quickly
|
|
78
|
+
const createRes = await client.tasks.create('hello', 'Say: RESPOND_TEST');
|
|
79
|
+
const task = await client.pollTask(createRes.body.taskId, { timeout: 60000 });
|
|
80
|
+
|
|
81
|
+
// Try to respond to a finished task
|
|
82
|
+
const respondRes = await client.tasks.respond(task.id, 'Some response');
|
|
83
|
+
assert.strictEqual(respondRes.status, 400);
|
|
84
|
+
assert.strictEqual(respondRes.body.error.code, 'TASK_NOT_WAITING');
|
|
85
|
+
}, { slow: true });
|