diff --git a/src/coordination/manager.ts b/src/coordination/manager.ts index cb8dc88..8f944de 100644 --- a/src/coordination/manager.ts +++ b/src/coordination/manager.ts @@ -18,6 +18,7 @@ import type { import type { WorktreeManager } from '../git/types.js'; import type { TaskRepository } from '../db/repositories/task-repository.js'; import type { AgentRepository } from '../db/repositories/agent-repository.js'; +import type { MessageRepository } from '../db/repositories/message-repository.js'; import type { CoordinationManager, MergeQueueItem, MergeResult } from './types.js'; // ============================================================================= @@ -55,6 +56,7 @@ export class DefaultCoordinationManager implements CoordinationManager { private worktreeManager?: WorktreeManager, private taskRepository?: TaskRepository, private agentRepository?: AgentRepository, + private messageRepository?: MessageRepository, private eventBus?: EventBus ) {} @@ -257,24 +259,45 @@ export class DefaultCoordinationManager implements CoordinationManager { /** * Handle a merge conflict. - * Creates a conflict-resolution task and assigns back to the agent. + * Creates a conflict-resolution task and notifies the agent via message. */ async handleConflict(taskId: string, conflicts: string[]): Promise { if (!this.taskRepository) { throw new Error('TaskRepository not configured'); } + if (!this.agentRepository) { + throw new Error('AgentRepository not configured'); + } + // Get original task for context const originalTask = await this.taskRepository.findById(taskId); if (!originalTask) { throw new Error(`Original task not found: ${taskId}`); } + // Get agent that was working on the task + const agent = await this.agentRepository.findByTaskId(taskId); + if (!agent) { + throw new Error(`No agent found for task: ${taskId}`); + } + + // Build conflict description + const conflictDescription = [ + 'Merge conflicts detected. Resolve conflicts in the following files:', + '', + ...conflicts.map((f) => `- ${f}`), + '', + `Original task: ${originalTask.name}`, + '', + 'Instructions: Resolve merge conflicts in the listed files, then mark task complete.', + ].join('\n'); + // Create new conflict-resolution task const conflictTask = await this.taskRepository.create({ planId: originalTask.planId, name: `Resolve conflicts: ${originalTask.name}`, - description: `Merge conflicts detected. Resolve conflicts in the following files:\n\n${conflicts.map((f) => `- ${f}`).join('\n')}\n\nOriginal task: ${originalTask.name}\n\nInstructions: Resolve merge conflicts in the listed files, then mark task complete.`, + description: conflictDescription, type: 'auto', priority: 'high', // Conflicts should be resolved quickly status: 'pending', @@ -284,6 +307,30 @@ export class DefaultCoordinationManager implements CoordinationManager { // Update original task status to blocked await this.taskRepository.update(taskId, { status: 'blocked' }); + // Create message to agent if messageRepository is configured + if (this.messageRepository) { + const messageContent = [ + `Merge conflict detected for task: ${originalTask.name}`, + '', + 'Conflicting files:', + ...conflicts.map((f) => `- ${f}`), + '', + `A new task has been created to resolve these conflicts: ${conflictTask.name}`, + '', + 'Please resolve the merge conflicts in the listed files and mark the resolution task as complete.', + ].join('\n'); + + await this.messageRepository.create({ + senderType: 'user', // System-generated messages appear as from user + senderId: null, + recipientType: 'agent', + recipientId: agent.id, + type: 'info', + content: messageContent, + requiresResponse: false, + }); + } + // Emit TaskQueuedEvent for the new conflict-resolution task const event: TaskQueuedEvent = { type: 'task:queued',