pmp-gywd 3.4.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/agents/agent-orchestrator.js +449 -0
- package/lib/agents/agent-types.js +557 -0
- package/lib/agents/base-agent.js +310 -0
- package/lib/agents/index.js +40 -0
- package/lib/analytics/index.js +28 -0
- package/lib/analytics/model-generator.js +387 -0
- package/lib/analytics/review-agent.js +468 -0
- package/lib/analytics/test-generator.js +431 -0
- package/lib/dashboard/dashboard-renderer.js +452 -0
- package/lib/dashboard/index.js +15 -0
- package/lib/grilling/change-validator.js +565 -0
- package/lib/grilling/decision-griller.js +535 -0
- package/lib/grilling/index.js +28 -0
- package/lib/grilling/plan-challenger.js +526 -0
- package/lib/multi-agent/cloud-sync.js +405 -0
- package/lib/multi-agent/coordinator.js +401 -0
- package/lib/multi-agent/index.js +34 -0
- package/lib/multi-agent/message-queue.js +377 -0
- package/lib/multi-agent/team-sync.js +420 -0
- package/lib/permissions/index.js +26 -0
- package/lib/permissions/operation-classifier.js +450 -0
- package/lib/permissions/permission-router.js +349 -0
- package/lib/permissions/risk-scorer.js +340 -0
- package/lib/plugins/index.js +22 -0
- package/lib/plugins/marketplace.js +490 -0
- package/lib/plugins/plugin-loader.js +437 -0
- package/package.json +3 -1
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Agent Orchestrator
|
|
5
|
+
*
|
|
6
|
+
* Manages multiple agents, handles context sharing, and aggregates results.
|
|
7
|
+
* Part of Phase 29: Agent Runtime.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { EventEmitter } = require('events');
|
|
11
|
+
const { AGENT_STATE, AGENT_PRIORITY } = require('./base-agent');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Orchestration strategies
|
|
15
|
+
*/
|
|
16
|
+
const ORCHESTRATION_STRATEGY = {
|
|
17
|
+
SEQUENTIAL: 'sequential', // Run agents one after another
|
|
18
|
+
PARALLEL: 'parallel', // Run all agents simultaneously
|
|
19
|
+
PRIORITY: 'priority', // Run by priority, highest first
|
|
20
|
+
PIPELINE: 'pipeline', // Each agent's output feeds next agent's input
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Result aggregation strategies
|
|
25
|
+
*/
|
|
26
|
+
const AGGREGATION_STRATEGY = {
|
|
27
|
+
MERGE: 'merge', // Merge all results into one object
|
|
28
|
+
ARRAY: 'array', // Collect results as array
|
|
29
|
+
VOTE: 'vote', // Majority voting on common fields
|
|
30
|
+
WEIGHTED: 'weighted', // Weight results by agent priority
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Agent Orchestrator class
|
|
35
|
+
*/
|
|
36
|
+
class AgentOrchestrator extends EventEmitter {
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
super();
|
|
39
|
+
|
|
40
|
+
this.agents = new Map();
|
|
41
|
+
this.strategy = options.strategy || ORCHESTRATION_STRATEGY.PARALLEL;
|
|
42
|
+
this.aggregation = options.aggregation || AGGREGATION_STRATEGY.MERGE;
|
|
43
|
+
this.timeout = options.timeout || 60000;
|
|
44
|
+
this.maxConcurrent = options.maxConcurrent || 5;
|
|
45
|
+
|
|
46
|
+
this.results = new Map();
|
|
47
|
+
this.sharedContext = {};
|
|
48
|
+
this.isRunning = false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Register an agent with the orchestrator
|
|
53
|
+
* @param {BaseAgent} agent - Agent to register
|
|
54
|
+
* @returns {string} Agent ID
|
|
55
|
+
*/
|
|
56
|
+
register(agent) {
|
|
57
|
+
if (this.agents.has(agent.id)) {
|
|
58
|
+
throw new Error(`Agent ${agent.id} already registered`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.agents.set(agent.id, agent);
|
|
62
|
+
|
|
63
|
+
// Set up event forwarding
|
|
64
|
+
agent.on('spawned', (data) => this.emit('agentSpawned', data));
|
|
65
|
+
agent.on('started', (data) => this.emit('agentStarted', data));
|
|
66
|
+
agent.on('completed', (data) => this.emit('agentCompleted', data));
|
|
67
|
+
agent.on('error', (data) => this.emit('agentError', data));
|
|
68
|
+
|
|
69
|
+
this.emit('agentRegistered', { agentId: agent.id, name: agent.name });
|
|
70
|
+
return agent.id;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Unregister an agent
|
|
75
|
+
* @param {string} agentId - Agent ID to unregister
|
|
76
|
+
*/
|
|
77
|
+
unregister(agentId) {
|
|
78
|
+
const agent = this.agents.get(agentId);
|
|
79
|
+
if (agent) {
|
|
80
|
+
agent.removeAllListeners();
|
|
81
|
+
this.agents.delete(agentId);
|
|
82
|
+
this.emit('agentUnregistered', { agentId });
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Set shared context for all agents
|
|
88
|
+
* @param {object} context - Context to share
|
|
89
|
+
*/
|
|
90
|
+
setSharedContext(context) {
|
|
91
|
+
this.sharedContext = { ...this.sharedContext, ...context };
|
|
92
|
+
this.emit('contextUpdated', { keys: Object.keys(context) });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Run all registered agents
|
|
97
|
+
* @param {object} input - Input to pass to all agents
|
|
98
|
+
* @returns {Promise<object>}
|
|
99
|
+
*/
|
|
100
|
+
async runAll(input = {}) {
|
|
101
|
+
if (this.isRunning) {
|
|
102
|
+
throw new Error('Orchestrator is already running');
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.isRunning = true;
|
|
106
|
+
this.results.clear();
|
|
107
|
+
|
|
108
|
+
const combinedInput = { ...this.sharedContext, ...input };
|
|
109
|
+
|
|
110
|
+
this.emit('started', {
|
|
111
|
+
agentCount: this.agents.size,
|
|
112
|
+
strategy: this.strategy,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
let results;
|
|
117
|
+
|
|
118
|
+
switch (this.strategy) {
|
|
119
|
+
case ORCHESTRATION_STRATEGY.SEQUENTIAL:
|
|
120
|
+
results = await this._runSequential(combinedInput);
|
|
121
|
+
break;
|
|
122
|
+
case ORCHESTRATION_STRATEGY.PARALLEL:
|
|
123
|
+
results = await this._runParallel(combinedInput);
|
|
124
|
+
break;
|
|
125
|
+
case ORCHESTRATION_STRATEGY.PRIORITY:
|
|
126
|
+
results = await this._runByPriority(combinedInput);
|
|
127
|
+
break;
|
|
128
|
+
case ORCHESTRATION_STRATEGY.PIPELINE:
|
|
129
|
+
results = await this._runPipeline(combinedInput);
|
|
130
|
+
break;
|
|
131
|
+
default:
|
|
132
|
+
results = await this._runParallel(combinedInput);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const aggregated = this._aggregateResults(results);
|
|
136
|
+
|
|
137
|
+
this.emit('completed', {
|
|
138
|
+
agentCount: this.agents.size,
|
|
139
|
+
successCount: results.filter(r => r.success).length,
|
|
140
|
+
failureCount: results.filter(r => !r.success).length,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
return aggregated;
|
|
144
|
+
} finally {
|
|
145
|
+
this.isRunning = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Run agents sequentially
|
|
151
|
+
* @param {object} input
|
|
152
|
+
* @returns {Promise<Array>}
|
|
153
|
+
*/
|
|
154
|
+
async _runSequential(input) {
|
|
155
|
+
const results = [];
|
|
156
|
+
|
|
157
|
+
for (const [agentId, agent] of this.agents) {
|
|
158
|
+
try {
|
|
159
|
+
const result = await agent.run(input);
|
|
160
|
+
results.push({ agentId, success: true, result });
|
|
161
|
+
this.results.set(agentId, result);
|
|
162
|
+
} catch (error) {
|
|
163
|
+
results.push({ agentId, success: false, error: error.message });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return results;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Run agents in parallel
|
|
172
|
+
* @param {object} input
|
|
173
|
+
* @returns {Promise<Array>}
|
|
174
|
+
*/
|
|
175
|
+
async _runParallel(input) {
|
|
176
|
+
const agentArray = Array.from(this.agents.entries());
|
|
177
|
+
const results = [];
|
|
178
|
+
|
|
179
|
+
// Run in batches to respect maxConcurrent
|
|
180
|
+
for (let i = 0; i < agentArray.length; i += this.maxConcurrent) {
|
|
181
|
+
const batch = agentArray.slice(i, i + this.maxConcurrent);
|
|
182
|
+
|
|
183
|
+
const batchResults = await Promise.allSettled(
|
|
184
|
+
batch.map(async ([agentId, agent]) => {
|
|
185
|
+
const result = await agent.run(input);
|
|
186
|
+
this.results.set(agentId, result);
|
|
187
|
+
return { agentId, result };
|
|
188
|
+
})
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
for (const settledResult of batchResults) {
|
|
192
|
+
if (settledResult.status === 'fulfilled') {
|
|
193
|
+
results.push({
|
|
194
|
+
agentId: settledResult.value.agentId,
|
|
195
|
+
success: true,
|
|
196
|
+
result: settledResult.value.result,
|
|
197
|
+
});
|
|
198
|
+
} else {
|
|
199
|
+
results.push({
|
|
200
|
+
agentId: 'unknown',
|
|
201
|
+
success: false,
|
|
202
|
+
error: settledResult.reason.message,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return results;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Run agents by priority (highest first)
|
|
213
|
+
* @param {object} input
|
|
214
|
+
* @returns {Promise<Array>}
|
|
215
|
+
*/
|
|
216
|
+
async _runByPriority(input) {
|
|
217
|
+
const sortedAgents = Array.from(this.agents.entries())
|
|
218
|
+
.sort(([, a], [, b]) => a.priority - b.priority);
|
|
219
|
+
|
|
220
|
+
const results = [];
|
|
221
|
+
|
|
222
|
+
for (const [agentId, agent] of sortedAgents) {
|
|
223
|
+
try {
|
|
224
|
+
const result = await agent.run(input);
|
|
225
|
+
results.push({ agentId, success: true, result, priority: agent.priority });
|
|
226
|
+
this.results.set(agentId, result);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
results.push({ agentId, success: false, error: error.message, priority: agent.priority });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return results;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Run agents in pipeline (output → input)
|
|
237
|
+
* @param {object} input
|
|
238
|
+
* @returns {Promise<Array>}
|
|
239
|
+
*/
|
|
240
|
+
async _runPipeline(input) {
|
|
241
|
+
const results = [];
|
|
242
|
+
let currentInput = { ...input };
|
|
243
|
+
|
|
244
|
+
for (const [agentId, agent] of this.agents) {
|
|
245
|
+
try {
|
|
246
|
+
const result = await agent.run(currentInput);
|
|
247
|
+
results.push({ agentId, success: true, result });
|
|
248
|
+
this.results.set(agentId, result);
|
|
249
|
+
|
|
250
|
+
// Pass result as input to next agent
|
|
251
|
+
currentInput = { ...currentInput, previousResult: result };
|
|
252
|
+
} catch (error) {
|
|
253
|
+
results.push({ agentId, success: false, error: error.message });
|
|
254
|
+
// Stop pipeline on failure
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return results;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Aggregate results based on strategy
|
|
264
|
+
* @param {Array} results
|
|
265
|
+
* @returns {object}
|
|
266
|
+
*/
|
|
267
|
+
_aggregateResults(results) {
|
|
268
|
+
const successfulResults = results.filter(r => r.success);
|
|
269
|
+
|
|
270
|
+
switch (this.aggregation) {
|
|
271
|
+
case AGGREGATION_STRATEGY.MERGE:
|
|
272
|
+
return this._mergeResults(successfulResults);
|
|
273
|
+
case AGGREGATION_STRATEGY.ARRAY:
|
|
274
|
+
return this._arrayResults(successfulResults);
|
|
275
|
+
case AGGREGATION_STRATEGY.VOTE:
|
|
276
|
+
return this._voteResults(successfulResults);
|
|
277
|
+
case AGGREGATION_STRATEGY.WEIGHTED:
|
|
278
|
+
return this._weightedResults(successfulResults);
|
|
279
|
+
default:
|
|
280
|
+
return this._mergeResults(successfulResults);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Merge all results into single object
|
|
286
|
+
* @param {Array} results
|
|
287
|
+
* @returns {object}
|
|
288
|
+
*/
|
|
289
|
+
_mergeResults(results) {
|
|
290
|
+
const merged = {
|
|
291
|
+
_meta: {
|
|
292
|
+
agentCount: results.length,
|
|
293
|
+
strategy: this.aggregation,
|
|
294
|
+
timestamp: new Date().toISOString(),
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
for (const { agentId, result } of results) {
|
|
299
|
+
const agent = this.agents.get(agentId);
|
|
300
|
+
const key = agent ? agent.type : agentId;
|
|
301
|
+
merged[key] = result;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return merged;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Collect results as array
|
|
309
|
+
* @param {Array} results
|
|
310
|
+
* @returns {object}
|
|
311
|
+
*/
|
|
312
|
+
_arrayResults(results) {
|
|
313
|
+
return {
|
|
314
|
+
_meta: {
|
|
315
|
+
agentCount: results.length,
|
|
316
|
+
strategy: this.aggregation,
|
|
317
|
+
timestamp: new Date().toISOString(),
|
|
318
|
+
},
|
|
319
|
+
results: results.map(({ agentId, result }) => {
|
|
320
|
+
const agent = this.agents.get(agentId);
|
|
321
|
+
return {
|
|
322
|
+
agent: agent ? agent.name : agentId,
|
|
323
|
+
type: agent ? agent.type : 'unknown',
|
|
324
|
+
result,
|
|
325
|
+
};
|
|
326
|
+
}),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Majority voting on common fields
|
|
332
|
+
* @param {Array} results
|
|
333
|
+
* @returns {object}
|
|
334
|
+
*/
|
|
335
|
+
_voteResults(results) {
|
|
336
|
+
const votes = {};
|
|
337
|
+
const finalResult = {};
|
|
338
|
+
|
|
339
|
+
// Collect votes for each field
|
|
340
|
+
for (const { result } of results) {
|
|
341
|
+
if (typeof result === 'object' && result !== null) {
|
|
342
|
+
for (const [key, value] of Object.entries(result)) {
|
|
343
|
+
if (!votes[key]) votes[key] = new Map();
|
|
344
|
+
|
|
345
|
+
const valueKey = JSON.stringify(value);
|
|
346
|
+
const currentCount = votes[key].get(valueKey) || 0;
|
|
347
|
+
votes[key].set(valueKey, currentCount + 1);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Select majority for each field
|
|
353
|
+
for (const [key, valueMap] of Object.entries(votes)) {
|
|
354
|
+
let maxCount = 0;
|
|
355
|
+
let winner = null;
|
|
356
|
+
|
|
357
|
+
for (const [valueKey, count] of valueMap.entries()) {
|
|
358
|
+
if (count > maxCount) {
|
|
359
|
+
maxCount = count;
|
|
360
|
+
winner = JSON.parse(valueKey);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
finalResult[key] = winner;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
_meta: {
|
|
369
|
+
agentCount: results.length,
|
|
370
|
+
strategy: this.aggregation,
|
|
371
|
+
timestamp: new Date().toISOString(),
|
|
372
|
+
},
|
|
373
|
+
...finalResult,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Weight results by agent priority
|
|
379
|
+
* @param {Array} results
|
|
380
|
+
* @returns {object}
|
|
381
|
+
*/
|
|
382
|
+
_weightedResults(results) {
|
|
383
|
+
const weighted = {
|
|
384
|
+
_meta: {
|
|
385
|
+
agentCount: results.length,
|
|
386
|
+
strategy: this.aggregation,
|
|
387
|
+
timestamp: new Date().toISOString(),
|
|
388
|
+
},
|
|
389
|
+
byPriority: {},
|
|
390
|
+
};
|
|
391
|
+
|
|
392
|
+
// Group by priority
|
|
393
|
+
for (const { agentId, result } of results) {
|
|
394
|
+
const agent = this.agents.get(agentId);
|
|
395
|
+
const priority = agent ? agent.priority : AGENT_PRIORITY.NORMAL;
|
|
396
|
+
const priorityKey = `priority_${priority}`;
|
|
397
|
+
|
|
398
|
+
if (!weighted.byPriority[priorityKey]) {
|
|
399
|
+
weighted.byPriority[priorityKey] = [];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
weighted.byPriority[priorityKey].push({
|
|
403
|
+
agent: agent ? agent.name : agentId,
|
|
404
|
+
result,
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return weighted;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Get status of all agents
|
|
413
|
+
* @returns {Array}
|
|
414
|
+
*/
|
|
415
|
+
getStatus() {
|
|
416
|
+
return Array.from(this.agents.values()).map(agent => agent.getStatus());
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Cancel all running agents
|
|
421
|
+
* @param {string} reason
|
|
422
|
+
*/
|
|
423
|
+
cancelAll(reason = 'Orchestrator cancelled') {
|
|
424
|
+
for (const agent of this.agents.values()) {
|
|
425
|
+
if (agent.state === AGENT_STATE.RUNNING || agent.state === AGENT_STATE.SPAWNING) {
|
|
426
|
+
agent.cancel(reason);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
this.isRunning = false;
|
|
431
|
+
this.emit('cancelled', { reason });
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Clear all agents and results
|
|
436
|
+
*/
|
|
437
|
+
clear() {
|
|
438
|
+
this.cancelAll('Clearing orchestrator');
|
|
439
|
+
this.agents.clear();
|
|
440
|
+
this.results.clear();
|
|
441
|
+
this.sharedContext = {};
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
module.exports = {
|
|
446
|
+
AgentOrchestrator,
|
|
447
|
+
ORCHESTRATION_STRATEGY,
|
|
448
|
+
AGGREGATION_STRATEGY,
|
|
449
|
+
};
|