feat(06-02): enhance conflict bounce-back with agent messaging
Adds MessageRepository to DefaultCoordinationManager for conflict notification: - Creates info message to agent when merge conflict occurs - Message includes conflicting file list and resolution instructions - System-generated messages use senderType='user' with null senderId - Refactors conflict description into cleaner array.join format
This commit is contained in:
@@ -18,6 +18,7 @@ import type {
|
|||||||
import type { WorktreeManager } from '../git/types.js';
|
import type { WorktreeManager } from '../git/types.js';
|
||||||
import type { TaskRepository } from '../db/repositories/task-repository.js';
|
import type { TaskRepository } from '../db/repositories/task-repository.js';
|
||||||
import type { AgentRepository } from '../db/repositories/agent-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';
|
import type { CoordinationManager, MergeQueueItem, MergeResult } from './types.js';
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
@@ -55,6 +56,7 @@ export class DefaultCoordinationManager implements CoordinationManager {
|
|||||||
private worktreeManager?: WorktreeManager,
|
private worktreeManager?: WorktreeManager,
|
||||||
private taskRepository?: TaskRepository,
|
private taskRepository?: TaskRepository,
|
||||||
private agentRepository?: AgentRepository,
|
private agentRepository?: AgentRepository,
|
||||||
|
private messageRepository?: MessageRepository,
|
||||||
private eventBus?: EventBus
|
private eventBus?: EventBus
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -257,24 +259,45 @@ export class DefaultCoordinationManager implements CoordinationManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a merge conflict.
|
* 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<void> {
|
async handleConflict(taskId: string, conflicts: string[]): Promise<void> {
|
||||||
if (!this.taskRepository) {
|
if (!this.taskRepository) {
|
||||||
throw new Error('TaskRepository not configured');
|
throw new Error('TaskRepository not configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.agentRepository) {
|
||||||
|
throw new Error('AgentRepository not configured');
|
||||||
|
}
|
||||||
|
|
||||||
// Get original task for context
|
// Get original task for context
|
||||||
const originalTask = await this.taskRepository.findById(taskId);
|
const originalTask = await this.taskRepository.findById(taskId);
|
||||||
if (!originalTask) {
|
if (!originalTask) {
|
||||||
throw new Error(`Original task not found: ${taskId}`);
|
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
|
// Create new conflict-resolution task
|
||||||
const conflictTask = await this.taskRepository.create({
|
const conflictTask = await this.taskRepository.create({
|
||||||
planId: originalTask.planId,
|
planId: originalTask.planId,
|
||||||
name: `Resolve conflicts: ${originalTask.name}`,
|
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',
|
type: 'auto',
|
||||||
priority: 'high', // Conflicts should be resolved quickly
|
priority: 'high', // Conflicts should be resolved quickly
|
||||||
status: 'pending',
|
status: 'pending',
|
||||||
@@ -284,6 +307,30 @@ export class DefaultCoordinationManager implements CoordinationManager {
|
|||||||
// Update original task status to blocked
|
// Update original task status to blocked
|
||||||
await this.taskRepository.update(taskId, { status: '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
|
// Emit TaskQueuedEvent for the new conflict-resolution task
|
||||||
const event: TaskQueuedEvent = {
|
const event: TaskQueuedEvent = {
|
||||||
type: 'task:queued',
|
type: 'task:queued',
|
||||||
|
|||||||
Reference in New Issue
Block a user