claude-flow-novice 2.15.9 → 2.15.11
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/.claude/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +519 -0
- package/.claude/skills/cfn-loop-orchestration/ORCHESTRATOR_IMPLEMENTATION.md +493 -0
- package/.claude/skills/cfn-loop-orchestration/ORCHESTRATOR_QUICK_START.md +499 -0
- package/.claude/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +104 -0
- package/.claude/skills/cfn-loop-orchestration/orchestrate.sh +2 -2
- package/.claude/skills/cfn-loop-orchestration/src/orchestrate.ts +648 -0
- package/.claude/skills/cfn-loop-orchestration/tests/orchestrate.test.ts +836 -0
- package/README.md +205 -10
- package/claude-assets/agents/cfn-dev-team/CLAUDE.md +9 -81
- package/claude-assets/agents/cfn-dev-team/architecture/base-template-generator.md +4 -4
- package/claude-assets/agents/cfn-dev-team/architecture/planner.md +4 -4
- package/claude-assets/agents/cfn-dev-team/architecture/system-architect.md +5 -5
- package/claude-assets/agents/cfn-dev-team/coordinators/cfn-v3-coordinator.md +184 -229
- package/claude-assets/agents/cfn-dev-team/dev-ops/devops-engineer.md +4 -4
- package/claude-assets/agents/cfn-dev-team/dev-ops/docker-specialist.md +9 -37
- package/claude-assets/agents/cfn-dev-team/dev-ops/kubernetes-specialist.md +9 -37
- package/claude-assets/agents/cfn-dev-team/dev-ops/monitoring-specialist.md +4 -4
- package/claude-assets/agents/cfn-dev-team/developers/api-gateway-specialist.md +10 -40
- package/claude-assets/agents/cfn-dev-team/developers/database/database-architect.md +3 -0
- package/claude-assets/agents/cfn-dev-team/developers/frontend/mobile-dev.md +4 -1
- package/claude-assets/agents/cfn-dev-team/developers/frontend/react-frontend-engineer.md +4 -1
- package/claude-assets/agents/cfn-dev-team/developers/frontend/typescript-specialist.md +4 -1
- package/claude-assets/agents/cfn-dev-team/developers/frontend/ui-designer.md +5 -0
- package/claude-assets/agents/cfn-dev-team/developers/graphql-specialist.md +2 -1
- package/claude-assets/agents/cfn-dev-team/developers/rust-developer.md +2 -1
- package/claude-assets/agents/cfn-dev-team/documentation/pseudocode.md +2 -7
- package/claude-assets/agents/cfn-dev-team/product-owners/accessibility-advocate-persona.md +4 -4
- package/claude-assets/agents/cfn-dev-team/product-owners/cto-agent.md +4 -4
- package/claude-assets/agents/cfn-dev-team/product-owners/power-user-persona.md +4 -4
- package/claude-assets/agents/cfn-dev-team/product-owners/product-owner.md +18 -22
- package/claude-assets/agents/cfn-dev-team/reviewers/code-reviewer.md +1 -1
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/code-quality-validator.md +1 -1
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/perf-analyzer.md +1 -1
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/performance-benchmarker.md +1 -1
- package/claude-assets/agents/cfn-dev-team/reviewers/quality/security-specialist.md +1 -1
- package/claude-assets/agents/cfn-dev-team/testers/api-testing-specialist.md +7 -35
- package/claude-assets/agents/cfn-dev-team/testers/chaos-engineering-specialist.md +17 -36
- package/claude-assets/agents/cfn-dev-team/testers/contract-tester.md +10 -11
- package/claude-assets/agents/cfn-dev-team/testers/e2e/playwright-tester.md +5 -5
- package/claude-assets/agents/cfn-dev-team/testers/integration-tester.md +10 -12
- package/claude-assets/agents/cfn-dev-team/testers/interaction-tester.md +7 -36
- package/claude-assets/agents/cfn-dev-team/testers/load-testing-specialist.md +10 -12
- package/claude-assets/agents/cfn-dev-team/testers/mutation-testing-specialist.md +10 -12
- package/claude-assets/agents/cfn-dev-team/testers/playwright-tester.md +7 -37
- package/claude-assets/agents/cfn-dev-team/testers/tester.md +7 -33
- package/claude-assets/agents/cfn-dev-team/testers/unit/tdd-london-unit-swarm.md +5 -5
- package/claude-assets/agents/cfn-dev-team/testers/validation/validation-production-validator.md +4 -4
- package/claude-assets/agents/cfn-dev-team/testing/test-validation-agent.md +4 -4
- package/claude-assets/agents/cfn-dev-team/utility/agent-builder.md +16 -16
- package/claude-assets/agents/cfn-dev-team/utility/analyst.md +4 -4
- package/claude-assets/agents/cfn-dev-team/utility/code-booster.md +4 -4
- package/claude-assets/agents/cfn-dev-team/utility/context-curator.md +4 -4
- package/claude-assets/agents/cfn-dev-team/utility/epic-creator.md +7 -85
- package/claude-assets/agents/cfn-dev-team/utility/memory-leak-specialist.md +7 -93
- package/claude-assets/agents/cfn-dev-team/utility/researcher.md +4 -4
- package/claude-assets/agents/cfn-dev-team/utility/z-ai-specialist.md +7 -84
- package/claude-assets/skills/cfn-loop-orchestration/IMPLEMENTATION_SUMMARY.md +519 -0
- package/claude-assets/skills/cfn-loop-orchestration/ORCHESTRATOR_IMPLEMENTATION.md +493 -0
- package/claude-assets/skills/cfn-loop-orchestration/ORCHESTRATOR_QUICK_START.md +499 -0
- package/claude-assets/skills/cfn-loop-orchestration/helpers/orchestrate-ts.sh +104 -0
- package/claude-assets/skills/cfn-loop-orchestration/orchestrate.sh +2 -2
- package/claude-assets/skills/cfn-loop-orchestration/src/orchestrate.ts +648 -0
- package/claude-assets/skills/cfn-loop-orchestration/tests/orchestrate.test.ts +836 -0
- package/dist/cli/agent-definition-parser.js +37 -4
- package/dist/cli/agent-definition-parser.js.map +1 -1
- package/dist/cli/agent-executor.js +32 -2
- package/dist/cli/agent-executor.js.map +1 -1
- package/dist/coordination/coordinate.js +369 -0
- package/dist/coordination/coordinate.js.map +1 -0
- package/dist/coordination/spawn-agent.js +364 -0
- package/dist/coordination/spawn-agent.js.map +1 -0
- package/dist/coordination/types-export.js +38 -0
- package/dist/coordination/types-export.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coordination Layer for Agent Communication and Tracking
|
|
3
|
+
*
|
|
4
|
+
* Provides agent registration, status tracking, message passing, and
|
|
5
|
+
* coordination protocol enforcement with Redis as the backing store.
|
|
6
|
+
*
|
|
7
|
+
* Supports both Task Mode (Redis stubbed) and CLI Mode (full coordination).
|
|
8
|
+
*
|
|
9
|
+
* Key Features:
|
|
10
|
+
* - Agent registration with metadata tracking
|
|
11
|
+
* - Status updates and health checks
|
|
12
|
+
* - Message passing between agents (pub/sub)
|
|
13
|
+
* - Broadcast messages with protocol enforcement
|
|
14
|
+
* - Wait for completion with timeout support
|
|
15
|
+
* - Type-safe interfaces using branded types
|
|
16
|
+
*/ import { CoordinationError, CoordinationErrorType } from './types-export';
|
|
17
|
+
/**
|
|
18
|
+
* Coordination Layer Implementation
|
|
19
|
+
*
|
|
20
|
+
* Manages agent lifecycle, status tracking, and inter-agent communication.
|
|
21
|
+
* Gracefully handles Task Mode (no Redis) and CLI Mode (full coordination).
|
|
22
|
+
*/ export class CoordinationLayer {
|
|
23
|
+
redis;
|
|
24
|
+
logger;
|
|
25
|
+
canUseRedis;
|
|
26
|
+
executionMode;
|
|
27
|
+
taskId;
|
|
28
|
+
// In-memory registry for Task Mode
|
|
29
|
+
taskModeRegistry = new Map();
|
|
30
|
+
taskModeMessages = new Map();
|
|
31
|
+
constructor(config){
|
|
32
|
+
this.redis = config.redis;
|
|
33
|
+
this.logger = config.logger;
|
|
34
|
+
this.canUseRedis = config.canUseRedis;
|
|
35
|
+
this.executionMode = config.executionMode;
|
|
36
|
+
this.taskId = config.taskId;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Register an agent with the coordination layer
|
|
40
|
+
*
|
|
41
|
+
* Creates tracking record for agent lifecycle.
|
|
42
|
+
*/ async registerAgent(agentId, type, metadata, iteration = 1, pid) {
|
|
43
|
+
const now = new Date().toISOString();
|
|
44
|
+
const agentMetadata = {
|
|
45
|
+
agentId,
|
|
46
|
+
type,
|
|
47
|
+
taskId: this.taskId,
|
|
48
|
+
status: 'registered',
|
|
49
|
+
iteration,
|
|
50
|
+
createdAt: now,
|
|
51
|
+
lastHeartbeat: now,
|
|
52
|
+
pid,
|
|
53
|
+
metadata
|
|
54
|
+
};
|
|
55
|
+
if (!this.canUseRedis) {
|
|
56
|
+
// Task Mode: Store in memory
|
|
57
|
+
this.taskModeRegistry.set(agentId, agentMetadata);
|
|
58
|
+
this.logger.info(`Task Mode: Registered agent ${agentId} (type: ${type})`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// CLI Mode: Store in Redis
|
|
62
|
+
const key = `swarm:${this.taskId}:agents:${agentId}`;
|
|
63
|
+
try {
|
|
64
|
+
await this.redis.hset(key, 'type', type, 'status', 'registered', 'iteration', iteration.toString(), 'createdAt', now, 'lastHeartbeat', now, ...pid ? [
|
|
65
|
+
'pid',
|
|
66
|
+
pid.toString()
|
|
67
|
+
] : [], ...metadata ? [
|
|
68
|
+
'metadata',
|
|
69
|
+
JSON.stringify(metadata)
|
|
70
|
+
] : []);
|
|
71
|
+
// Set 24h TTL
|
|
72
|
+
await this.redis.expire(key, 86400);
|
|
73
|
+
this.logger.debug(`Registered agent ${agentId} in Redis`);
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw new CoordinationError(CoordinationErrorType.REDIS_ERROR, `Failed to register agent ${agentId}`, this.executionMode, true);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Update agent status
|
|
80
|
+
*
|
|
81
|
+
* Tracks status changes during agent lifecycle.
|
|
82
|
+
*/ async updateAgentStatus(agentId, status) {
|
|
83
|
+
const now = new Date().toISOString();
|
|
84
|
+
if (!this.canUseRedis) {
|
|
85
|
+
// Task Mode: Update in memory
|
|
86
|
+
const agent = this.taskModeRegistry.get(agentId);
|
|
87
|
+
if (agent) {
|
|
88
|
+
agent.status = status;
|
|
89
|
+
agent.lastHeartbeat = now;
|
|
90
|
+
this.taskModeRegistry.set(agentId, agent);
|
|
91
|
+
}
|
|
92
|
+
this.logger.debug(`Task Mode: Updated ${agentId} status to ${status}`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// CLI Mode: Update in Redis
|
|
96
|
+
const key = `swarm:${this.taskId}:agents:${agentId}`;
|
|
97
|
+
try {
|
|
98
|
+
await this.redis.hset(key, 'status', status, 'lastHeartbeat', now);
|
|
99
|
+
this.logger.debug(`Updated agent ${agentId} status to ${status}`);
|
|
100
|
+
} catch (error) {
|
|
101
|
+
throw new CoordinationError(CoordinationErrorType.REDIS_ERROR, `Failed to update agent ${agentId} status`, this.executionMode, true);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Send direct message between agents
|
|
106
|
+
*
|
|
107
|
+
* Delivers message from one agent to another.
|
|
108
|
+
*/ async sendMessage(from, to, messageType, payload, correlationId) {
|
|
109
|
+
const message = {
|
|
110
|
+
from,
|
|
111
|
+
to,
|
|
112
|
+
type: messageType,
|
|
113
|
+
payload,
|
|
114
|
+
timestamp: new Date().toISOString(),
|
|
115
|
+
correlationId
|
|
116
|
+
};
|
|
117
|
+
if (!this.canUseRedis) {
|
|
118
|
+
// Task Mode: Store in memory
|
|
119
|
+
const key = `${from}:${to}`;
|
|
120
|
+
if (!this.taskModeMessages.has(key)) {
|
|
121
|
+
this.taskModeMessages.set(key, []);
|
|
122
|
+
}
|
|
123
|
+
this.taskModeMessages.get(key).push(message);
|
|
124
|
+
this.logger.debug(`Task Mode: Message from ${from} to ${to}`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
// CLI Mode: Store in Redis
|
|
128
|
+
const key = `swarm:${this.taskId}:messages:${from}:${to}`;
|
|
129
|
+
try {
|
|
130
|
+
await this.redis.lpush(key, JSON.stringify(message));
|
|
131
|
+
await this.redis.expire(key, 3600); // 1h TTL for messages
|
|
132
|
+
this.logger.debug(`Message sent from ${from} to ${to}`);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
throw new CoordinationError(CoordinationErrorType.REDIS_ERROR, `Failed to send message from ${from} to ${to}`, this.executionMode, true);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Broadcast message to all agents in task
|
|
139
|
+
*
|
|
140
|
+
* Delivers message to every agent participating in the task.
|
|
141
|
+
* Used for coordination signals (gate passed, iteration started, etc).
|
|
142
|
+
*/ async broadcastMessage(from, messageType, payload, correlationId) {
|
|
143
|
+
const message = {
|
|
144
|
+
from,
|
|
145
|
+
taskId: this.taskId,
|
|
146
|
+
type: messageType,
|
|
147
|
+
payload,
|
|
148
|
+
timestamp: new Date().toISOString(),
|
|
149
|
+
correlationId
|
|
150
|
+
};
|
|
151
|
+
if (!this.canUseRedis) {
|
|
152
|
+
// Task Mode: Log broadcast
|
|
153
|
+
this.logger.info(`Task Mode: Broadcast ${messageType} from ${from} (payload: ${JSON.stringify(payload)})`);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// CLI Mode: Store in Redis as sorted set for ordering
|
|
157
|
+
const key = `swarm:${this.taskId}:broadcasts`;
|
|
158
|
+
try {
|
|
159
|
+
const timestamp = Date.now();
|
|
160
|
+
await this.redis.zadd(key, timestamp, JSON.stringify(message));
|
|
161
|
+
// Set 1h TTL
|
|
162
|
+
await this.redis.expire(key, 3600);
|
|
163
|
+
this.logger.debug(`Broadcast ${messageType} to all agents in task ${this.taskId}`);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
throw new CoordinationError(CoordinationErrorType.REDIS_ERROR, `Failed to broadcast message`, this.executionMode, true);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Wait for agent completion
|
|
170
|
+
*
|
|
171
|
+
* Blocking wait for one or more agents to complete with timeout.
|
|
172
|
+
* Used by orchestrator to synchronize on agent completion.
|
|
173
|
+
*/ async waitForCompletion(agentIds, timeoutMs = 600000 // 10 minutes default
|
|
174
|
+
) {
|
|
175
|
+
const results = new Map();
|
|
176
|
+
const startTime = Date.now();
|
|
177
|
+
if (!this.canUseRedis) {
|
|
178
|
+
// Task Mode: Poll in-memory registry
|
|
179
|
+
while(Date.now() - startTime < timeoutMs){
|
|
180
|
+
let allComplete = true;
|
|
181
|
+
for (const agentId of agentIds){
|
|
182
|
+
const agent = this.taskModeRegistry.get(agentId);
|
|
183
|
+
const status = agent?.status ?? 'unknown';
|
|
184
|
+
if (status === 'complete' || status === 'failed') {
|
|
185
|
+
results.set(agentId, status);
|
|
186
|
+
} else {
|
|
187
|
+
allComplete = false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (allComplete && results.size === agentIds.length) {
|
|
191
|
+
return results;
|
|
192
|
+
}
|
|
193
|
+
// Wait 100ms before checking again
|
|
194
|
+
await new Promise((resolve)=>setTimeout(resolve, 100));
|
|
195
|
+
}
|
|
196
|
+
// Timeout: Return whatever we have
|
|
197
|
+
for (const agentId of agentIds){
|
|
198
|
+
if (!results.has(agentId)) {
|
|
199
|
+
const agent = this.taskModeRegistry.get(agentId);
|
|
200
|
+
results.set(agentId, agent?.status ?? 'timeout');
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return results;
|
|
204
|
+
}
|
|
205
|
+
// CLI Mode: Wait on Redis with blocking operations
|
|
206
|
+
const channel = `swarm:${this.taskId}:completion`;
|
|
207
|
+
try {
|
|
208
|
+
while(Date.now() - startTime < timeoutMs){
|
|
209
|
+
for (const agentId of agentIds){
|
|
210
|
+
if (results.has(agentId)) continue;
|
|
211
|
+
const key = `swarm:${this.taskId}:agents:${agentId}`;
|
|
212
|
+
const statusStr = await this.redis.hget(key, 'status');
|
|
213
|
+
const status = statusStr ?? 'unknown';
|
|
214
|
+
if (status === 'complete' || status === 'failed' || status === 'timeout') {
|
|
215
|
+
results.set(agentId, status);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (results.size === agentIds.length) {
|
|
219
|
+
return results;
|
|
220
|
+
}
|
|
221
|
+
// Wait 100ms before checking again
|
|
222
|
+
await new Promise((resolve)=>setTimeout(resolve, 100));
|
|
223
|
+
}
|
|
224
|
+
// Timeout: Mark remaining as timeout
|
|
225
|
+
for (const agentId of agentIds){
|
|
226
|
+
if (!results.has(agentId)) {
|
|
227
|
+
results.set(agentId, 'timeout');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return results;
|
|
231
|
+
} catch (error) {
|
|
232
|
+
throw new CoordinationError(CoordinationErrorType.REDIS_ERROR, `Failed to wait for agent completion`, this.executionMode, true);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Get agent status
|
|
237
|
+
*
|
|
238
|
+
* Returns current status for a single agent.
|
|
239
|
+
*/ async getAgentStatus(agentId) {
|
|
240
|
+
if (!this.canUseRedis) {
|
|
241
|
+
// Task Mode: Check in-memory registry
|
|
242
|
+
const agent = this.taskModeRegistry.get(agentId);
|
|
243
|
+
return agent?.status ?? 'unknown';
|
|
244
|
+
}
|
|
245
|
+
// CLI Mode: Get from Redis
|
|
246
|
+
const key = `swarm:${this.taskId}:agents:${agentId}`;
|
|
247
|
+
try {
|
|
248
|
+
const status = await this.redis.hget(key, 'status');
|
|
249
|
+
return status ?? 'unknown';
|
|
250
|
+
} catch (error) {
|
|
251
|
+
this.logger.error(`Failed to get agent ${agentId} status`, error);
|
|
252
|
+
return 'unknown';
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Get agent metadata
|
|
257
|
+
*
|
|
258
|
+
* Returns full metadata for a registered agent.
|
|
259
|
+
*/ async getAgentMetadata(agentId) {
|
|
260
|
+
if (!this.canUseRedis) {
|
|
261
|
+
// Task Mode: Return from in-memory registry
|
|
262
|
+
return this.taskModeRegistry.get(agentId) ?? null;
|
|
263
|
+
}
|
|
264
|
+
// CLI Mode: Get from Redis
|
|
265
|
+
const key = `swarm:${this.taskId}:agents:${agentId}`;
|
|
266
|
+
try {
|
|
267
|
+
const data = await this.redis.hgetall(key);
|
|
268
|
+
if (!data || Object.keys(data).length === 0) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
agentId,
|
|
273
|
+
type: data.type,
|
|
274
|
+
taskId: this.taskId,
|
|
275
|
+
status: data.status || 'unknown',
|
|
276
|
+
iteration: parseInt(data.iteration || '1', 10),
|
|
277
|
+
createdAt: data.createdAt,
|
|
278
|
+
lastHeartbeat: data.lastHeartbeat,
|
|
279
|
+
pid: data.pid ? parseInt(data.pid, 10) : undefined,
|
|
280
|
+
metadata: data.metadata ? JSON.parse(data.metadata) : undefined
|
|
281
|
+
};
|
|
282
|
+
} catch (error) {
|
|
283
|
+
this.logger.error(`Failed to get agent ${agentId} metadata`, error);
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Get all agents for a task
|
|
289
|
+
*
|
|
290
|
+
* Returns metadata for all registered agents.
|
|
291
|
+
*/ async getAllAgents() {
|
|
292
|
+
if (!this.canUseRedis) {
|
|
293
|
+
// Task Mode: Return from in-memory registry
|
|
294
|
+
return Array.from(this.taskModeRegistry.values());
|
|
295
|
+
}
|
|
296
|
+
// CLI Mode: Scan Redis for all agents
|
|
297
|
+
const agents = [];
|
|
298
|
+
const pattern = `swarm:${this.taskId}:agents:*`;
|
|
299
|
+
try {
|
|
300
|
+
let cursor = '0';
|
|
301
|
+
do {
|
|
302
|
+
const [newCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern);
|
|
303
|
+
cursor = newCursor;
|
|
304
|
+
for (const key of keys){
|
|
305
|
+
const agentIdMatch = key.match(/agents:(.+)$/);
|
|
306
|
+
if (agentIdMatch) {
|
|
307
|
+
const agentId = agentIdMatch[1];
|
|
308
|
+
const metadata = await this.getAgentMetadata(agentId);
|
|
309
|
+
if (metadata) {
|
|
310
|
+
agents.push(metadata);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}while (cursor !== '0')
|
|
315
|
+
return agents;
|
|
316
|
+
} catch (error) {
|
|
317
|
+
this.logger.error('Failed to get all agents', error);
|
|
318
|
+
return [];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Health check for agents
|
|
323
|
+
*
|
|
324
|
+
* Returns stale agents that haven't updated status recently.
|
|
325
|
+
*/ async getStaleAgents(staleThresholdMs = 600000) {
|
|
326
|
+
const agents = await this.getAllAgents();
|
|
327
|
+
const now = Date.now();
|
|
328
|
+
const stale = [];
|
|
329
|
+
for (const agent of agents){
|
|
330
|
+
const lastHeartbeat = new Date(agent.lastHeartbeat).getTime();
|
|
331
|
+
if (now - lastHeartbeat > staleThresholdMs) {
|
|
332
|
+
stale.push(agent);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return stale;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Clean up task coordination data
|
|
339
|
+
*
|
|
340
|
+
* Removes all Redis keys associated with a task.
|
|
341
|
+
* Should only be called after task completion.
|
|
342
|
+
*/ async cleanupTask() {
|
|
343
|
+
if (!this.canUseRedis) {
|
|
344
|
+
// Task Mode: Clear in-memory structures
|
|
345
|
+
this.taskModeRegistry.clear();
|
|
346
|
+
this.taskModeMessages.clear();
|
|
347
|
+
this.logger.info(`Task Mode: Cleaned up in-memory coordination data`);
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
// CLI Mode: Remove Redis keys
|
|
351
|
+
const pattern = `swarm:${this.taskId}:*`;
|
|
352
|
+
try {
|
|
353
|
+
let cursor = '0';
|
|
354
|
+
const batch = 100;
|
|
355
|
+
do {
|
|
356
|
+
const [newCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', batch.toString());
|
|
357
|
+
cursor = newCursor;
|
|
358
|
+
if (keys.length > 0) {
|
|
359
|
+
await this.redis.del(...keys);
|
|
360
|
+
}
|
|
361
|
+
}while (cursor !== '0')
|
|
362
|
+
this.logger.info(`Cleaned up coordination data for task ${this.taskId}`);
|
|
363
|
+
} catch (error) {
|
|
364
|
+
this.logger.error('Failed to cleanup task coordination data', error);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
//# sourceMappingURL=coordinate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/coordination/coordinate.ts"],"sourcesContent":["/**\r\n * Coordination Layer for Agent Communication and Tracking\r\n *\r\n * Provides agent registration, status tracking, message passing, and\r\n * coordination protocol enforcement with Redis as the backing store.\r\n *\r\n * Supports both Task Mode (Redis stubbed) and CLI Mode (full coordination).\r\n *\r\n * Key Features:\r\n * - Agent registration with metadata tracking\r\n * - Status updates and health checks\r\n * - Message passing between agents (pub/sub)\r\n * - Broadcast messages with protocol enforcement\r\n * - Wait for completion with timeout support\r\n * - Type-safe interfaces using branded types\r\n */\r\n\r\nimport type Redis from 'ioredis';\r\n// Types from Redis coordination skill\r\nimport type {\r\n TaskId,\r\n AgentId,\r\n CorrelationId,\r\n ExecutionMode,\r\n Logger,\r\n} from './types-export';\r\nimport {\r\n CoordinationError,\r\n CoordinationErrorType,\r\n} from './types-export';\r\n\r\n/**\r\n * Agent Registration Metadata\r\n *\r\n * Tracks agent information for coordination and monitoring.\r\n */\r\nexport interface AgentMetadata {\r\n agentId: AgentId;\r\n type: string;\r\n taskId: TaskId;\r\n status: AgentStatus;\r\n iteration: number;\r\n createdAt: string;\r\n lastHeartbeat: string;\r\n pid?: number;\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Agent Status\r\n *\r\n * Lifecycle states for agents during execution.\r\n */\r\nexport type AgentStatus =\r\n | 'registered' // Just registered, not yet started\r\n | 'initializing' // Starting up, loading context\r\n | 'working' // Active work in progress\r\n | 'blocked' // Waiting for dependencies\r\n | 'complete' // Work complete\r\n | 'failed' // Execution failed\r\n | 'timeout' // Exceeded timeout\r\n | 'unknown'; // Status unknown\r\n\r\n/**\r\n * Coordination Message\r\n *\r\n * Structured message for agent-to-agent communication.\r\n */\r\nexport interface CoordinationMessage {\r\n from: AgentId;\r\n to: AgentId | 'broadcast';\r\n type: string;\r\n payload: Record<string, unknown>;\r\n timestamp: string;\r\n correlationId: CorrelationId;\r\n}\r\n\r\n/**\r\n * Broadcast Message\r\n *\r\n * Message delivered to all agents in a task.\r\n */\r\nexport interface BroadcastMessage {\r\n from: AgentId | 'orchestrator';\r\n taskId: TaskId;\r\n type: string;\r\n payload: Record<string, unknown>;\r\n timestamp: string;\r\n correlationId: CorrelationId;\r\n}\r\n\r\n/**\r\n * Coordination Layer Configuration\r\n */\r\nexport interface CoordinationConfig {\r\n redis: Redis;\r\n logger: Logger;\r\n canUseRedis: boolean;\r\n executionMode: ExecutionMode;\r\n taskId: TaskId;\r\n}\r\n\r\n/**\r\n * Coordination Layer Implementation\r\n *\r\n * Manages agent lifecycle, status tracking, and inter-agent communication.\r\n * Gracefully handles Task Mode (no Redis) and CLI Mode (full coordination).\r\n */\r\nexport class CoordinationLayer {\r\n private redis: Redis | null;\r\n private logger: Logger;\r\n private canUseRedis: boolean;\r\n private executionMode: ExecutionMode;\r\n private taskId: TaskId;\r\n\r\n // In-memory registry for Task Mode\r\n private taskModeRegistry: Map<AgentId, AgentMetadata> = new Map();\r\n private taskModeMessages: Map<string, CoordinationMessage[]> = new Map();\r\n\r\n constructor(config: CoordinationConfig) {\r\n this.redis = config.redis;\r\n this.logger = config.logger;\r\n this.canUseRedis = config.canUseRedis;\r\n this.executionMode = config.executionMode;\r\n this.taskId = config.taskId;\r\n }\r\n\r\n /**\r\n * Register an agent with the coordination layer\r\n *\r\n * Creates tracking record for agent lifecycle.\r\n */\r\n async registerAgent(\r\n agentId: AgentId,\r\n type: string,\r\n metadata?: Record<string, unknown>,\r\n iteration: number = 1,\r\n pid?: number\r\n ): Promise<void> {\r\n const now = new Date().toISOString();\r\n const agentMetadata: AgentMetadata = {\r\n agentId,\r\n type,\r\n taskId: this.taskId,\r\n status: 'registered',\r\n iteration,\r\n createdAt: now,\r\n lastHeartbeat: now,\r\n pid,\r\n metadata,\r\n };\r\n\r\n if (!this.canUseRedis) {\r\n // Task Mode: Store in memory\r\n this.taskModeRegistry.set(agentId, agentMetadata);\r\n this.logger.info(`Task Mode: Registered agent ${agentId} (type: ${type})`);\r\n return;\r\n }\r\n\r\n // CLI Mode: Store in Redis\r\n const key = `swarm:${this.taskId}:agents:${agentId}`;\r\n try {\r\n await this.redis!.hset(\r\n key,\r\n 'type', type,\r\n 'status', 'registered',\r\n 'iteration', iteration.toString(),\r\n 'createdAt', now,\r\n 'lastHeartbeat', now,\r\n ...(pid ? ['pid', pid.toString()] : []),\r\n ...(metadata ? ['metadata', JSON.stringify(metadata)] : [])\r\n );\r\n\r\n // Set 24h TTL\r\n await this.redis!.expire(key, 86400);\r\n\r\n this.logger.debug(`Registered agent ${agentId} in Redis`);\r\n } catch (error) {\r\n throw new CoordinationError(\r\n CoordinationErrorType.REDIS_ERROR,\r\n `Failed to register agent ${agentId}`,\r\n this.executionMode,\r\n true\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Update agent status\r\n *\r\n * Tracks status changes during agent lifecycle.\r\n */\r\n async updateAgentStatus(agentId: AgentId, status: AgentStatus): Promise<void> {\r\n const now = new Date().toISOString();\r\n\r\n if (!this.canUseRedis) {\r\n // Task Mode: Update in memory\r\n const agent = this.taskModeRegistry.get(agentId);\r\n if (agent) {\r\n agent.status = status;\r\n agent.lastHeartbeat = now;\r\n this.taskModeRegistry.set(agentId, agent);\r\n }\r\n this.logger.debug(`Task Mode: Updated ${agentId} status to ${status}`);\r\n return;\r\n }\r\n\r\n // CLI Mode: Update in Redis\r\n const key = `swarm:${this.taskId}:agents:${agentId}`;\r\n try {\r\n await this.redis!.hset(\r\n key,\r\n 'status', status,\r\n 'lastHeartbeat', now\r\n );\r\n this.logger.debug(`Updated agent ${agentId} status to ${status}`);\r\n } catch (error) {\r\n throw new CoordinationError(\r\n CoordinationErrorType.REDIS_ERROR,\r\n `Failed to update agent ${agentId} status`,\r\n this.executionMode,\r\n true\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Send direct message between agents\r\n *\r\n * Delivers message from one agent to another.\r\n */\r\n async sendMessage(\r\n from: AgentId,\r\n to: AgentId,\r\n messageType: string,\r\n payload: Record<string, unknown>,\r\n correlationId: CorrelationId\r\n ): Promise<void> {\r\n const message: CoordinationMessage = {\r\n from,\r\n to,\r\n type: messageType,\r\n payload,\r\n timestamp: new Date().toISOString(),\r\n correlationId,\r\n };\r\n\r\n if (!this.canUseRedis) {\r\n // Task Mode: Store in memory\r\n const key = `${from}:${to}`;\r\n if (!this.taskModeMessages.has(key)) {\r\n this.taskModeMessages.set(key, []);\r\n }\r\n this.taskModeMessages.get(key)!.push(message);\r\n this.logger.debug(`Task Mode: Message from ${from} to ${to}`);\r\n return;\r\n }\r\n\r\n // CLI Mode: Store in Redis\r\n const key = `swarm:${this.taskId}:messages:${from}:${to}`;\r\n try {\r\n await this.redis!.lpush(key, JSON.stringify(message));\r\n await this.redis!.expire(key, 3600); // 1h TTL for messages\r\n this.logger.debug(`Message sent from ${from} to ${to}`);\r\n } catch (error) {\r\n throw new CoordinationError(\r\n CoordinationErrorType.REDIS_ERROR,\r\n `Failed to send message from ${from} to ${to}`,\r\n this.executionMode,\r\n true\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Broadcast message to all agents in task\r\n *\r\n * Delivers message to every agent participating in the task.\r\n * Used for coordination signals (gate passed, iteration started, etc).\r\n */\r\n async broadcastMessage(\r\n from: AgentId | 'orchestrator',\r\n messageType: string,\r\n payload: Record<string, unknown>,\r\n correlationId: CorrelationId\r\n ): Promise<void> {\r\n const message: BroadcastMessage = {\r\n from,\r\n taskId: this.taskId,\r\n type: messageType,\r\n payload,\r\n timestamp: new Date().toISOString(),\r\n correlationId,\r\n };\r\n\r\n if (!this.canUseRedis) {\r\n // Task Mode: Log broadcast\r\n this.logger.info(\r\n `Task Mode: Broadcast ${messageType} from ${from} (payload: ${JSON.stringify(payload)})`\r\n );\r\n return;\r\n }\r\n\r\n // CLI Mode: Store in Redis as sorted set for ordering\r\n const key = `swarm:${this.taskId}:broadcasts`;\r\n try {\r\n const timestamp = Date.now();\r\n await this.redis!.zadd(\r\n key,\r\n timestamp,\r\n JSON.stringify(message)\r\n );\r\n\r\n // Set 1h TTL\r\n await this.redis!.expire(key, 3600);\r\n\r\n this.logger.debug(`Broadcast ${messageType} to all agents in task ${this.taskId}`);\r\n } catch (error) {\r\n throw new CoordinationError(\r\n CoordinationErrorType.REDIS_ERROR,\r\n `Failed to broadcast message`,\r\n this.executionMode,\r\n true\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Wait for agent completion\r\n *\r\n * Blocking wait for one or more agents to complete with timeout.\r\n * Used by orchestrator to synchronize on agent completion.\r\n */\r\n async waitForCompletion(\r\n agentIds: AgentId[],\r\n timeoutMs: number = 600000 // 10 minutes default\r\n ): Promise<Map<AgentId, AgentStatus>> {\r\n const results = new Map<AgentId, AgentStatus>();\r\n const startTime = Date.now();\r\n\r\n if (!this.canUseRedis) {\r\n // Task Mode: Poll in-memory registry\r\n while (Date.now() - startTime < timeoutMs) {\r\n let allComplete = true;\r\n\r\n for (const agentId of agentIds) {\r\n const agent = this.taskModeRegistry.get(agentId);\r\n const status = agent?.status ?? 'unknown';\r\n\r\n if (status === 'complete' || status === 'failed') {\r\n results.set(agentId, status);\r\n } else {\r\n allComplete = false;\r\n }\r\n }\r\n\r\n if (allComplete && results.size === agentIds.length) {\r\n return results;\r\n }\r\n\r\n // Wait 100ms before checking again\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n }\r\n\r\n // Timeout: Return whatever we have\r\n for (const agentId of agentIds) {\r\n if (!results.has(agentId)) {\r\n const agent = this.taskModeRegistry.get(agentId);\r\n results.set(agentId, agent?.status ?? 'timeout');\r\n }\r\n }\r\n return results;\r\n }\r\n\r\n // CLI Mode: Wait on Redis with blocking operations\r\n const channel = `swarm:${this.taskId}:completion`;\r\n\r\n try {\r\n while (Date.now() - startTime < timeoutMs) {\r\n for (const agentId of agentIds) {\r\n if (results.has(agentId)) continue;\r\n\r\n const key = `swarm:${this.taskId}:agents:${agentId}`;\r\n const statusStr = await this.redis!.hget(key, 'status');\r\n const status = (statusStr as AgentStatus) ?? 'unknown';\r\n\r\n if (status === 'complete' || status === 'failed' || status === 'timeout') {\r\n results.set(agentId, status);\r\n }\r\n }\r\n\r\n if (results.size === agentIds.length) {\r\n return results;\r\n }\r\n\r\n // Wait 100ms before checking again\r\n await new Promise(resolve => setTimeout(resolve, 100));\r\n }\r\n\r\n // Timeout: Mark remaining as timeout\r\n for (const agentId of agentIds) {\r\n if (!results.has(agentId)) {\r\n results.set(agentId, 'timeout');\r\n }\r\n }\r\n\r\n return results;\r\n } catch (error) {\r\n throw new CoordinationError(\r\n CoordinationErrorType.REDIS_ERROR,\r\n `Failed to wait for agent completion`,\r\n this.executionMode,\r\n true\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Get agent status\r\n *\r\n * Returns current status for a single agent.\r\n */\r\n async getAgentStatus(agentId: AgentId): Promise<AgentStatus> {\r\n if (!this.canUseRedis) {\r\n // Task Mode: Check in-memory registry\r\n const agent = this.taskModeRegistry.get(agentId);\r\n return agent?.status ?? 'unknown';\r\n }\r\n\r\n // CLI Mode: Get from Redis\r\n const key = `swarm:${this.taskId}:agents:${agentId}`;\r\n try {\r\n const status = await this.redis!.hget(key, 'status');\r\n return (status as AgentStatus) ?? 'unknown';\r\n } catch (error) {\r\n this.logger.error(`Failed to get agent ${agentId} status`, error);\r\n return 'unknown';\r\n }\r\n }\r\n\r\n /**\r\n * Get agent metadata\r\n *\r\n * Returns full metadata for a registered agent.\r\n */\r\n async getAgentMetadata(agentId: AgentId): Promise<AgentMetadata | null> {\r\n if (!this.canUseRedis) {\r\n // Task Mode: Return from in-memory registry\r\n return this.taskModeRegistry.get(agentId) ?? null;\r\n }\r\n\r\n // CLI Mode: Get from Redis\r\n const key = `swarm:${this.taskId}:agents:${agentId}`;\r\n try {\r\n const data = await this.redis!.hgetall(key);\r\n\r\n if (!data || Object.keys(data).length === 0) {\r\n return null;\r\n }\r\n\r\n return {\r\n agentId,\r\n type: data.type,\r\n taskId: this.taskId,\r\n status: (data.status as AgentStatus) || 'unknown',\r\n iteration: parseInt(data.iteration || '1', 10),\r\n createdAt: data.createdAt,\r\n lastHeartbeat: data.lastHeartbeat,\r\n pid: data.pid ? parseInt(data.pid, 10) : undefined,\r\n metadata: data.metadata ? JSON.parse(data.metadata) : undefined,\r\n };\r\n } catch (error) {\r\n this.logger.error(`Failed to get agent ${agentId} metadata`, error);\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Get all agents for a task\r\n *\r\n * Returns metadata for all registered agents.\r\n */\r\n async getAllAgents(): Promise<AgentMetadata[]> {\r\n if (!this.canUseRedis) {\r\n // Task Mode: Return from in-memory registry\r\n return Array.from(this.taskModeRegistry.values());\r\n }\r\n\r\n // CLI Mode: Scan Redis for all agents\r\n const agents: AgentMetadata[] = [];\r\n const pattern = `swarm:${this.taskId}:agents:*`;\r\n\r\n try {\r\n let cursor = '0';\r\n do {\r\n const [newCursor, keys] = await this.redis!.scan(cursor, 'MATCH', pattern);\r\n cursor = newCursor;\r\n\r\n for (const key of keys) {\r\n const agentIdMatch = key.match(/agents:(.+)$/);\r\n if (agentIdMatch) {\r\n const agentId = agentIdMatch[1] as AgentId;\r\n const metadata = await this.getAgentMetadata(agentId);\r\n if (metadata) {\r\n agents.push(metadata);\r\n }\r\n }\r\n }\r\n } while (cursor !== '0');\r\n\r\n return agents;\r\n } catch (error) {\r\n this.logger.error('Failed to get all agents', error);\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Health check for agents\r\n *\r\n * Returns stale agents that haven't updated status recently.\r\n */\r\n async getStaleAgents(staleThresholdMs: number = 600000): Promise<AgentMetadata[]> {\r\n const agents = await this.getAllAgents();\r\n const now = Date.now();\r\n const stale: AgentMetadata[] = [];\r\n\r\n for (const agent of agents) {\r\n const lastHeartbeat = new Date(agent.lastHeartbeat).getTime();\r\n if (now - lastHeartbeat > staleThresholdMs) {\r\n stale.push(agent);\r\n }\r\n }\r\n\r\n return stale;\r\n }\r\n\r\n /**\r\n * Clean up task coordination data\r\n *\r\n * Removes all Redis keys associated with a task.\r\n * Should only be called after task completion.\r\n */\r\n async cleanupTask(): Promise<void> {\r\n if (!this.canUseRedis) {\r\n // Task Mode: Clear in-memory structures\r\n this.taskModeRegistry.clear();\r\n this.taskModeMessages.clear();\r\n this.logger.info(`Task Mode: Cleaned up in-memory coordination data`);\r\n return;\r\n }\r\n\r\n // CLI Mode: Remove Redis keys\r\n const pattern = `swarm:${this.taskId}:*`;\r\n try {\r\n let cursor = '0';\r\n const batch = 100;\r\n\r\n do {\r\n const [newCursor, keys] = await this.redis!.scan(cursor, 'MATCH', pattern, 'COUNT', batch.toString());\r\n cursor = newCursor;\r\n\r\n if (keys.length > 0) {\r\n await this.redis!.del(...keys);\r\n }\r\n } while (cursor !== '0');\r\n\r\n this.logger.info(`Cleaned up coordination data for task ${this.taskId}`);\r\n } catch (error) {\r\n this.logger.error('Failed to cleanup task coordination data', error);\r\n }\r\n }\r\n}\r\n"],"names":["CoordinationError","CoordinationErrorType","CoordinationLayer","redis","logger","canUseRedis","executionMode","taskId","taskModeRegistry","Map","taskModeMessages","config","registerAgent","agentId","type","metadata","iteration","pid","now","Date","toISOString","agentMetadata","status","createdAt","lastHeartbeat","set","info","key","hset","toString","JSON","stringify","expire","debug","error","REDIS_ERROR","updateAgentStatus","agent","get","sendMessage","from","to","messageType","payload","correlationId","message","timestamp","has","push","lpush","broadcastMessage","zadd","waitForCompletion","agentIds","timeoutMs","results","startTime","allComplete","size","length","Promise","resolve","setTimeout","channel","statusStr","hget","getAgentStatus","getAgentMetadata","data","hgetall","Object","keys","parseInt","undefined","parse","getAllAgents","Array","values","agents","pattern","cursor","newCursor","scan","agentIdMatch","match","getStaleAgents","staleThresholdMs","stale","getTime","cleanupTask","clear","batch","del"],"mappings":"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"}
|