@pi-unipi/subagents 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/package.json +1 -1
  2. package/src/__tests__/badge-generation.test.ts +244 -0
  3. package/src/index.ts +28 -7
  4. package/dist/__tests__/config.test.d.ts +0 -11
  5. package/dist/__tests__/config.test.d.ts.map +0 -1
  6. package/dist/__tests__/config.test.js +0 -196
  7. package/dist/__tests__/config.test.js.map +0 -1
  8. package/dist/__tests__/esc-propagation.test.d.ts +0 -10
  9. package/dist/__tests__/esc-propagation.test.d.ts.map +0 -1
  10. package/dist/__tests__/esc-propagation.test.js +0 -140
  11. package/dist/__tests__/esc-propagation.test.js.map +0 -1
  12. package/dist/__tests__/file-lock.test.d.ts +0 -12
  13. package/dist/__tests__/file-lock.test.d.ts.map +0 -1
  14. package/dist/__tests__/file-lock.test.js +0 -187
  15. package/dist/__tests__/file-lock.test.js.map +0 -1
  16. package/dist/__tests__/workflow-integration.test.d.ts +0 -12
  17. package/dist/__tests__/workflow-integration.test.d.ts.map +0 -1
  18. package/dist/__tests__/workflow-integration.test.js +0 -261
  19. package/dist/__tests__/workflow-integration.test.js.map +0 -1
  20. package/dist/agent-manager.d.ts +0 -75
  21. package/dist/agent-manager.d.ts.map +0 -1
  22. package/dist/agent-manager.js +0 -268
  23. package/dist/agent-manager.js.map +0 -1
  24. package/dist/agent-runner.d.ts +0 -51
  25. package/dist/agent-runner.d.ts.map +0 -1
  26. package/dist/agent-runner.js +0 -254
  27. package/dist/agent-runner.js.map +0 -1
  28. package/dist/config.d.ts +0 -24
  29. package/dist/config.d.ts.map +0 -1
  30. package/dist/config.js +0 -132
  31. package/dist/config.js.map +0 -1
  32. package/dist/conversation-viewer.d.ts +0 -40
  33. package/dist/conversation-viewer.d.ts.map +0 -1
  34. package/dist/conversation-viewer.js +0 -276
  35. package/dist/conversation-viewer.js.map +0 -1
  36. package/dist/custom-agents.d.ts +0 -14
  37. package/dist/custom-agents.d.ts.map +0 -1
  38. package/dist/custom-agents.js +0 -106
  39. package/dist/custom-agents.js.map +0 -1
  40. package/dist/file-lock.d.ts +0 -42
  41. package/dist/file-lock.d.ts.map +0 -1
  42. package/dist/file-lock.js +0 -91
  43. package/dist/file-lock.js.map +0 -1
  44. package/dist/index.d.ts +0 -10
  45. package/dist/index.d.ts.map +0 -1
  46. package/dist/index.js +0 -653
  47. package/dist/index.js.map +0 -1
  48. package/dist/model-resolver.d.ts +0 -19
  49. package/dist/model-resolver.d.ts.map +0 -1
  50. package/dist/model-resolver.js +0 -61
  51. package/dist/model-resolver.js.map +0 -1
  52. package/dist/prompts.d.ts +0 -13
  53. package/dist/prompts.d.ts.map +0 -1
  54. package/dist/prompts.js +0 -31
  55. package/dist/prompts.js.map +0 -1
  56. package/dist/types.d.ts +0 -96
  57. package/dist/types.d.ts.map +0 -1
  58. package/dist/types.js +0 -36
  59. package/dist/types.js.map +0 -1
  60. package/dist/widget.d.ts +0 -55
  61. package/dist/widget.d.ts.map +0 -1
  62. package/dist/widget.js +0 -404
  63. package/dist/widget.js.map +0 -1
@@ -1,140 +0,0 @@
1
- /**
2
- * Test: ESC propagation — all children abort on parent ESC
3
- *
4
- * Verifies:
5
- * - forwardAbortSignal wires parent signal to child session
6
- * - abortAll stops all running agents
7
- * - All agents stop within reasonable time
8
- */
9
- import { describe, it } from "node:test";
10
- import assert from "node:assert/strict";
11
- // Mock AbortController to track abort calls
12
- function createMockAbortController() {
13
- let aborted = false;
14
- const listeners = [];
15
- return {
16
- get signal() {
17
- return {
18
- aborted,
19
- addEventListener: (_event, listener) => {
20
- listeners.push(listener);
21
- },
22
- removeEventListener: (_event, listener) => {
23
- const idx = listeners.indexOf(listener);
24
- if (idx !== -1)
25
- listeners.splice(idx, 1);
26
- },
27
- };
28
- },
29
- abort() {
30
- aborted = true;
31
- for (const listener of listeners)
32
- listener();
33
- },
34
- get wasAborted() {
35
- return aborted;
36
- },
37
- };
38
- }
39
- describe("ESC Propagation", () => {
40
- describe("forwardAbortSignal", () => {
41
- it("should call session.abort() when signal fires", () => {
42
- // Simulate the forwardAbortSignal logic from agent-runner.ts
43
- const sessionAborted = { value: false };
44
- const session = { abort: () => { sessionAborted.value = true; } };
45
- const controller = createMockAbortController();
46
- // Wire abort signal
47
- const onAbort = () => session.abort();
48
- controller.signal.addEventListener("abort", onAbort);
49
- // Trigger abort
50
- controller.abort();
51
- assert.equal(sessionAborted.value, true, "Session should be aborted");
52
- });
53
- it("should not call session.abort() if signal not fired", () => {
54
- const sessionAborted = { value: false };
55
- const session = { abort: () => { sessionAborted.value = true; } };
56
- const controller = createMockAbortController();
57
- const onAbort = () => session.abort();
58
- controller.signal.addEventListener("abort", onAbort);
59
- // Don't abort
60
- assert.equal(sessionAborted.value, false, "Session should not be aborted");
61
- });
62
- it("should cleanup listener when returned function called", () => {
63
- const controller = createMockAbortController();
64
- let callCount = 0;
65
- const onAbort = () => { callCount++; };
66
- controller.signal.addEventListener("abort", onAbort);
67
- // Simulate cleanup
68
- const cleanup = () => controller.signal.removeEventListener("abort", onAbort);
69
- cleanup();
70
- controller.abort();
71
- assert.equal(callCount, 0, "Listener should not fire after cleanup");
72
- });
73
- });
74
- describe("abortAll", () => {
75
- it("should abort all running agents", () => {
76
- const agents = new Map();
77
- // Create 3 mock agents
78
- for (let i = 0; i < 3; i++) {
79
- const controller = createMockAbortController();
80
- agents.set(`agent-${i}`, {
81
- abortController: controller,
82
- status: "running",
83
- });
84
- }
85
- // Simulate abortAll
86
- let abortedCount = 0;
87
- for (const [id, record] of agents) {
88
- if (record.status === "running") {
89
- record.abortController.abort();
90
- record.status = "stopped";
91
- abortedCount++;
92
- }
93
- }
94
- assert.equal(abortedCount, 3, "Should abort all 3 agents");
95
- for (const [_, record] of agents) {
96
- assert.equal(record.status, "stopped", "All agents should be stopped");
97
- assert.equal(record.abortController.wasAborted, true, "All controllers should be aborted");
98
- }
99
- });
100
- it("should handle queued agents by removing from queue", () => {
101
- const queue = [
102
- { id: "queued-1", status: "queued" },
103
- { id: "queued-2", status: "queued" },
104
- ];
105
- const agents = new Map();
106
- for (const item of queue) {
107
- agents.set(item.id, { status: item.status });
108
- }
109
- // Simulate abortAll for queued
110
- for (const item of queue) {
111
- const record = agents.get(item.id);
112
- if (record) {
113
- record.status = "stopped";
114
- }
115
- }
116
- queue.length = 0;
117
- assert.equal(queue.length, 0, "Queue should be empty");
118
- for (const [_, record] of agents) {
119
- assert.equal(record.status, "stopped", "All queued agents should be stopped");
120
- }
121
- });
122
- });
123
- describe("ESC timing", () => {
124
- it("should abort within reasonable time", async () => {
125
- const controller = createMockAbortController();
126
- let abortedAt = null;
127
- const startedAt = Date.now();
128
- const onAbort = () => { abortedAt = Date.now(); };
129
- controller.signal.addEventListener("abort", onAbort);
130
- // Simulate abort after small delay
131
- setTimeout(() => controller.abort(), 10);
132
- // Wait for abort
133
- await new Promise(resolve => setTimeout(resolve, 50));
134
- assert.notEqual(abortedAt, null, "Should have aborted");
135
- const elapsed = abortedAt - startedAt;
136
- assert.ok(elapsed < 500, `Abort should happen within 500ms, took ${elapsed}ms`);
137
- });
138
- });
139
- });
140
- //# sourceMappingURL=esc-propagation.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"esc-propagation.test.js","sourceRoot":"","sources":["../../src/__tests__/esc-propagation.test.ts"],"names":[],"mappings":"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"}
@@ -1,12 +0,0 @@
1
- /**
2
- * Test: File locking — concurrent writes to same file queue correctly
3
- *
4
- * Verifies:
5
- * - Per-file locking works correctly
6
- * - Same file writes queue (second waits for first)
7
- * - Different file writes proceed in parallel
8
- * - Lock release unblocks waiting acquires
9
- * - releaseAll releases all locks for an agent
10
- */
11
- export {};
12
- //# sourceMappingURL=file-lock.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-lock.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/file-lock.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -1,187 +0,0 @@
1
- /**
2
- * Test: File locking — concurrent writes to same file queue correctly
3
- *
4
- * Verifies:
5
- * - Per-file locking works correctly
6
- * - Same file writes queue (second waits for first)
7
- * - Different file writes proceed in parallel
8
- * - Lock release unblocks waiting acquires
9
- * - releaseAll releases all locks for an agent
10
- */
11
- import { describe, it } from "node:test";
12
- import assert from "node:assert/strict";
13
- class FileLock {
14
- locks = new Map();
15
- queues = new Map();
16
- async acquire(filePath, agentId) {
17
- while (this.locks.has(filePath)) {
18
- await new Promise((resolve) => {
19
- const queue = this.queues.get(filePath) ?? [];
20
- queue.push(resolve);
21
- this.queues.set(filePath, queue);
22
- });
23
- }
24
- let releaseFn;
25
- const promise = new Promise((resolve) => {
26
- releaseFn = () => {
27
- this.locks.delete(filePath);
28
- resolve();
29
- const queue = this.queues.get(filePath);
30
- if (queue && queue.length > 0) {
31
- const next = queue.shift();
32
- next();
33
- }
34
- };
35
- });
36
- const entry = {
37
- agentId,
38
- filePath,
39
- promise,
40
- release: releaseFn,
41
- };
42
- this.locks.set(filePath, entry);
43
- return releaseFn;
44
- }
45
- isLocked(filePath) {
46
- return this.locks.has(filePath);
47
- }
48
- getHolder(filePath) {
49
- return this.locks.get(filePath)?.agentId;
50
- }
51
- get lockCount() {
52
- return this.locks.size;
53
- }
54
- releaseAll(agentId) {
55
- for (const [filePath, entry] of this.locks) {
56
- if (entry.agentId === agentId) {
57
- entry.release();
58
- }
59
- }
60
- }
61
- clear() {
62
- for (const entry of this.locks.values()) {
63
- entry.release();
64
- }
65
- this.locks.clear();
66
- this.queues.clear();
67
- }
68
- }
69
- describe("FileLock", () => {
70
- describe("Basic locking", () => {
71
- it("should acquire lock on unlocked file", async () => {
72
- const lock = new FileLock();
73
- const release = await lock.acquire("/src/auth.ts", "agent-1");
74
- assert.equal(lock.isLocked("/src/auth.ts"), true);
75
- assert.equal(lock.getHolder("/src/auth.ts"), "agent-1");
76
- assert.equal(lock.lockCount, 1);
77
- release();
78
- assert.equal(lock.isLocked("/src/auth.ts"), false);
79
- });
80
- it("should track multiple locks on different files", async () => {
81
- const lock = new FileLock();
82
- const release1 = await lock.acquire("/src/auth.ts", "agent-1");
83
- const release2 = await lock.acquire("/src/login.ts", "agent-2");
84
- assert.equal(lock.lockCount, 2);
85
- assert.equal(lock.getHolder("/src/auth.ts"), "agent-1");
86
- assert.equal(lock.getHolder("/src/login.ts"), "agent-2");
87
- release1();
88
- release2();
89
- assert.equal(lock.lockCount, 0);
90
- });
91
- });
92
- describe("Queuing behavior", () => {
93
- it("should queue second acquire on same file", async () => {
94
- const lock = new FileLock();
95
- const events = [];
96
- // First acquire
97
- const release1 = await lock.acquire("/src/auth.ts", "agent-1");
98
- events.push("agent-1-acquired");
99
- // Second acquire (should queue)
100
- const acquire2Promise = lock.acquire("/src/auth.ts", "agent-2").then((release) => {
101
- events.push("agent-2-acquired");
102
- return release;
103
- });
104
- // agent-2 should not have acquired yet
105
- assert.deepEqual(events, ["agent-1-acquired"]);
106
- // Release first lock
107
- release1();
108
- events.push("agent-1-released");
109
- // Wait for agent-2
110
- const release2 = await acquire2Promise;
111
- assert.deepEqual(events, ["agent-1-acquired", "agent-1-released", "agent-2-acquired"]);
112
- release2();
113
- });
114
- it("should queue multiple acquires on same file", async () => {
115
- const lock = new FileLock();
116
- const events = [];
117
- const release1 = await lock.acquire("/src/auth.ts", "agent-1");
118
- events.push("1-acquired");
119
- const p2 = lock.acquire("/src/auth.ts", "agent-2").then(r => { events.push("2-acquired"); return r; });
120
- const p3 = lock.acquire("/src/auth.ts", "agent-3").then(r => { events.push("3-acquired"); return r; });
121
- release1();
122
- const release2 = await p2;
123
- assert.deepEqual(events, ["1-acquired", "2-acquired"]);
124
- release2();
125
- const release3 = await p3;
126
- assert.deepEqual(events, ["1-acquired", "2-acquired", "3-acquired"]);
127
- release3();
128
- });
129
- });
130
- describe("Parallel different files", () => {
131
- it("should allow parallel writes to different files", async () => {
132
- const lock = new FileLock();
133
- const events = [];
134
- // Both should acquire immediately (different files)
135
- const release1 = await lock.acquire("/src/auth.ts", "agent-1");
136
- events.push("auth-acquired");
137
- const release2 = await lock.acquire("/src/login.ts", "agent-2");
138
- events.push("login-acquired");
139
- assert.deepEqual(events, ["auth-acquired", "login-acquired"]);
140
- assert.equal(lock.lockCount, 2);
141
- release1();
142
- release2();
143
- });
144
- });
145
- describe("releaseAll", () => {
146
- it("should release all locks for a specific agent", async () => {
147
- const lock = new FileLock();
148
- // agent-1 holds 3 files
149
- await lock.acquire("/src/a.ts", "agent-1");
150
- await lock.acquire("/src/b.ts", "agent-1");
151
- await lock.acquire("/src/c.ts", "agent-1");
152
- // agent-2 holds 1 file
153
- await lock.acquire("/src/d.ts", "agent-2");
154
- assert.equal(lock.lockCount, 4);
155
- // Release all for agent-1
156
- lock.releaseAll("agent-1");
157
- assert.equal(lock.lockCount, 1);
158
- assert.equal(lock.isLocked("/src/a.ts"), false);
159
- assert.equal(lock.isLocked("/src/b.ts"), false);
160
- assert.equal(lock.isLocked("/src/c.ts"), false);
161
- assert.equal(lock.isLocked("/src/d.ts"), true);
162
- });
163
- it("should unblock queued acquires when releasing all", async () => {
164
- const lock = new FileLock();
165
- const events = [];
166
- const release1 = await lock.acquire("/src/a.ts", "agent-1");
167
- const p2 = lock.acquire("/src/a.ts", "agent-2").then(r => { events.push("agent-2-acquired"); return r; });
168
- // Release all for agent-1
169
- lock.releaseAll("agent-1");
170
- const release2 = await p2;
171
- assert.deepEqual(events, ["agent-2-acquired"]);
172
- release2();
173
- });
174
- });
175
- describe("clear", () => {
176
- it("should release all locks and clear queues", async () => {
177
- const lock = new FileLock();
178
- await lock.acquire("/src/a.ts", "agent-1");
179
- await lock.acquire("/src/b.ts", "agent-2");
180
- lock.clear();
181
- assert.equal(lock.lockCount, 0);
182
- assert.equal(lock.isLocked("/src/a.ts"), false);
183
- assert.equal(lock.isLocked("/src/b.ts"), false);
184
- });
185
- });
186
- });
187
- //# sourceMappingURL=file-lock.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"file-lock.test.js","sourceRoot":"","sources":["../../src/__tests__/file-lock.test.ts"],"names":[],"mappings":"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"}
@@ -1,12 +0,0 @@
1
- /**
2
- * Test: Workflow integration — `/unipi:work` with subagent support
3
- *
4
- * Verifies:
5
- * - spawn_helper and get_helper_result tools are properly defined
6
- * - Agent types (explore, work) are correctly configured
7
- * - Concurrency limit is respected
8
- * - Custom agent type loading works
9
- * - System prompt builder generates correct prompts
10
- */
11
- export {};
12
- //# sourceMappingURL=workflow-integration.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"workflow-integration.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/workflow-integration.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -1,261 +0,0 @@
1
- /**
2
- * Test: Workflow integration — `/unipi:work` with subagent support
3
- *
4
- * Verifies:
5
- * - spawn_helper and get_helper_result tools are properly defined
6
- * - Agent types (explore, work) are correctly configured
7
- * - Concurrency limit is respected
8
- * - Custom agent type loading works
9
- * - System prompt builder generates correct prompts
10
- */
11
- import { describe, it } from "node:test";
12
- import assert from "node:assert/strict";
13
- // Test type definitions
14
- const BUILTIN_TYPES = ["explore", "work"];
15
- // Test prompt builder
16
- function buildAgentPrompt(config, cwd, env, parentSystemPrompt) {
17
- if (config.promptMode === "append") {
18
- return [
19
- parentSystemPrompt,
20
- "",
21
- "---",
22
- "",
23
- `## Agent Role: ${config.displayName ?? config.name}`,
24
- config.systemPrompt,
25
- ].join("\n");
26
- }
27
- return [
28
- `# ${config.displayName ?? config.name}`,
29
- "",
30
- config.systemPrompt,
31
- "",
32
- "---",
33
- "",
34
- `Working directory: ${cwd}`,
35
- `Git: ${env.isGitRepo ? `${env.branch} on ${env.platform}` : "not a git repo"}`,
36
- ].join("\n");
37
- }
38
- // Test concurrency manager
39
- class ConcurrencyManager {
40
- maxConcurrent;
41
- running = 0;
42
- queue = [];
43
- constructor(maxConcurrent) {
44
- this.maxConcurrent = maxConcurrent;
45
- }
46
- async acquire(id) {
47
- if (this.running >= this.maxConcurrent) {
48
- await new Promise((resolve) => {
49
- this.queue.push({ id, resolve });
50
- });
51
- }
52
- this.running++;
53
- let released = false;
54
- return () => {
55
- if (released)
56
- return;
57
- released = true;
58
- this.running--;
59
- // Start next in queue
60
- if (this.queue.length > 0) {
61
- const next = this.queue.shift();
62
- next.resolve();
63
- }
64
- };
65
- }
66
- getRunning() {
67
- return this.running;
68
- }
69
- getQueueLength() {
70
- return this.queue.length;
71
- }
72
- }
73
- describe("Workflow Integration", () => {
74
- describe("Tool Definitions", () => {
75
- it("should define spawn_helper tool with correct parameters", () => {
76
- const toolDef = {
77
- name: "spawn_helper",
78
- description: "Launch a sub-agent for parallel work.",
79
- parameters: {
80
- type: "object",
81
- required: ["type", "prompt", "description"],
82
- properties: {
83
- type: { type: "string" },
84
- prompt: { type: "string" },
85
- description: { type: "string" },
86
- run_in_background: { type: "boolean" },
87
- max_turns: { type: "number" },
88
- model: { type: "string" },
89
- thinking: { type: "string" },
90
- },
91
- },
92
- };
93
- assert.equal(toolDef.name, "spawn_helper");
94
- assert.equal(toolDef.parameters.required.length, 3);
95
- assert.ok(toolDef.parameters.properties.type);
96
- assert.ok(toolDef.parameters.properties.prompt);
97
- assert.ok(toolDef.parameters.properties.description);
98
- });
99
- it("should define get_helper_result tool with correct parameters", () => {
100
- const toolDef = {
101
- name: "get_helper_result",
102
- description: "Check status and retrieve results from a background agent.",
103
- parameters: {
104
- type: "object",
105
- required: ["agent_id"],
106
- properties: {
107
- agent_id: { type: "string" },
108
- wait: { type: "boolean" },
109
- },
110
- },
111
- };
112
- assert.equal(toolDef.name, "get_helper_result");
113
- assert.equal(toolDef.parameters.required.length, 1);
114
- assert.ok(toolDef.parameters.properties.agent_id);
115
- });
116
- });
117
- describe("Agent Types", () => {
118
- it("should have explore and work as builtin types", () => {
119
- assert.deepEqual(BUILTIN_TYPES, ["explore", "work"]);
120
- });
121
- it("should define explore agent with read-only tools", () => {
122
- const exploreConfig = {
123
- name: "explore",
124
- description: "Fast parallel codebase exploration",
125
- builtinToolNames: ["read", "bash", "grep", "find", "ls"],
126
- systemPrompt: "You are a read-only exploration agent.",
127
- promptMode: "replace",
128
- extensions: true,
129
- skills: true,
130
- };
131
- assert.ok(!exploreConfig.builtinToolNames?.includes("write"));
132
- assert.ok(!exploreConfig.builtinToolNames?.includes("edit"));
133
- assert.ok(exploreConfig.builtinToolNames?.includes("read"));
134
- });
135
- it("should define work agent with read-write tools", () => {
136
- const workConfig = {
137
- name: "work",
138
- description: "Parallel file writes with transparent locking",
139
- builtinToolNames: ["read", "write", "edit", "bash", "grep", "find", "ls"],
140
- systemPrompt: "You are a read-write work agent.",
141
- promptMode: "replace",
142
- extensions: true,
143
- skills: true,
144
- };
145
- assert.ok(workConfig.builtinToolNames?.includes("write"));
146
- assert.ok(workConfig.builtinToolNames?.includes("edit"));
147
- assert.ok(workConfig.builtinToolNames?.includes("read"));
148
- });
149
- });
150
- describe("System Prompt Builder", () => {
151
- const env = {
152
- isGitRepo: true,
153
- branch: "main",
154
- platform: "GitHub",
155
- };
156
- it("should build replace mode prompt", () => {
157
- const config = {
158
- name: "explore",
159
- displayName: "Explorer",
160
- description: "Test",
161
- systemPrompt: "Find all authentication files.",
162
- promptMode: "replace",
163
- extensions: true,
164
- skills: true,
165
- };
166
- const prompt = buildAgentPrompt(config, "/workspace", env, "Parent prompt");
167
- assert.ok(prompt.startsWith("# Explorer"));
168
- assert.ok(prompt.includes("Find all authentication files."));
169
- assert.ok(prompt.includes("Working directory: /workspace"));
170
- assert.ok(!prompt.includes("Parent prompt"));
171
- });
172
- it("should build append mode prompt", () => {
173
- const config = {
174
- name: "work",
175
- displayName: "Worker",
176
- description: "Test",
177
- systemPrompt: "Refactor the auth module.",
178
- promptMode: "append",
179
- extensions: true,
180
- skills: true,
181
- };
182
- const prompt = buildAgentPrompt(config, "/workspace", env, "Parent prompt");
183
- assert.ok(prompt.startsWith("Parent prompt"));
184
- assert.ok(prompt.includes("## Agent Role: Worker"));
185
- assert.ok(prompt.includes("Refactor the auth module."));
186
- });
187
- });
188
- describe("Concurrency Limit", () => {
189
- it("should respect max concurrent limit", async () => {
190
- const manager = new ConcurrencyManager(2);
191
- const events = [];
192
- // Start 3 agents, only 2 should run immediately
193
- const release1 = await manager.acquire("agent-1");
194
- events.push("agent-1-started");
195
- const release2 = await manager.acquire("agent-2");
196
- events.push("agent-2-started");
197
- assert.equal(manager.getRunning(), 2);
198
- assert.deepEqual(events, ["agent-1-started", "agent-2-started"]);
199
- // Third agent should queue
200
- const acquire3Promise = manager.acquire("agent-3").then((release) => {
201
- events.push("agent-3-started");
202
- return release;
203
- });
204
- assert.equal(manager.getQueueLength(), 1);
205
- // Release first agent
206
- release1();
207
- const release3 = await acquire3Promise;
208
- assert.equal(manager.getRunning(), 2);
209
- assert.deepEqual(events, ["agent-1-started", "agent-2-started", "agent-3-started"]);
210
- release2();
211
- release3();
212
- });
213
- it("should queue agents in order", async () => {
214
- const manager = new ConcurrencyManager(1);
215
- const events = [];
216
- const release1 = await manager.acquire("agent-1");
217
- events.push("1");
218
- const p2 = manager.acquire("agent-2").then(r => { events.push("2"); return r; });
219
- const p3 = manager.acquire("agent-3").then(r => { events.push("3"); return r; });
220
- release1();
221
- const release2 = await p2;
222
- release2();
223
- const release3 = await p3;
224
- assert.deepEqual(events, ["1", "2", "3"]);
225
- release3();
226
- });
227
- });
228
- describe("Custom Agent Loading", () => {
229
- it("should parse agent markdown frontmatter", () => {
230
- const markdown = `---
231
- name: code-checker
232
- description: Code quality checker
233
- tools: read, grep, find, bash
234
- thinking: high
235
- ---
236
- You are a code quality checker. Review code for issues.`;
237
- // Simple frontmatter parser
238
- const match = markdown.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
239
- assert.ok(match);
240
- const frontmatter = match[1];
241
- const body = match[2];
242
- assert.ok(frontmatter.includes("name: code-checker"));
243
- assert.ok(frontmatter.includes("tools: read, grep, find, bash"));
244
- assert.ok(body.includes("You are a code quality checker."));
245
- });
246
- it("should validate required fields", () => {
247
- const validAgent = {
248
- name: "test-agent",
249
- description: "Test agent",
250
- systemPrompt: "Do something.",
251
- promptMode: "replace",
252
- extensions: true,
253
- skills: true,
254
- };
255
- assert.ok(validAgent.name);
256
- assert.ok(validAgent.description);
257
- assert.ok(validAgent.systemPrompt);
258
- });
259
- });
260
- });
261
- //# sourceMappingURL=workflow-integration.test.js.map