orchestry-mcp
Version:
Orchestry MCP Server for multi-session task management
534 lines (483 loc) • 16.8 kB
text/typescript
/**
* Role-Based Task Management for Orchestry
*
* Manages task assignments based on LLM session roles and expertise
*/
import { DatabaseManager } from '../database-manager.js';
import { Priority, TaskStatus } from '../../shared/types.js';
// Define roles that LLM sessions can have
export type LLMRole =
| 'backend'
| 'frontend'
| 'fullstack'
| 'devops'
| 'database'
| 'testing'
| 'security'
| 'ui-ux'
| 'architect'
| 'product-manager'
| 'code-reviewer'
| 'documentation';
// Role capabilities and preferred task types
export const ROLE_CAPABILITIES: Record<LLMRole, {
skills: string[];
preferredTasks: string[];
canReview: LLMRole[];
}> = {
backend: {
skills: ['API development', 'Database design', 'Server-side logic', 'Authentication'],
preferredTasks: ['api', 'database', 'auth', 'server', 'integration'],
canReview: ['frontend', 'database'],
},
frontend: {
skills: ['React', 'Vue', 'UI components', 'State management', 'Responsive design'],
preferredTasks: ['ui', 'component', 'form', 'styling', 'ux'],
canReview: ['ui-ux'],
},
fullstack: {
skills: ['End-to-end features', 'API integration', 'Full application flow'],
preferredTasks: ['feature', 'integration', 'api', 'ui'],
canReview: ['backend', 'frontend'],
},
devops: {
skills: ['CI/CD', 'Deployment', 'Infrastructure', 'Monitoring', 'Docker', 'Kubernetes'],
preferredTasks: ['deployment', 'ci', 'infrastructure', 'monitoring', 'docker'],
canReview: ['security'],
},
database: {
skills: ['Schema design', 'Query optimization', 'Migration', 'Data modeling'],
preferredTasks: ['schema', 'migration', 'query', 'index', 'optimization'],
canReview: ['backend'],
},
testing: {
skills: ['Unit tests', 'Integration tests', 'E2E tests', 'Test automation'],
preferredTasks: ['test', 'testing', 'e2e', 'unit-test', 'integration-test'],
canReview: ['fullstack', 'backend', 'frontend'],
},
security: {
skills: ['Security audit', 'Vulnerability assessment', 'Authentication', 'Authorization'],
preferredTasks: ['security', 'auth', 'vulnerability', 'audit', 'encryption'],
canReview: ['backend', 'devops'],
},
'ui-ux': {
skills: ['Design systems', 'User experience', 'Wireframes', 'Prototypes'],
preferredTasks: ['design', 'ux', 'wireframe', 'prototype', 'user-flow'],
canReview: ['frontend'],
},
architect: {
skills: ['System design', 'Architecture patterns', 'Technology selection'],
preferredTasks: ['architecture', 'design', 'pattern', 'structure', 'planning'],
canReview: ['fullstack', 'backend', 'database'],
},
'product-manager': {
skills: ['Requirements', 'User stories', 'Prioritization', 'Roadmap'],
preferredTasks: ['requirements', 'story', 'epic', 'planning', 'specification'],
canReview: ['ui-ux'],
},
'code-reviewer': {
skills: ['Code quality', 'Best practices', 'Performance review', 'Security review'],
preferredTasks: ['review', 'refactor', 'optimization', 'quality'],
canReview: ['backend', 'frontend', 'fullstack'],
},
documentation: {
skills: ['API docs', 'User guides', 'Technical writing', 'README'],
preferredTasks: ['docs', 'documentation', 'readme', 'guide', 'tutorial'],
canReview: [],
},
};
// Priority matrix for automatic prioritization
export const PRIORITY_MATRIX = {
// Task type to base priority mapping
taskTypes: {
security: 'critical',
bug: 'high',
feature: 'medium',
enhancement: 'medium',
documentation: 'low',
refactor: 'low',
},
// Keywords that affect priority
keywords: {
urgent: 'critical',
asap: 'critical',
blocker: 'critical',
critical: 'critical',
important: 'high',
breaking: 'high',
crash: 'high',
security: 'critical',
vulnerability: 'critical',
optimization: 'low',
'nice-to-have': 'low',
},
// Dependencies increase priority
hasDependents: {
many: 'high', // 3+ tasks depend on this
some: 'medium', // 1-2 tasks depend on this
none: null, // No change
},
};
export class RoleBasedTaskManager {
private sessions: Map<string, {
role: LLMRole;
currentLoad: number;
expertise: string[];
availability: 'available' | 'busy' | 'away';
}> = new Map();
constructor(private dbManager: DatabaseManager) {}
/**
* Register a new LLM session with a role
*/
registerSession(sessionId: string, role: LLMRole, expertise?: string[]) {
this.sessions.set(sessionId, {
role,
currentLoad: 0,
expertise: expertise || ROLE_CAPABILITIES[role].skills,
availability: 'available',
});
return {
sessionId,
role,
capabilities: ROLE_CAPABILITIES[role],
};
}
/**
* Automatically assign a task to the best available session
*/
async autoAssignTask(
taskTitle: string,
taskDescription: string,
requiredSkills?: string[]
): Promise<{
assignedTo: string;
role: LLMRole;
priority: Priority;
reason: string;
}> {
// Determine priority automatically
const priority = this.determinePriority(taskTitle, taskDescription);
// Find best match based on task requirements
let bestMatch: { sessionId: string; score: number } | null = null;
for (const [sessionId, session] of this.sessions) {
if (session.availability !== 'available') continue;
// Calculate match score
let score = 0;
// Check if role matches task type
const taskKeywords = (taskTitle + ' ' + taskDescription).toLowerCase();
for (const preferred of ROLE_CAPABILITIES[session.role].preferredTasks) {
if (taskKeywords.includes(preferred)) {
score += 10;
}
}
// Check skill match
if (requiredSkills) {
for (const skill of requiredSkills) {
if (session.expertise.includes(skill)) {
score += 5;
}
}
}
// Factor in current load (prefer less loaded sessions)
score -= session.currentLoad * 2;
if (!bestMatch || score > bestMatch.score) {
bestMatch = { sessionId, score };
}
}
if (!bestMatch) {
// No available session, create a task pool entry
return {
assignedTo: 'pool',
role: 'fullstack' as LLMRole,
priority,
reason: 'No available session. Task added to pool for next available agent.',
};
}
const session = this.sessions.get(bestMatch.sessionId)!;
session.currentLoad++;
return {
assignedTo: bestMatch.sessionId,
role: session.role,
priority,
reason: `Best match based on ${session.role} expertise and availability`,
};
}
/**
* Determine priority based on content analysis
*/
private determinePriority(title: string, description: string): Priority {
const content = (title + ' ' + description).toLowerCase();
// Check for priority keywords
for (const [keyword, priority] of Object.entries(PRIORITY_MATRIX.keywords)) {
if (content.includes(keyword)) {
return priority as Priority;
}
}
// Check task type
for (const [type, priority] of Object.entries(PRIORITY_MATRIX.taskTypes)) {
if (content.includes(type)) {
return priority as Priority;
}
}
// Default to medium
return 'medium';
}
/**
* Get recommended next task for a session based on role
*/
async getNextTask(sessionId: string): Promise<{
task: any;
reason: string;
} | null> {
const session = this.sessions.get(sessionId);
if (!session) return null;
// This would query the database for unassigned tasks
// matching the session's role and expertise
// Prioritizing by priority and dependencies
return {
task: {
title: 'Implement user authentication',
priority: 'high',
estimatedHours: 4,
},
reason: `High priority task matching ${session.role} expertise`,
};
}
/**
* Load balancing - redistribute tasks if a session becomes unavailable
*/
async redistributeTasks(unavailableSessionId: string) {
const session = this.sessions.get(unavailableSessionId);
if (!session) return;
session.availability = 'away';
// Find tasks assigned to this session and reassign them
// This would query the database and reassign based on priority
console.log(`Redistributing tasks from ${unavailableSessionId}`);
}
/**
* Get workload distribution across all sessions
*/
getWorkloadDistribution() {
const distribution: Record<LLMRole, {
sessions: number;
totalLoad: number;
averageLoad: number;
}> = {} as any;
for (const [_, session] of this.sessions) {
if (!distribution[session.role]) {
distribution[session.role] = {
sessions: 0,
totalLoad: 0,
averageLoad: 0,
};
}
distribution[session.role].sessions++;
distribution[session.role].totalLoad += session.currentLoad;
}
// Calculate averages
for (const role in distribution) {
const data = distribution[role as LLMRole];
data.averageLoad = data.totalLoad / data.sessions;
}
return distribution;
}
}
/**
* Enhanced collaboration tools with role-based features
*/
export class EnhancedCollaborationTools {
private taskManager: RoleBasedTaskManager;
constructor(private dbManager: DatabaseManager) {
this.taskManager = new RoleBasedTaskManager(dbManager);
}
getEnhancedTools() {
return [
{
name: 'register_session_role',
description: `Register this LLM session with a specific role and expertise.
This helps with automatic task assignment and workload distribution.
Example: register_session_role("backend", ["Node.js", "PostgreSQL", "REST APIs"])`,
inputSchema: {
type: 'object',
properties: {
role: {
type: 'string',
enum: ['backend', 'frontend', 'fullstack', 'devops', 'database',
'testing', 'security', 'ui-ux', 'architect', 'product-manager',
'code-reviewer', 'documentation'],
description: 'Your primary role in this session',
},
expertise: {
type: 'array',
items: { type: 'string' },
description: 'Specific technologies or areas you can handle',
},
sessionName: { type: 'string', description: 'Optional friendly name for this session' },
},
required: ['role'],
},
},
{
name: 'create_prioritized_task',
description: `Create a task with automatic priority and assignment.
The system will determine priority based on content and assign to the best available session.
Example: create_prioritized_task("Fix security vulnerability in login", "SQL injection risk found")`,
inputSchema: {
type: 'object',
properties: {
title: { type: 'string', description: 'Task title' },
description: { type: 'string', description: 'Detailed task description' },
requiredSkills: {
type: 'array',
items: { type: 'string' },
description: 'Skills needed for this task',
},
explicitPriority: {
type: 'string',
enum: ['low', 'medium', 'high', 'critical'],
description: 'Override automatic priority detection',
},
preferredRole: {
type: 'string',
enum: ['backend', 'frontend', 'fullstack', 'devops', 'database',
'testing', 'security', 'ui-ux', 'architect'],
description: 'Preferred role for this task',
},
dependencies: {
type: 'array',
items: { type: 'string' },
description: 'Task IDs this depends on',
},
estimatedHours: { type: 'number', description: 'Estimated hours to complete' },
},
required: ['title', 'description'],
},
},
{
name: 'claim_next_task',
description: `Get the next appropriate task for your role and expertise.
System will recommend the highest priority task matching your capabilities.
Example: claim_next_task()`,
inputSchema: {
type: 'object',
properties: {
taskType: {
type: 'string',
description: 'Optional: specific type of task you want',
},
},
},
},
{
name: 'update_task_assignment',
description: `Update task assignment and priority.
Example: update_task_assignment("task-123", "session-backend-1", "critical")`,
inputSchema: {
type: 'object',
properties: {
taskId: { type: 'string', description: 'Task ID' },
assignTo: { type: 'string', description: 'Session ID or "pool" for unassigned' },
priority: {
type: 'string',
enum: ['low', 'medium', 'high', 'critical'],
description: 'New priority',
},
reason: { type: 'string', description: 'Reason for reassignment' },
},
required: ['taskId'],
},
},
{
name: 'get_role_workload',
description: `Check workload distribution across all roles.
Helps identify bottlenecks and overloaded roles.
Example: get_role_workload()`,
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'request_role_specific_help',
description: `Request help from a specific role.
Example: request_role_specific_help("database", "Need help optimizing this query", "SELECT * FROM users...")`,
inputSchema: {
type: 'object',
properties: {
targetRole: {
type: 'string',
enum: ['backend', 'frontend', 'fullstack', 'devops', 'database',
'testing', 'security', 'ui-ux', 'architect'],
description: 'Role you need help from',
},
issue: { type: 'string', description: 'What you need help with' },
context: { type: 'string', description: 'Current code or situation' },
priority: {
type: 'string',
enum: ['low', 'medium', 'high', 'critical'],
description: 'How urgent is this',
},
},
required: ['targetRole', 'issue'],
},
},
{
name: 'escalate_task',
description: `Escalate a task to higher priority or different role.
Use when you discover a task is more complex or critical than initially thought.
Example: escalate_task("task-123", "critical", "architect", "Requires system redesign")`,
inputSchema: {
type: 'object',
properties: {
taskId: { type: 'string', description: 'Task to escalate' },
newPriority: {
type: 'string',
enum: ['high', 'critical'],
description: 'New priority level',
},
escalateToRole: {
type: 'string',
enum: ['architect', 'security', 'product-manager'],
description: 'Role to escalate to',
},
reason: { type: 'string', description: 'Why escalation is needed' },
},
required: ['taskId', 'reason'],
},
},
];
}
}
/**
* Priority queue for task management
*/
export class TaskPriorityQueue {
private queues: Map<Priority, any[]> = new Map([
['critical', []],
['high', []],
['medium', []],
['low', []],
]);
addTask(task: any, priority: Priority) {
this.queues.get(priority)?.push(task);
}
getNextTask(): any | null {
// Get highest priority task
for (const priority of ['critical', 'high', 'medium', 'low'] as Priority[]) {
const queue = this.queues.get(priority);
if (queue && queue.length > 0) {
return queue.shift();
}
}
return null;
}
getQueueStatus() {
const status: Record<Priority, number> = {
critical: this.queues.get('critical')?.length || 0,
high: this.queues.get('high')?.length || 0,
medium: this.queues.get('medium')?.length || 0,
low: this.queues.get('low')?.length || 0,
};
return status;
}
}