Initial commit: Agent Governance System Phase 8

Phase 8 Production Hardening with complete governance infrastructure:

- Vault integration with tiered policies (T0-T4)
- DragonflyDB state management
- SQLite audit ledger
- Pipeline DSL and templates
- Promotion/revocation engine
- Checkpoint system for session persistence
- Health manager and circuit breaker for fault tolerance
- GitHub/Slack integrations
- Architectural test pipeline with bug watcher, suggestion engine, council review
- Multi-agent chaos testing framework

Test Results:
- Governance tests: 68/68 passing
- E2E workflow: 16/16 passing
- Phase 2 Vault: 14/14 passing
- Integration tests: 27/27 passing

Coverage: 57.6% average across 12 phases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
profit
2026-01-23 22:07:06 -05:00
co-authored by Claude Opus 4.5
commit 77655c298c
338 changed files with 63491 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
# Multi Agent
> Multi-agent coordination demos
## Overview
This directory contains multi-agent coordination demos.
## Key Files
| File | Description |
|------|-------------|
| `types.ts` | TypeScript module |
| `coordination.ts` | TypeScript module |
| `agents.ts` | TypeScript module |
| `orchestrator.ts` | TypeScript module |
| `package.json` | Data/Schema |
## Interfaces / APIs
*Document any APIs, CLI commands, or interfaces here.*
## Status
** Not Started**
See [STATUS.md](./STATUS.md) for detailed progress tracking.
## Architecture Reference
Part of the [Agent Governance System](/opt/agent-governance/docs/ARCHITECTURE.md).
Parent: [Agents](..)
---
*Last updated: 2026-01-23 23:25:09 UTC*
+30
View File
@@ -0,0 +1,30 @@
# Status: Multi Agent
## Current Phase
** NOT STARTED**
## Tasks
| Status | Task | Updated |
|--------|------|---------|
| ☐ | *No tasks defined* | - |
## Dependencies
*No external dependencies.*
## Issues / Blockers
*No current issues or blockers.*
## Activity Log
### 2026-01-23 23:25:09 UTC
- **Phase**: NOT STARTED
- **Action**: Initialized
- **Details**: Status tracking initialized for this directory.
---
*Last updated: 2026-01-23 23:25:09 UTC*
+893
View File
@@ -0,0 +1,893 @@
/**
* Multi-Agent Coordination System - Agent Implementations
*/
import OpenAI from "openai";
import { $ } from "bun";
import type {
AgentRole,
AgentState,
AgentMessage,
TaskDefinition,
BlackboardEntry,
ConsensusVote,
} from "./types";
import {
Blackboard,
MessageBus,
AgentStateManager,
SpawnController,
MetricsCollector,
} from "./coordination";
function now(): string {
return new Date().toISOString();
}
function generateId(): string {
return Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
}
async function getVaultSecret(path: string): Promise<Record<string, any>> {
const initKeys = await Bun.file("/opt/vault/init-keys.json").json();
const token = initKeys.root_token;
const result = await $`curl -sk -H "X-Vault-Token: ${token}" https://127.0.0.1:8200/v1/secret/data/${path}`.json();
return result.data.data;
}
// =============================================================================
// Base Agent Class
// =============================================================================
abstract class BaseAgent {
protected role: AgentRole;
protected taskId: string;
protected blackboard: Blackboard;
protected messageBus: MessageBus;
protected stateManager: AgentStateManager;
protected metrics: MetricsCollector;
protected llm!: OpenAI;
protected model: string;
protected state: AgentState;
protected startTime: number;
protected log: (msg: string) => void;
constructor(
role: AgentRole,
taskId: string,
blackboard: Blackboard,
messageBus: MessageBus,
stateManager: AgentStateManager,
metrics: MetricsCollector,
model: string = "anthropic/claude-sonnet-4"
) {
this.role = role;
this.taskId = taskId;
this.blackboard = blackboard;
this.messageBus = messageBus;
this.stateManager = stateManager;
this.metrics = metrics;
this.model = model;
this.startTime = Date.now();
this.state = {
agent_id: `${role}-${taskId}`,
role,
status: "IDLE",
current_task: "",
progress: 0,
confidence: 0,
last_activity: now(),
messages_sent: 0,
messages_received: 0,
proposals_made: 0,
conflicts_detected: 0,
};
this.log = (msg: string) => {
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
console.log(`[${elapsed}s] [${this.role}] ${msg}`);
};
}
async init(): Promise<void> {
const secrets = await getVaultSecret("api-keys/openrouter");
this.llm = new OpenAI({
baseURL: "https://openrouter.ai/api/v1",
apiKey: secrets.api_key,
});
// Register message handler
this.messageBus.onMessage(this.role, (msg) => this.handleMessage(msg));
await this.updateState({ status: "IDLE" });
this.log("Initialized");
}
protected async updateState(partial: Partial<AgentState>): Promise<void> {
Object.assign(this.state, partial);
this.state.last_activity = now();
await this.stateManager.updateState(this.state);
}
protected async handleMessage(msg: AgentMessage): Promise<void> {
this.state.messages_received++;
this.log(`Received ${msg.type} from ${msg.from}`);
// Override in subclasses for specific handling
}
protected async sendMessage(to: AgentRole | "ALL", type: AgentMessage["type"], payload: any, correlationId?: string): Promise<void> {
await this.messageBus.send(to, type, payload, correlationId);
this.state.messages_sent++;
await this.updateState({});
}
protected async writeToBlackboard(section: Parameters<Blackboard["write"]>[0], key: string, value: any): Promise<void> {
await this.blackboard.write(section, key, value, this.role);
this.log(`Wrote to blackboard: ${section}/${key}`);
}
protected async readFromBlackboard(section: Parameters<Blackboard["read"]>[0], key: string): Promise<any> {
const entry = await this.blackboard.read(section, key);
return entry?.value;
}
protected async callLLM(systemPrompt: string, userPrompt: string, maxTokens: number = 2000): Promise<string> {
const response = await this.llm.chat.completions.create({
model: this.model,
messages: [
{ role: "system", content: systemPrompt },
{ role: "user", content: userPrompt },
],
max_tokens: maxTokens,
temperature: 0.4,
});
return response.choices[0].message.content || "";
}
abstract run(task: TaskDefinition): Promise<void>;
}
// =============================================================================
// Agent ALPHA - Research & Analysis Specialist
// =============================================================================
export class AgentAlpha extends BaseAgent {
private proposals: Map<string, any> = new Map();
constructor(
taskId: string,
blackboard: Blackboard,
messageBus: MessageBus,
stateManager: AgentStateManager,
metrics: MetricsCollector,
model?: string
) {
super("ALPHA", taskId, blackboard, messageBus, stateManager, metrics, model);
}
protected async handleMessage(msg: AgentMessage): Promise<void> {
await super.handleMessage(msg);
switch (msg.type) {
case "FEEDBACK":
await this.handleFeedback(msg);
break;
case "QUERY":
await this.handleQuery(msg);
break;
case "SYNC":
await this.handleSync(msg);
break;
}
}
private async handleFeedback(msg: AgentMessage): Promise<void> {
const { proposal_id, accepted, reasoning, suggestions } = msg.payload;
this.log(`Received feedback on proposal ${proposal_id}: ${accepted ? "ACCEPTED" : "REJECTED"}`);
if (!accepted && suggestions) {
// Revise proposal based on feedback
const original = this.proposals.get(proposal_id);
if (original) {
await this.reviseProposal(proposal_id, original, suggestions);
}
}
}
private async handleQuery(msg: AgentMessage): Promise<void> {
const { question, context } = msg.payload;
this.log(`Answering query from ${msg.from}: ${question.slice(0, 50)}...`);
const answer = await this.analyzeQuestion(question, context);
await this.sendMessage(msg.from as AgentRole, "RESPONSE", {
question,
answer,
confidence: answer.confidence,
}, msg.id);
}
private async handleSync(msg: AgentMessage): Promise<void> {
// Share current state with other agents
const currentProposals = Array.from(this.proposals.entries());
await this.sendMessage(msg.from as AgentRole, "RESPONSE", {
proposals: currentProposals,
progress: this.state.progress,
current_task: this.state.current_task,
}, msg.id);
}
private async analyzeQuestion(question: string, context: any): Promise<any> {
const response = await this.callLLM(
`You are Agent ALPHA, a research and analysis specialist. Answer questions thoroughly and provide confidence scores.
Output JSON: { "answer": "...", "confidence": 0.0-1.0, "supporting_evidence": [...], "uncertainties": [...] }`,
`Question: ${question}\nContext: ${JSON.stringify(context)}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
return match ? JSON.parse(match[0]) : { answer: response, confidence: 0.5 };
} catch {
return { answer: response, confidence: 0.5 };
}
}
private async reviseProposal(proposalId: string, original: any, suggestions: string[]): Promise<void> {
this.log(`Revising proposal ${proposalId} based on feedback`);
const response = await this.callLLM(
`You are Agent ALPHA. Revise the proposal based on the feedback suggestions.
Output JSON: { "revised_proposal": {...}, "changes_made": [...], "confidence": 0.0-1.0 }`,
`Original proposal: ${JSON.stringify(original)}\nSuggestions: ${suggestions.join(", ")}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
if (match) {
const revised = JSON.parse(match[0]);
const newProposalId = proposalId + "-rev";
this.proposals.set(newProposalId, revised.revised_proposal);
await this.writeToBlackboard("solutions", newProposalId, revised);
await this.sendMessage("BETA", "PROPOSAL", {
proposal_id: newProposalId,
proposal: revised.revised_proposal,
is_revision: true,
original_id: proposalId,
});
this.state.proposals_made++;
}
} catch (e) {
this.log(`Failed to revise proposal: ${e}`);
}
}
async run(task: TaskDefinition): Promise<void> {
await this.updateState({ status: "WORKING", current_task: "Analyzing problem" });
this.log(`Starting analysis of: ${task.objective}`);
// Phase 1: Problem Analysis
await this.writeToBlackboard("problem", "objective", task.objective);
await this.writeToBlackboard("problem", "constraints", task.constraints);
const analysis = await this.analyzeProblem(task);
await this.writeToBlackboard("problem", "analysis", analysis);
this.log(`Problem analysis complete. Complexity: ${analysis.complexity_score}`);
await this.updateState({ progress: 0.2 });
// Phase 2: Generate Initial Proposals
await this.updateState({ current_task: "Generating solution proposals" });
const proposals = await this.generateProposals(task, analysis);
for (const proposal of proposals) {
const proposalId = generateId();
this.proposals.set(proposalId, proposal);
await this.writeToBlackboard("solutions", proposalId, proposal);
// Send proposal to BETA for evaluation
await this.sendMessage("BETA", "PROPOSAL", {
proposal_id: proposalId,
proposal,
phase: "initial",
});
this.state.proposals_made++;
}
this.log(`Generated ${proposals.length} initial proposals`);
await this.updateState({ progress: 0.5 });
// Phase 3: Iterative Refinement Loop
await this.updateState({ current_task: "Refining solutions" });
let iterations = 0;
const maxIterations = 5;
while (iterations < maxIterations && this.state.progress < 0.9) {
iterations++;
this.log(`Refinement iteration ${iterations}`);
// Read feedback from blackboard
const feedbackEntries = await this.blackboard.readSection("progress");
const latestFeedback = feedbackEntries
.filter(e => e.author === "BETA")
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())[0];
if (latestFeedback?.value?.needs_revision) {
// More work needed
await this.refineBasedOnFeedback(latestFeedback.value);
}
await this.updateState({ progress: Math.min(0.9, this.state.progress + 0.1) });
await new Promise(r => setTimeout(r, 500)); // Brief pause between iterations
}
await this.updateState({ status: "WAITING", current_task: "Awaiting consensus", progress: 0.9 });
this.log("Analysis phase complete, awaiting consensus");
}
private async analyzeProblem(task: TaskDefinition): Promise<any> {
const response = await this.callLLM(
`You are Agent ALPHA, a research and analysis specialist. Analyze the problem thoroughly.
Output JSON: {
"complexity_score": 0.0-1.0,
"key_challenges": [...],
"dependencies": [...],
"risks": [...],
"recommended_approach": "...",
"subtask_breakdown": [...]
}`,
`Objective: ${task.objective}\nConstraints: ${task.constraints.join(", ")}\nSubtasks: ${task.subtasks.map(s => s.description).join(", ")}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
return match ? JSON.parse(match[0]) : { complexity_score: 0.5, key_challenges: [response] };
} catch {
return { complexity_score: 0.5, key_challenges: [response] };
}
}
private async generateProposals(task: TaskDefinition, analysis: any): Promise<any[]> {
const response = await this.callLLM(
`You are Agent ALPHA. Generate 2-3 distinct solution proposals based on the analysis.
Output JSON array: [
{ "name": "...", "approach": "...", "steps": [...], "pros": [...], "cons": [...], "confidence": 0.0-1.0 },
...
]`,
`Task: ${task.objective}\nAnalysis: ${JSON.stringify(analysis)}\nConstraints: ${task.constraints.join(", ")}`
);
try {
const match = response.match(/\[[\s\S]*\]/);
return match ? JSON.parse(match[0]) : [{ name: "Default", approach: response, steps: [], confidence: 0.5 }];
} catch {
return [{ name: "Default", approach: response, steps: [], confidence: 0.5 }];
}
}
private async refineBasedOnFeedback(feedback: any): Promise<void> {
const { proposal_id, issues, suggestions } = feedback;
if (proposal_id && this.proposals.has(proposal_id)) {
await this.reviseProposal(proposal_id, this.proposals.get(proposal_id), suggestions || []);
}
}
}
// =============================================================================
// Agent BETA - Implementation & Synthesis Specialist
// =============================================================================
export class AgentBeta extends BaseAgent {
private evaluatedProposals: Map<string, any> = new Map();
constructor(
taskId: string,
blackboard: Blackboard,
messageBus: MessageBus,
stateManager: AgentStateManager,
metrics: MetricsCollector,
model?: string
) {
super("BETA", taskId, blackboard, messageBus, stateManager, metrics, model);
}
protected async handleMessage(msg: AgentMessage): Promise<void> {
await super.handleMessage(msg);
switch (msg.type) {
case "PROPOSAL":
await this.evaluateProposal(msg);
break;
case "QUERY":
await this.handleQuery(msg);
break;
case "SYNC":
await this.handleSync(msg);
break;
}
}
private async evaluateProposal(msg: AgentMessage): Promise<void> {
const { proposal_id, proposal, phase, is_revision } = msg.payload;
this.log(`Evaluating proposal ${proposal_id} (${is_revision ? "revision" : phase})`);
await this.updateState({ current_task: `Evaluating proposal ${proposal_id}` });
const evaluation = await this.performEvaluation(proposal);
this.evaluatedProposals.set(proposal_id, { proposal, evaluation });
// Write evaluation to blackboard
await this.writeToBlackboard("progress", `eval_${proposal_id}`, {
proposal_id,
evaluation,
evaluator: this.role,
timestamp: now(),
});
// Send feedback to ALPHA
const accepted = evaluation.score >= 0.7 && evaluation.feasibility >= 0.6;
await this.sendMessage("ALPHA", "FEEDBACK", {
proposal_id,
accepted,
score: evaluation.score,
reasoning: evaluation.reasoning,
suggestions: evaluation.improvements,
needs_revision: !accepted,
}, msg.id);
if (accepted) {
this.log(`Proposal ${proposal_id} accepted with score ${evaluation.score}`);
// Cast vote for consensus
const vote: ConsensusVote = {
agent: this.role,
proposal_id,
vote: "ACCEPT",
reasoning: evaluation.reasoning,
timestamp: now(),
};
await this.blackboard.recordVote(vote);
} else {
this.log(`Proposal ${proposal_id} needs revision. Score: ${evaluation.score}`);
await this.metrics.increment("conflicts_detected");
this.state.conflicts_detected++;
}
}
private async handleQuery(msg: AgentMessage): Promise<void> {
const { question, context } = msg.payload;
this.log(`Answering query from ${msg.from}`);
const answer = await this.synthesizeAnswer(question, context);
await this.sendMessage(msg.from as AgentRole, "RESPONSE", {
question,
answer,
}, msg.id);
}
private async handleSync(msg: AgentMessage): Promise<void> {
const evaluations = Array.from(this.evaluatedProposals.entries());
await this.sendMessage(msg.from as AgentRole, "RESPONSE", {
evaluations,
progress: this.state.progress,
}, msg.id);
}
private async performEvaluation(proposal: any): Promise<any> {
const response = await this.callLLM(
`You are Agent BETA, an implementation and synthesis specialist. Evaluate this proposal critically.
Output JSON: {
"score": 0.0-1.0,
"feasibility": 0.0-1.0,
"completeness": 0.0-1.0,
"reasoning": "...",
"strengths": [...],
"weaknesses": [...],
"improvements": [...],
"implementation_notes": "..."
}`,
`Proposal: ${JSON.stringify(proposal)}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
return match ? JSON.parse(match[0]) : { score: 0.5, feasibility: 0.5, reasoning: response };
} catch {
return { score: 0.5, feasibility: 0.5, reasoning: response };
}
}
private async synthesizeAnswer(question: string, context: any): Promise<any> {
const response = await this.callLLM(
`You are Agent BETA. Provide a practical, implementation-focused answer.`,
`Question: ${question}\nContext: ${JSON.stringify(context)}`
);
return { answer: response, source: this.role };
}
async run(task: TaskDefinition): Promise<void> {
await this.updateState({ status: "WORKING", current_task: "Preparing for evaluation" });
this.log(`Starting evaluation mode for: ${task.objective}`);
// Phase 1: Read and understand the problem from blackboard
await new Promise(r => setTimeout(r, 1000)); // Wait for ALPHA to write problem analysis
const problemAnalysis = await this.readFromBlackboard("problem", "analysis");
if (problemAnalysis) {
this.log(`Read problem analysis: complexity ${problemAnalysis.complexity_score}`);
}
await this.updateState({ progress: 0.1 });
// Phase 2: Active evaluation loop - wait for proposals and evaluate
await this.updateState({ current_task: "Evaluating proposals" });
let iterations = 0;
const maxIterations = 10;
while (iterations < maxIterations && this.state.progress < 0.9) {
iterations++;
// Check for new proposals on blackboard
const solutions = await this.blackboard.readSection("solutions");
const newProposals = solutions.filter(s =>
!this.evaluatedProposals.has(s.key) && s.author === "ALPHA"
);
for (const entry of newProposals) {
// Evaluate via message handling (simulated direct read)
if (!this.evaluatedProposals.has(entry.key)) {
await this.evaluateProposal({
id: generateId(),
from: "ALPHA",
to: "BETA",
type: "PROPOSAL",
payload: {
proposal_id: entry.key,
proposal: entry.value,
phase: "blackboard_read",
},
timestamp: now(),
});
}
}
await this.updateState({ progress: Math.min(0.9, 0.1 + (iterations * 0.08)) });
await new Promise(r => setTimeout(r, 500));
}
// Phase 3: Generate synthesis of best solutions
await this.updateState({ current_task: "Synthesizing final solution" });
const bestProposals = Array.from(this.evaluatedProposals.entries())
.filter(([_, v]) => v.evaluation.score >= 0.6)
.sort((a, b) => b[1].evaluation.score - a[1].evaluation.score);
if (bestProposals.length > 0) {
const synthesis = await this.synthesizeSolution(bestProposals.map(([_, v]) => v));
await this.writeToBlackboard("solutions", "synthesis", synthesis);
this.log(`Generated synthesis from ${bestProposals.length} proposals`);
// Vote for synthesis
const vote: ConsensusVote = {
agent: this.role,
proposal_id: "synthesis",
vote: "ACCEPT",
reasoning: "Synthesized best elements from top proposals",
timestamp: now(),
};
await this.blackboard.recordVote(vote);
}
await this.updateState({ status: "WAITING", current_task: "Awaiting consensus", progress: 0.9 });
this.log("Evaluation phase complete, awaiting consensus");
}
private async synthesizeSolution(proposals: any[]): Promise<any> {
const response = await this.callLLM(
`You are Agent BETA. Synthesize the best elements from these evaluated proposals into a final solution.
Output JSON: {
"final_solution": { "name": "...", "approach": "...", "steps": [...] },
"confidence": 0.0-1.0,
"sources": [...],
"trade_offs": [...]
}`,
`Proposals and evaluations: ${JSON.stringify(proposals)}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
return match ? JSON.parse(match[0]) : { final_solution: { approach: response }, confidence: 0.5 };
} catch {
return { final_solution: { approach: response }, confidence: 0.5 };
}
}
}
// =============================================================================
// Agent GAMMA - Mediator & Resolver (Conditionally Spawned)
// =============================================================================
export class AgentGamma extends BaseAgent {
private spawnReason: string;
constructor(
taskId: string,
blackboard: Blackboard,
messageBus: MessageBus,
stateManager: AgentStateManager,
metrics: MetricsCollector,
spawnReason: string,
model?: string
) {
super("GAMMA", taskId, blackboard, messageBus, stateManager, metrics, model);
this.spawnReason = spawnReason;
}
protected async handleMessage(msg: AgentMessage): Promise<void> {
await super.handleMessage(msg);
switch (msg.type) {
case "QUERY":
await this.handleQuery(msg);
break;
case "HANDOFF":
await this.handleHandoff(msg);
break;
}
}
private async handleQuery(msg: AgentMessage): Promise<void> {
const { question, context } = msg.payload;
const answer = await this.mediateQuestion(question, context);
await this.sendMessage(msg.from as AgentRole, "RESPONSE", answer, msg.id);
}
private async handleHandoff(msg: AgentMessage): Promise<void> {
this.log(`Received handoff from ${msg.from}: ${JSON.stringify(msg.payload).slice(0, 100)}...`);
}
private async mediateQuestion(question: string, context: any): Promise<any> {
const response = await this.callLLM(
`You are Agent GAMMA, a mediator and conflict resolver. Provide balanced, integrative answers.`,
`Question: ${question}\nContext: ${JSON.stringify(context)}`
);
return { answer: response, mediator: true };
}
async run(task: TaskDefinition): Promise<void> {
await this.updateState({ status: "WORKING", current_task: `Resolving: ${this.spawnReason}` });
this.log(`GAMMA spawned for: ${this.spawnReason}`);
// Announce presence
await this.sendMessage("ALL", "SYNC", {
event: "GAMMA_SPAWNED",
reason: this.spawnReason,
timestamp: now(),
});
// Phase 1: Gather current state from all sources
const problemState = await this.blackboard.readSection("problem");
const solutionsState = await this.blackboard.readSection("solutions");
const progressState = await this.blackboard.readSection("progress");
const conflictsState = await this.blackboard.readSection("conflicts");
this.log(`Gathered state: ${problemState.length} problem entries, ${solutionsState.length} solutions`);
await this.updateState({ progress: 0.2 });
// Phase 2: Analyze situation based on spawn reason
let resolution: any;
switch (this.spawnReason) {
case "STUCK":
resolution = await this.resolveStuck(task, problemState, solutionsState, progressState);
break;
case "CONFLICT":
resolution = await this.resolveConflict(conflictsState, solutionsState);
break;
case "COMPLEXITY":
resolution = await this.handleComplexity(task, problemState, solutionsState);
break;
case "SUCCESS":
resolution = await this.validateSuccess(task, solutionsState);
break;
default:
resolution = await this.generalMediation(task, problemState, solutionsState);
}
await this.writeToBlackboard("progress", "gamma_resolution", resolution);
await this.updateState({ progress: 0.7 });
// Phase 3: Drive to consensus
await this.updateState({ current_task: "Driving consensus" });
const consensusResult = await this.driveConsensus(resolution);
await this.writeToBlackboard("consensus", "final", consensusResult);
// Cast final vote
const vote: ConsensusVote = {
agent: this.role,
proposal_id: "final_consensus",
vote: consensusResult.achieved ? "ACCEPT" : "ABSTAIN",
reasoning: consensusResult.reasoning,
timestamp: now(),
};
await this.blackboard.recordVote(vote);
await this.updateState({ status: "COMPLETED", progress: 1.0, current_task: "Resolution complete" });
this.log(`GAMMA resolution complete. Consensus: ${consensusResult.achieved}`);
}
private async resolveStuck(task: TaskDefinition, problem: BlackboardEntry[], solutions: BlackboardEntry[], progress: BlackboardEntry[]): Promise<any> {
this.log("Analyzing stuck condition...");
const response = await this.callLLM(
`You are Agent GAMMA, a mediator. The other agents appear stuck. Analyze the situation and provide direction.
Output JSON: {
"diagnosis": "why agents are stuck",
"blockers": [...],
"recommended_actions": [...],
"new_approach": "...",
"confidence": 0.0-1.0
}`,
`Task: ${task.objective}\nProblem analysis: ${JSON.stringify(problem)}\nSolutions so far: ${JSON.stringify(solutions)}\nProgress: ${JSON.stringify(progress)}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
const result = match ? JSON.parse(match[0]) : { diagnosis: response, confidence: 0.5 };
// Broadcast new direction to other agents
await this.sendMessage("ALL", "HANDOFF", {
type: "NEW_DIRECTION",
...result,
});
return result;
} catch {
return { diagnosis: response, confidence: 0.5 };
}
}
private async resolveConflict(conflicts: BlackboardEntry[], solutions: BlackboardEntry[]): Promise<any> {
this.log("Mediating conflicts...");
const response = await this.callLLM(
`You are Agent GAMMA, a conflict mediator. Resolve the conflicts between proposals.
Output JSON: {
"conflicts_analyzed": [...],
"resolution": "...",
"compromise_solution": {...},
"reasoning": "...",
"confidence": 0.0-1.0
}`,
`Conflicts: ${JSON.stringify(conflicts)}\nSolutions: ${JSON.stringify(solutions)}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
const result = match ? JSON.parse(match[0]) : { resolution: response, confidence: 0.5 };
await this.metrics.increment("conflicts_resolved");
return result;
} catch {
return { resolution: response, confidence: 0.5 };
}
}
private async handleComplexity(task: TaskDefinition, problem: BlackboardEntry[], solutions: BlackboardEntry[]): Promise<any> {
this.log("Breaking down complexity...");
const response = await this.callLLM(
`You are Agent GAMMA. The task is too complex. Break it into manageable pieces.
Output JSON: {
"complexity_analysis": "...",
"decomposition": [...subtasks...],
"priority_order": [...],
"delegation": { "ALPHA": [...], "BETA": [...] },
"confidence": 0.0-1.0
}`,
`Task: ${task.objective}\nProblem: ${JSON.stringify(problem)}\nCurrent solutions: ${JSON.stringify(solutions)}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
const result = match ? JSON.parse(match[0]) : { decomposition: [response], confidence: 0.5 };
// Delegate subtasks
if (result.delegation) {
if (result.delegation.ALPHA) {
await this.sendMessage("ALPHA", "HANDOFF", {
type: "SUBTASK_ASSIGNMENT",
tasks: result.delegation.ALPHA,
});
}
if (result.delegation.BETA) {
await this.sendMessage("BETA", "HANDOFF", {
type: "SUBTASK_ASSIGNMENT",
tasks: result.delegation.BETA,
});
}
}
return result;
} catch {
return { decomposition: [response], confidence: 0.5 };
}
}
private async validateSuccess(task: TaskDefinition, solutions: BlackboardEntry[]): Promise<any> {
this.log("Validating task success...");
const response = await this.callLLM(
`You are Agent GAMMA. Validate that the task has been successfully completed.
Output JSON: {
"success": true/false,
"criteria_met": [...],
"criteria_unmet": [...],
"final_assessment": "...",
"recommendations": [...],
"confidence": 0.0-1.0
}`,
`Task: ${task.objective}\nSuccess criteria: ${task.success_criteria.join(", ")}\nSolutions: ${JSON.stringify(solutions)}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
return match ? JSON.parse(match[0]) : { success: false, final_assessment: response };
} catch {
return { success: false, final_assessment: response };
}
}
private async generalMediation(task: TaskDefinition, problem: BlackboardEntry[], solutions: BlackboardEntry[]): Promise<any> {
this.log("General mediation...");
const response = await this.callLLM(
`You are Agent GAMMA, a general mediator. Help coordinate the other agents toward a solution.
Output JSON: {
"assessment": "...",
"recommendations": [...],
"next_steps": [...],
"confidence": 0.0-1.0
}`,
`Task: ${task.objective}\nProblem: ${JSON.stringify(problem)}\nSolutions: ${JSON.stringify(solutions)}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
return match ? JSON.parse(match[0]) : { assessment: response, confidence: 0.5 };
} catch {
return { assessment: response, confidence: 0.5 };
}
}
private async driveConsensus(resolution: any): Promise<any> {
this.log("Driving to consensus...");
// Check existing votes
const synthesisVotes = await this.blackboard.getVotes("synthesis");
const acceptVotes = synthesisVotes.filter(v => v.vote === "ACCEPT");
const response = await this.callLLM(
`You are Agent GAMMA. Based on the resolution and votes, determine if consensus is achieved.
Output JSON: {
"achieved": true/false,
"reasoning": "...",
"final_decision": "...",
"dissenting_views": [...],
"action_items": [...]
}`,
`Resolution: ${JSON.stringify(resolution)}\nVotes: ${JSON.stringify(synthesisVotes)}\nAccept count: ${acceptVotes.length}`
);
try {
const match = response.match(/\{[\s\S]*\}/);
return match ? JSON.parse(match[0]) : { achieved: acceptVotes.length >= 2, reasoning: response };
} catch {
return { achieved: acceptVotes.length >= 2, reasoning: response };
}
}
}
export { BaseAgent };
+108
View File
@@ -0,0 +1,108 @@
{
"lockfileVersion": 1,
"configVersion": 1,
"workspaces": {
"": {
"name": "multi-agent-coordination",
"dependencies": {
"openai": "^4.0.0",
"redis": "^4.6.0",
},
},
},
"packages": {
"@redis/bloom": ["@redis/bloom@1.2.0", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg=="],
"@redis/client": ["@redis/client@1.6.1", "", { "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" } }, "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw=="],
"@redis/graph": ["@redis/graph@1.1.1", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw=="],
"@redis/json": ["@redis/json@1.0.7", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ=="],
"@redis/search": ["@redis/search@1.2.0", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw=="],
"@redis/time-series": ["@redis/time-series@1.1.0", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g=="],
"@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
"@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
"cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"generic-pool": ["generic-pool@3.9.0", "", {}, "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g=="],
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"openai": ["openai@4.104.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA=="],
"redis": ["redis@4.7.1", "", { "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.1", "@redis/graph": "1.1.1", "@redis/json": "1.0.7", "@redis/search": "1.2.0", "@redis/time-series": "1.1.0" } }, "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ=="],
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
"web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
}
}
+500
View File
@@ -0,0 +1,500 @@
/**
* Multi-Agent Coordination System - Blackboard & Messaging
*/
import { createClient, RedisClientType } from "redis";
import { $ } from "bun";
import type {
AgentRole,
AgentMessage,
BlackboardEntry,
BlackboardSection,
AgentState,
CoordinationMetrics,
SpawnCondition,
ConsensusVote,
} from "./types";
function now(): string {
return new Date().toISOString();
}
function generateId(): string {
return Math.random().toString(36).slice(2, 10) + Date.now().toString(36);
}
async function getVaultSecret(path: string): Promise<Record<string, any>> {
const initKeys = await Bun.file("/opt/vault/init-keys.json").json();
const token = initKeys.root_token;
const result = await $`curl -sk -H "X-Vault-Token: ${token}" https://127.0.0.1:8200/v1/secret/data/${path}`.json();
return result.data.data;
}
// =============================================================================
// Blackboard System - Shared Memory for Agent Coordination
// =============================================================================
export class Blackboard {
private redis!: RedisClientType;
private taskId: string;
private versionCounters: Map<string, number> = new Map();
constructor(taskId: string) {
this.taskId = taskId;
}
async connect(): Promise<void> {
const creds = await getVaultSecret("services/dragonfly");
this.redis = createClient({
url: "redis://" + creds.host + ":" + creds.port,
password: creds.password,
});
await this.redis.connect();
}
async disconnect(): Promise<void> {
await this.redis.quit();
}
private key(section: BlackboardSection, entryKey: string): string {
return `blackboard:${this.taskId}:${section}:${entryKey}`;
}
private sectionKey(section: BlackboardSection): string {
return `blackboard:${this.taskId}:${section}`;
}
async write(section: BlackboardSection, entryKey: string, value: any, author: AgentRole): Promise<BlackboardEntry> {
const fullKey = this.key(section, entryKey);
const currentVersion = this.versionCounters.get(fullKey) || 0;
const newVersion = currentVersion + 1;
this.versionCounters.set(fullKey, newVersion);
const entry: BlackboardEntry = {
section,
key: entryKey,
value,
author,
version: newVersion,
timestamp: now(),
};
await this.redis.hSet(this.sectionKey(section), entryKey, JSON.stringify(entry));
await this.redis.rPush(`blackboard:${this.taskId}:history`, JSON.stringify({
action: "WRITE",
...entry,
}));
// Increment metrics
await this.redis.hIncrBy(`metrics:${this.taskId}`, "blackboard_writes", 1);
return entry;
}
async read(section: BlackboardSection, entryKey: string): Promise<BlackboardEntry | null> {
const data = await this.redis.hGet(this.sectionKey(section), entryKey);
if (!data) return null;
await this.redis.hIncrBy(`metrics:${this.taskId}`, "blackboard_reads", 1);
return JSON.parse(data);
}
async readSection(section: BlackboardSection): Promise<BlackboardEntry[]> {
const data = await this.redis.hGetAll(this.sectionKey(section));
const entries: BlackboardEntry[] = [];
for (const value of Object.values(data)) {
entries.push(JSON.parse(value));
}
await this.redis.hIncrBy(`metrics:${this.taskId}`, "blackboard_reads", entries.length);
return entries;
}
async getHistory(limit: number = 100): Promise<any[]> {
const data = await this.redis.lRange(`blackboard:${this.taskId}:history`, -limit, -1);
return data.map(d => JSON.parse(d));
}
// Consensus tracking
async recordVote(vote: ConsensusVote): Promise<void> {
await this.redis.rPush(`blackboard:${this.taskId}:votes:${vote.proposal_id}`, JSON.stringify(vote));
await this.write("consensus", vote.proposal_id + ":" + vote.agent, vote, vote.agent);
}
async getVotes(proposalId: string): Promise<ConsensusVote[]> {
const data = await this.redis.lRange(`blackboard:${this.taskId}:votes:${proposalId}`, 0, -1);
return data.map(d => JSON.parse(d));
}
async checkConsensus(proposalId: string, requiredAgents: AgentRole[]): Promise<{ reached: boolean; votes: ConsensusVote[] }> {
const votes = await this.getVotes(proposalId);
const acceptVotes = votes.filter(v => v.vote === "ACCEPT");
const rejectVotes = votes.filter(v => v.vote === "REJECT");
// Consensus requires majority accept and no rejects from required agents
const hasAllRequired = requiredAgents.every(agent =>
votes.some(v => v.agent === agent)
);
const reached = hasAllRequired &&
acceptVotes.length > rejectVotes.length &&
rejectVotes.length === 0;
return { reached, votes };
}
}
// =============================================================================
// Direct Messaging System - Point-to-Point Communication
// =============================================================================
export class MessageBus {
private redis!: RedisClientType;
private subscriber!: RedisClientType;
private taskId: string;
private agentRole: AgentRole;
private messageHandlers: Map<string, (msg: AgentMessage) => void> = new Map();
constructor(taskId: string, agentRole: AgentRole) {
this.taskId = taskId;
this.agentRole = agentRole;
}
async connect(): Promise<void> {
const creds = await getVaultSecret("services/dragonfly");
const config = {
url: "redis://" + creds.host + ":" + creds.port,
password: creds.password,
};
this.redis = createClient(config);
this.subscriber = createClient(config);
await this.redis.connect();
await this.subscriber.connect();
// Subscribe to direct messages and broadcast
await this.subscriber.subscribe(`msg:${this.taskId}:${this.agentRole}`, (message) => {
this.handleMessage(JSON.parse(message));
});
await this.subscriber.subscribe(`msg:${this.taskId}:ALL`, (message) => {
this.handleMessage(JSON.parse(message));
});
}
async disconnect(): Promise<void> {
await this.subscriber.unsubscribe();
await this.subscriber.quit();
await this.redis.quit();
}
private handleMessage(msg: AgentMessage): void {
// Store in message log
this.redis.rPush(`msg:${this.taskId}:log`, JSON.stringify(msg));
this.redis.hIncrBy(`metrics:${this.taskId}`, "total_messages", 1);
this.redis.hIncrBy(`metrics:${this.taskId}`, "direct_messages", 1);
// Call registered handlers
for (const handler of this.messageHandlers.values()) {
try {
handler(msg);
} catch (e) {
console.error("Message handler error:", e);
}
}
}
onMessage(handlerId: string, handler: (msg: AgentMessage) => void): void {
this.messageHandlers.set(handlerId, handler);
}
removeHandler(handlerId: string): void {
this.messageHandlers.delete(handlerId);
}
async send(to: AgentRole | "ALL", type: AgentMessage["type"], payload: any, correlationId?: string): Promise<AgentMessage> {
const msg: AgentMessage = {
id: generateId(),
from: this.agentRole,
to,
type,
payload,
timestamp: now(),
correlation_id: correlationId,
};
await this.redis.publish(`msg:${this.taskId}:${to}`, JSON.stringify(msg));
await this.redis.rPush(`msg:${this.taskId}:log`, JSON.stringify(msg));
await this.redis.hIncrBy(`metrics:${this.taskId}`, "total_messages", 1);
await this.redis.hIncrBy(`metrics:${this.taskId}`, "direct_messages", 1);
return msg;
}
async getMessageLog(limit: number = 100): Promise<AgentMessage[]> {
const data = await this.redis.lRange(`msg:${this.taskId}:log`, -limit, -1);
return data.map(d => JSON.parse(d));
}
}
// =============================================================================
// Agent State Manager
// =============================================================================
export class AgentStateManager {
private redis!: RedisClientType;
private taskId: string;
constructor(taskId: string) {
this.taskId = taskId;
}
async connect(): Promise<void> {
const creds = await getVaultSecret("services/dragonfly");
this.redis = createClient({
url: "redis://" + creds.host + ":" + creds.port,
password: creds.password,
});
await this.redis.connect();
}
async disconnect(): Promise<void> {
await this.redis.quit();
}
async updateState(state: AgentState): Promise<void> {
state.last_activity = now();
await this.redis.hSet(`agents:${this.taskId}`, state.role, JSON.stringify(state));
}
async getState(role: AgentRole): Promise<AgentState | null> {
const data = await this.redis.hGet(`agents:${this.taskId}`, role);
return data ? JSON.parse(data) : null;
}
async getAllStates(): Promise<AgentState[]> {
const data = await this.redis.hGetAll(`agents:${this.taskId}`);
return Object.values(data).map(d => JSON.parse(d));
}
async isBlocked(role: AgentRole, thresholdSeconds: number): Promise<boolean> {
const state = await this.getState(role);
if (!state || !state.blocked_since) return false;
const blockedDuration = (Date.now() - new Date(state.blocked_since).getTime()) / 1000;
return blockedDuration > thresholdSeconds;
}
async detectStuckAgents(thresholdSeconds: number): Promise<AgentRole[]> {
const states = await this.getAllStates();
const stuckAgents: AgentRole[] = [];
for (const state of states) {
if (state.status === "COMPLETED" || state.status === "FAILED") continue;
const lastActivity = new Date(state.last_activity).getTime();
const inactivitySeconds = (Date.now() - lastActivity) / 1000;
if (inactivitySeconds > thresholdSeconds) {
stuckAgents.push(state.role);
}
}
return stuckAgents;
}
}
// =============================================================================
// Spawn Controller - Manages Conditional Agent Spawning
// =============================================================================
export class SpawnController {
private redis!: RedisClientType;
private taskId: string;
private conditions: SpawnCondition[] = [];
private gammaSpawned: boolean = false;
constructor(taskId: string) {
this.taskId = taskId;
}
async connect(): Promise<void> {
const creds = await getVaultSecret("services/dragonfly");
this.redis = createClient({
url: "redis://" + creds.host + ":" + creds.port,
password: creds.password,
});
await this.redis.connect();
// Initialize default spawn conditions
this.conditions = [
{
type: "STUCK",
threshold: 30, // seconds
current_value: 0,
triggered: false,
description: "Spawn GAMMA when agents are stuck for 30+ seconds",
},
{
type: "CONFLICT",
threshold: 3, // conflicts
current_value: 0,
triggered: false,
description: "Spawn GAMMA when 3+ unresolved conflicts detected",
},
{
type: "COMPLEXITY",
threshold: 0.8, // complexity score
current_value: 0,
triggered: false,
description: "Spawn GAMMA when task complexity exceeds 0.8",
},
{
type: "SUCCESS",
threshold: 1.0, // completion
current_value: 0,
triggered: false,
description: "Spawn GAMMA to validate and finalize when task complete",
},
];
}
async disconnect(): Promise<void> {
await this.redis.quit();
}
async updateCondition(type: SpawnCondition["type"], value: number): Promise<SpawnCondition | null> {
const condition = this.conditions.find(c => c.type === type);
if (!condition) return null;
condition.current_value = value;
condition.triggered = value >= condition.threshold;
await this.redis.hSet(`spawn:${this.taskId}:conditions`, type, JSON.stringify(condition));
return condition;
}
async checkSpawnConditions(): Promise<{ shouldSpawn: boolean; reason: SpawnCondition | null }> {
if (this.gammaSpawned) {
return { shouldSpawn: false, reason: null };
}
for (const condition of this.conditions) {
if (condition.triggered) {
return { shouldSpawn: true, reason: condition };
}
}
return { shouldSpawn: false, reason: null };
}
async markGammaSpawned(reason: SpawnCondition): Promise<void> {
this.gammaSpawned = true;
await this.redis.hSet(`metrics:${this.taskId}`, "gamma_spawned", "true");
await this.redis.hSet(`metrics:${this.taskId}`, "gamma_spawn_reason", reason.type);
await this.redis.hSet(`metrics:${this.taskId}`, "gamma_spawn_time", now());
}
isGammaSpawned(): boolean {
return this.gammaSpawned;
}
getConditions(): SpawnCondition[] {
return this.conditions;
}
}
// =============================================================================
// Metrics Collector
// =============================================================================
export class MetricsCollector {
private redis!: RedisClientType;
private taskId: string;
private startTime: number;
constructor(taskId: string) {
this.taskId = taskId;
this.startTime = Date.now();
}
async connect(): Promise<void> {
const creds = await getVaultSecret("services/dragonfly");
this.redis = createClient({
url: "redis://" + creds.host + ":" + creds.port,
password: creds.password,
});
await this.redis.connect();
await this.redis.hSet(`metrics:${this.taskId}`, {
task_id: this.taskId,
start_time: now(),
total_messages: "0",
direct_messages: "0",
blackboard_writes: "0",
blackboard_reads: "0",
conflicts_detected: "0",
conflicts_resolved: "0",
gamma_spawned: "false",
final_consensus: "false",
performance_score: "0",
});
}
async disconnect(): Promise<void> {
await this.redis.quit();
}
async increment(metric: string, by: number = 1): Promise<void> {
await this.redis.hIncrBy(`metrics:${this.taskId}`, metric, by);
}
async set(metric: string, value: string): Promise<void> {
await this.redis.hSet(`metrics:${this.taskId}`, metric, value);
}
async getMetrics(): Promise<CoordinationMetrics> {
const data = await this.redis.hGetAll(`metrics:${this.taskId}`);
return {
task_id: this.taskId,
start_time: data.start_time || now(),
end_time: data.end_time,
total_messages: parseInt(data.total_messages || "0"),
direct_messages: parseInt(data.direct_messages || "0"),
blackboard_writes: parseInt(data.blackboard_writes || "0"),
blackboard_reads: parseInt(data.blackboard_reads || "0"),
conflicts_detected: parseInt(data.conflicts_detected || "0"),
conflicts_resolved: parseInt(data.conflicts_resolved || "0"),
gamma_spawned: data.gamma_spawned === "true",
gamma_spawn_reason: data.gamma_spawn_reason,
gamma_spawn_time: data.gamma_spawn_time,
final_consensus: data.final_consensus === "true",
performance_score: parseFloat(data.performance_score || "0"),
};
}
async finalize(consensus: boolean): Promise<CoordinationMetrics> {
const endTime = now();
const elapsedMs = Date.now() - this.startTime;
await this.redis.hSet(`metrics:${this.taskId}`, "end_time", endTime);
await this.redis.hSet(`metrics:${this.taskId}`, "final_consensus", consensus ? "true" : "false");
// Calculate performance score
const metrics = await this.getMetrics();
const messageEfficiency = Math.max(0, 1 - (metrics.total_messages / 100));
const conflictPenalty = metrics.conflicts_detected * 0.1;
const timePenalty = Math.min(0.5, elapsedMs / 120000);
const consensusBonus = consensus ? 0.2 : 0;
const gammaBonus = metrics.gamma_spawned && consensus ? 0.1 : (metrics.gamma_spawned ? -0.1 : 0);
const score = Math.max(0, Math.min(1,
0.5 + messageEfficiency * 0.2 - conflictPenalty - timePenalty + consensusBonus + gammaBonus
));
await this.redis.hSet(`metrics:${this.taskId}`, "performance_score", score.toFixed(3));
return this.getMetrics();
}
}
+410
View File
@@ -0,0 +1,410 @@
/**
* Multi-Agent Coordination System - Orchestrator
* Manages parallel agent execution, spawn conditions, and metrics
*/
import type { TaskDefinition, CoordinationMetrics, SpawnCondition, AgentRole } from "./types";
import {
Blackboard,
MessageBus,
AgentStateManager,
SpawnController,
MetricsCollector,
} from "./coordination";
import { AgentAlpha, AgentBeta, AgentGamma } from "./agents";
function now(): string {
return new Date().toISOString();
}
function generateId(): string {
return "task-" + Math.random().toString(36).slice(2, 8) + "-" + Date.now().toString(36);
}
// =============================================================================
// Multi-Agent Orchestrator
// =============================================================================
export class MultiAgentOrchestrator {
private taskId: string;
private blackboard!: Blackboard;
private stateManager!: AgentStateManager;
private spawnController!: SpawnController;
private metrics!: MetricsCollector;
private alphaAgent!: AgentAlpha;
private betaAgent!: AgentBeta;
private gammaAgent?: AgentGamma;
private alphaBus!: MessageBus;
private betaBus!: MessageBus;
private gammaBus?: MessageBus;
private model: string;
private startTime!: number;
private monitorInterval?: ReturnType<typeof setInterval>;
constructor(model: string = "anthropic/claude-sonnet-4") {
this.taskId = generateId();
this.model = model;
}
private log(msg: string) {
const elapsed = this.startTime ? ((Date.now() - this.startTime) / 1000).toFixed(1) : "0.0";
console.log(`[${elapsed}s] [ORCHESTRATOR] ${msg}`);
}
async initialize(): Promise<void> {
this.startTime = Date.now();
console.log("\n" + "=".repeat(70));
console.log("MULTI-AGENT COORDINATION SYSTEM");
console.log("Task ID: " + this.taskId);
console.log("Model: " + this.model);
console.log("=".repeat(70) + "\n");
this.log("Initializing coordination infrastructure...");
// Initialize shared infrastructure
this.blackboard = new Blackboard(this.taskId);
this.stateManager = new AgentStateManager(this.taskId);
this.spawnController = new SpawnController(this.taskId);
this.metrics = new MetricsCollector(this.taskId);
await Promise.all([
this.blackboard.connect(),
this.stateManager.connect(),
this.spawnController.connect(),
this.metrics.connect(),
]);
this.log("Infrastructure connected");
// Initialize message buses for ALPHA and BETA
this.alphaBus = new MessageBus(this.taskId, "ALPHA");
this.betaBus = new MessageBus(this.taskId, "BETA");
await Promise.all([
this.alphaBus.connect(),
this.betaBus.connect(),
]);
this.log("Message buses connected");
// Create initial agents
this.alphaAgent = new AgentAlpha(
this.taskId, this.blackboard, this.alphaBus, this.stateManager, this.metrics, this.model
);
this.betaAgent = new AgentBeta(
this.taskId, this.blackboard, this.betaBus, this.stateManager, this.metrics, this.model
);
await Promise.all([
this.alphaAgent.init(),
this.betaAgent.init(),
]);
this.log("Agents ALPHA and BETA initialized");
}
async spawnGamma(reason: SpawnCondition): Promise<void> {
if (this.gammaAgent) {
this.log("GAMMA already spawned, skipping");
return;
}
this.log(`SPAWNING GAMMA - Reason: ${reason.type} (threshold: ${reason.threshold}, current: ${reason.current_value})`);
// Create message bus for GAMMA
this.gammaBus = new MessageBus(this.taskId, "GAMMA");
await this.gammaBus.connect();
// Create and initialize GAMMA agent
this.gammaAgent = new AgentGamma(
this.taskId, this.blackboard, this.gammaBus, this.stateManager, this.metrics,
reason.type, this.model
);
await this.gammaAgent.init();
await this.spawnController.markGammaSpawned(reason);
this.log("GAMMA agent spawned and initialized");
}
private async monitorConditions(): Promise<void> {
// Check stuck condition
const stuckAgents = await this.stateManager.detectStuckAgents(30);
if (stuckAgents.length > 0) {
this.log(`Stuck agents detected: ${stuckAgents.join(", ")}`);
const condition = await this.spawnController.updateCondition("STUCK", stuckAgents.length);
if (condition?.triggered) {
const { shouldSpawn, reason } = await this.spawnController.checkSpawnConditions();
if (shouldSpawn && reason) {
await this.spawnGamma(reason);
}
}
}
// Check conflict condition
const metricsData = await this.metrics.getMetrics();
const unresolvedConflicts = metricsData.conflicts_detected - metricsData.conflicts_resolved;
const conflictCondition = await this.spawnController.updateCondition("CONFLICT", unresolvedConflicts);
if (conflictCondition?.triggered && !this.spawnController.isGammaSpawned()) {
const { shouldSpawn, reason } = await this.spawnController.checkSpawnConditions();
if (shouldSpawn && reason) {
await this.spawnGamma(reason);
}
}
// Check complexity condition (from blackboard)
const analysis = await this.blackboard.read("problem", "analysis");
if (analysis?.value?.complexity_score) {
const complexityCondition = await this.spawnController.updateCondition("COMPLEXITY", analysis.value.complexity_score);
if (complexityCondition?.triggered && !this.spawnController.isGammaSpawned()) {
const { shouldSpawn, reason } = await this.spawnController.checkSpawnConditions();
if (shouldSpawn && reason) {
await this.spawnGamma(reason);
}
}
}
// Log current state
const states = await this.stateManager.getAllStates();
const statesSummary = states.map(s => `${s.role}:${s.status}(${(s.progress * 100).toFixed(0)}%)`).join(", ");
this.log(`Status: ${statesSummary} | Messages: ${metricsData.total_messages} | Conflicts: ${unresolvedConflicts}`);
}
async runTask(task: TaskDefinition): Promise<CoordinationMetrics> {
this.log(`Starting task: ${task.objective.slice(0, 60)}...`);
// Write task to blackboard
await this.blackboard.write("problem", "task_definition", task, "ALPHA");
// Start monitoring
this.monitorInterval = setInterval(() => this.monitorConditions(), 2000);
// Run agents in parallel
this.log("Launching ALPHA and BETA in parallel...");
const alphaPromise = this.alphaAgent.run(task).catch(e => {
this.log(`ALPHA error: ${e.message}`);
});
const betaPromise = this.betaAgent.run(task).catch(e => {
this.log(`BETA error: ${e.message}`);
});
// Wait for initial agents to complete (or timeout)
const timeout = task.timeout_seconds * 1000;
const timeoutPromise = new Promise<void>(resolve => setTimeout(resolve, timeout));
await Promise.race([
Promise.all([alphaPromise, betaPromise]),
timeoutPromise,
]);
this.log("Initial agents completed or timeout reached");
// Check if GAMMA needs to be spawned for success validation
const states = await this.stateManager.getAllStates();
const bothComplete = states.every(s => s.status === "WAITING" || s.status === "COMPLETED");
if (bothComplete && !this.spawnController.isGammaSpawned()) {
await this.spawnController.updateCondition("SUCCESS", 1.0);
const { shouldSpawn, reason } = await this.spawnController.checkSpawnConditions();
if (shouldSpawn && reason) {
await this.spawnGamma(reason);
}
}
// If GAMMA was spawned, run it
if (this.gammaAgent) {
this.log("Running GAMMA for resolution...");
await this.gammaAgent.run(task).catch(e => {
this.log(`GAMMA error: ${e.message}`);
});
}
// Stop monitoring
if (this.monitorInterval) {
clearInterval(this.monitorInterval);
}
// Check consensus
const consensus = await this.blackboard.checkConsensus("synthesis", ["ALPHA", "BETA"]);
const consensusAchieved = consensus.reached ||
(await this.blackboard.read("consensus", "final"))?.value?.achieved === true;
this.log(`Consensus achieved: ${consensusAchieved}`);
// Finalize metrics
const finalMetrics = await this.metrics.finalize(consensusAchieved);
return finalMetrics;
}
async cleanup(): Promise<void> {
this.log("Cleaning up...");
if (this.monitorInterval) {
clearInterval(this.monitorInterval);
}
await Promise.all([
this.alphaBus?.disconnect(),
this.betaBus?.disconnect(),
this.gammaBus?.disconnect(),
this.blackboard?.disconnect(),
this.stateManager?.disconnect(),
this.spawnController?.disconnect(),
this.metrics?.disconnect(),
].filter(Boolean));
this.log("Cleanup complete");
}
getTaskId(): string {
return this.taskId;
}
}
// =============================================================================
// Performance Analysis
// =============================================================================
export function analyzePerformance(metrics: CoordinationMetrics): void {
console.log("\n" + "=".repeat(70));
console.log("PERFORMANCE ANALYSIS");
console.log("=".repeat(70));
const duration = metrics.end_time
? (new Date(metrics.end_time).getTime() - new Date(metrics.start_time).getTime()) / 1000
: 0;
console.log("\nTiming:");
console.log(` Duration: ${duration.toFixed(1)}s`);
console.log("\nCommunication:");
console.log(` Total messages: ${metrics.total_messages}`);
console.log(` Direct messages: ${metrics.direct_messages}`);
console.log(` Blackboard writes: ${metrics.blackboard_writes}`);
console.log(` Blackboard reads: ${metrics.blackboard_reads}`);
console.log(` Communication overhead: ${((metrics.total_messages + metrics.blackboard_writes) / duration).toFixed(2)} ops/sec`);
console.log("\nCoordination:");
console.log(` Conflicts detected: ${metrics.conflicts_detected}`);
console.log(` Conflicts resolved: ${metrics.conflicts_resolved}`);
console.log(` Conflict resolution rate: ${metrics.conflicts_detected > 0 ? ((metrics.conflicts_resolved / metrics.conflicts_detected) * 100).toFixed(1) : 100}%`);
console.log("\nGamma Agent:");
console.log(` Spawned: ${metrics.gamma_spawned ? "Yes" : "No"}`);
if (metrics.gamma_spawned) {
console.log(` Spawn reason: ${metrics.gamma_spawn_reason}`);
console.log(` Spawn time: ${metrics.gamma_spawn_time}`);
}
console.log("\nOutcome:");
console.log(` Consensus achieved: ${metrics.final_consensus ? "Yes" : "No"}`);
console.log(` Performance score: ${(metrics.performance_score * 100).toFixed(1)}%`);
// Threshold analysis
console.log("\nThreshold Effects:");
const messageThreshold = 50;
const conflictThreshold = 3;
if (metrics.total_messages > messageThreshold) {
console.log(` ! High message volume (${metrics.total_messages} > ${messageThreshold}) - potential coordination overhead`);
} else {
console.log(` + Message volume within threshold (${metrics.total_messages} <= ${messageThreshold})`);
}
if (metrics.conflicts_detected > conflictThreshold) {
console.log(` ! High conflict rate (${metrics.conflicts_detected} > ${conflictThreshold}) - agents may have divergent strategies`);
} else {
console.log(` + Conflict rate within threshold (${metrics.conflicts_detected} <= ${conflictThreshold})`);
}
if (metrics.gamma_spawned && metrics.gamma_spawn_reason === "STUCK") {
console.log(` ! Gamma spawned due to stuck condition - consider adjusting agent strategies`);
}
console.log("\n" + "=".repeat(70));
}
// =============================================================================
// CLI Entry Point
// =============================================================================
async function main() {
const args = process.argv.slice(2);
// Default complex task
let objective = args[0] || `Design a distributed event-driven architecture for a real-time analytics platform that handles:
1) High-throughput data ingestion from multiple sources
2) Stream processing with exactly-once semantics
3) Real-time aggregations and windowed computations
4) Low-latency query serving for dashboards
5) Horizontal scalability to handle 1M events/second
The solution should consider fault tolerance, data consistency, and cost optimization.`;
let model = "anthropic/claude-sonnet-4";
const modelIdx = args.indexOf("--model");
if (modelIdx !== -1 && args[modelIdx + 1]) {
model = args[modelIdx + 1];
}
// Parse timeout
let timeout = 120;
const timeoutIdx = args.indexOf("--timeout");
if (timeoutIdx !== -1 && args[timeoutIdx + 1]) {
timeout = parseInt(args[timeoutIdx + 1]);
}
const task: TaskDefinition = {
task_id: generateId(),
objective,
complexity: "high",
subtasks: [
{ id: "s1", description: "Analyze data ingestion requirements", status: "pending", dependencies: [] },
{ id: "s2", description: "Design stream processing pipeline", status: "pending", dependencies: ["s1"] },
{ id: "s3", description: "Plan storage and query layer", status: "pending", dependencies: ["s1"] },
{ id: "s4", description: "Define scalability strategy", status: "pending", dependencies: ["s2", "s3"] },
{ id: "s5", description: "Integrate fault tolerance mechanisms", status: "pending", dependencies: ["s4"] },
],
constraints: [
"Must use open-source technologies where possible",
"Latency < 100ms for query responses",
"Support for multiple data formats (JSON, Avro, Protobuf)",
"Cost-effective for variable workloads",
],
success_criteria: [
"Complete architecture design with component diagrams",
"Data flow specifications",
"Scalability analysis",
"Fault tolerance mechanisms documented",
"Cost estimation provided",
],
timeout_seconds: timeout,
};
const orchestrator = new MultiAgentOrchestrator(model);
try {
await orchestrator.initialize();
const metrics = await orchestrator.runTask(task);
console.log("\n" + "=".repeat(70));
console.log("FINAL METRICS");
console.log("=".repeat(70));
console.log(JSON.stringify(metrics, null, 2));
analyzePerformance(metrics);
} catch (e: any) {
console.error("Orchestrator error:", e.message);
} finally {
await orchestrator.cleanup();
}
}
main().catch(console.error);
+14
View File
@@ -0,0 +1,14 @@
{
"name": "multi-agent-coordination",
"version": "0.1.0",
"description": "Multi-agent coordination system with parallel execution, shared blackboard, and conditional spawning",
"main": "orchestrator.ts",
"scripts": {
"start": "bun run orchestrator.ts",
"test": "bun run orchestrator.ts --timeout 60"
},
"dependencies": {
"openai": "^4.0.0",
"redis": "^4.6.0"
}
}
+92
View File
@@ -0,0 +1,92 @@
/**
* Multi-Agent Coordination System - Type Definitions
*/
export type AgentRole = "ALPHA" | "BETA" | "GAMMA";
export type AgentStatus = "IDLE" | "WORKING" | "WAITING" | "BLOCKED" | "COMPLETED" | "FAILED";
export type MessageType = "PROPOSAL" | "FEEDBACK" | "QUERY" | "RESPONSE" | "SYNC" | "HANDOFF" | "SPAWN_REQUEST";
export type BlackboardSection = "problem" | "solutions" | "constraints" | "progress" | "conflicts" | "consensus";
export interface AgentMessage {
id: string;
from: AgentRole;
to: AgentRole | "ALL" | "BLACKBOARD";
type: MessageType;
payload: any;
timestamp: string;
correlation_id?: string;
}
export interface BlackboardEntry {
section: BlackboardSection;
key: string;
value: any;
author: AgentRole;
version: number;
timestamp: string;
supersedes?: string;
}
export interface AgentState {
agent_id: string;
role: AgentRole;
status: AgentStatus;
current_task: string;
progress: number;
confidence: number;
last_activity: string;
messages_sent: number;
messages_received: number;
proposals_made: number;
conflicts_detected: number;
blocked_since?: string;
}
export interface CoordinationMetrics {
task_id: string;
start_time: string;
end_time?: string;
total_messages: number;
direct_messages: number;
blackboard_writes: number;
blackboard_reads: number;
conflicts_detected: number;
conflicts_resolved: number;
gamma_spawned: boolean;
gamma_spawn_reason?: string;
gamma_spawn_time?: string;
final_consensus: boolean;
performance_score: number;
}
export interface SpawnCondition {
type: "STUCK" | "CONFLICT" | "COMPLEXITY" | "SUCCESS" | "MANUAL";
threshold: number;
current_value: number;
triggered: boolean;
description: string;
}
export interface TaskDefinition {
task_id: string;
objective: string;
complexity: "low" | "medium" | "high" | "extreme";
subtasks: {
id: string;
description: string;
assigned_to?: AgentRole;
status: "pending" | "in_progress" | "completed" | "blocked";
dependencies: string[];
}[];
constraints: string[];
success_criteria: string[];
timeout_seconds: number;
}
export interface ConsensusVote {
agent: AgentRole;
proposal_id: string;
vote: "ACCEPT" | "REJECT" | "ABSTAIN";
reasoning: string;
timestamp: string;
}