Revert "fix: Spawn chat agent in background to avoid blocking event loop"
This reverts commit 8ee02f6013.
This commit is contained in:
@@ -8,6 +8,8 @@ import type { ProcedureBuilder } from '../trpc.js';
|
|||||||
import {
|
import {
|
||||||
requireAgentManager,
|
requireAgentManager,
|
||||||
requireInitiativeRepository,
|
requireInitiativeRepository,
|
||||||
|
requirePhaseRepository,
|
||||||
|
requirePageRepository,
|
||||||
requireTaskRepository,
|
requireTaskRepository,
|
||||||
requireChatSessionRepository,
|
requireChatSessionRepository,
|
||||||
} from './_helpers.js';
|
} from './_helpers.js';
|
||||||
@@ -81,12 +83,22 @@ export function chatSessionProcedures(publicProcedure: ProcedureBuilder) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather data needed for spawn (fast DB reads) before returning
|
// Spawn fresh agent with chat history + context
|
||||||
const messages = await chatRepo.findMessagesBySessionId(session.id);
|
const messages = await chatRepo.findMessagesBySessionId(session.id);
|
||||||
const chatHistory: ChatHistoryEntry[] = messages
|
const chatHistory: ChatHistoryEntry[] = messages
|
||||||
.slice(-MAX_HISTORY_MESSAGES)
|
.slice(-MAX_HISTORY_MESSAGES)
|
||||||
.map(m => ({ role: m.role as 'user' | 'assistant' | 'system', content: m.content }));
|
.map(m => ({ role: m.role as 'user' | 'assistant' | 'system', content: m.content }));
|
||||||
|
|
||||||
|
const context = await gatherInitiativeContext(
|
||||||
|
ctx.phaseRepository,
|
||||||
|
ctx.taskRepository,
|
||||||
|
ctx.pageRepository,
|
||||||
|
input.initiativeId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const prompt = buildChatPrompt(input.targetType, input.targetId, chatHistory, input.message);
|
||||||
|
|
||||||
|
// Create a task for the chat agent
|
||||||
const targetName = input.targetType === 'phase'
|
const targetName = input.targetType === 'phase'
|
||||||
? (await ctx.phaseRepository?.findById(input.targetId))?.name ?? input.targetId
|
? (await ctx.phaseRepository?.findById(input.targetId))?.name ?? input.targetId
|
||||||
: (await ctx.taskRepository?.findById(input.targetId))?.name ?? input.targetId;
|
: (await ctx.taskRepository?.findById(input.targetId))?.name ?? input.targetId;
|
||||||
@@ -99,68 +111,34 @@ export function chatSessionProcedures(publicProcedure: ProcedureBuilder) {
|
|||||||
status: 'in_progress',
|
status: 'in_progress',
|
||||||
});
|
});
|
||||||
|
|
||||||
const prompt = buildChatPrompt(input.targetType, input.targetId, chatHistory, input.message);
|
// Determine target phase/task for input context
|
||||||
|
const targetPhase = input.targetType === 'phase'
|
||||||
|
? await ctx.phaseRepository?.findById(input.targetId)
|
||||||
|
: undefined;
|
||||||
|
const targetTask = input.targetType === 'task'
|
||||||
|
? await ctx.taskRepository?.findById(input.targetId)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
// Spawn agent in background — worktree creation + file I/O is heavy
|
const agent = await agentManager.spawn({
|
||||||
// and blocks the event loop, causing ECONNREFUSED on the SSE connection.
|
taskId: task.id,
|
||||||
// The UI detects the agent via SSE events (agent:spawned) + query invalidation.
|
prompt,
|
||||||
const sessionId = session.id;
|
mode: 'chat',
|
||||||
void (async () => {
|
provider: input.provider,
|
||||||
try {
|
initiativeId: input.initiativeId,
|
||||||
const context = await gatherInitiativeContext(
|
inputContext: {
|
||||||
ctx.phaseRepository,
|
initiative,
|
||||||
ctx.taskRepository,
|
phase: targetPhase ?? undefined,
|
||||||
ctx.pageRepository,
|
task: targetTask ?? undefined,
|
||||||
input.initiativeId,
|
pages: context.pages.length > 0 ? context.pages : undefined,
|
||||||
);
|
phases: context.phases.length > 0 ? context.phases : undefined,
|
||||||
|
tasks: context.tasks.length > 0 ? context.tasks : undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const targetPhase = input.targetType === 'phase'
|
// Link agent to session
|
||||||
? await ctx.phaseRepository?.findById(input.targetId)
|
await chatRepo.updateSession(session.id, { agentId: agent.id });
|
||||||
: undefined;
|
|
||||||
const targetTask = input.targetType === 'task'
|
|
||||||
? await ctx.taskRepository?.findById(input.targetId)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const agent = await agentManager.spawn({
|
return { sessionId: session.id, agentId: agent.id, action: 'spawned' as const };
|
||||||
taskId: task.id,
|
|
||||||
prompt,
|
|
||||||
mode: 'chat',
|
|
||||||
provider: input.provider,
|
|
||||||
initiativeId: input.initiativeId,
|
|
||||||
inputContext: {
|
|
||||||
initiative,
|
|
||||||
phase: targetPhase ?? undefined,
|
|
||||||
task: targetTask ?? undefined,
|
|
||||||
pages: context.pages.length > 0 ? context.pages : undefined,
|
|
||||||
phases: context.phases.length > 0 ? context.phases : undefined,
|
|
||||||
tasks: context.tasks.length > 0 ? context.tasks : undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await chatRepo.updateSession(sessionId, { agentId: agent.id });
|
|
||||||
|
|
||||||
ctx.eventBus.emit({
|
|
||||||
type: 'chat:message_created' as const,
|
|
||||||
timestamp: new Date(),
|
|
||||||
payload: { chatSessionId: sessionId, role: 'system' as const },
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
const msg = err instanceof Error ? err.message : String(err);
|
|
||||||
// Store error as system message so the UI can display it
|
|
||||||
await chatRepo.createMessage({
|
|
||||||
chatSessionId: sessionId,
|
|
||||||
role: 'system',
|
|
||||||
content: `Failed to start agent: ${msg}`,
|
|
||||||
});
|
|
||||||
ctx.eventBus.emit({
|
|
||||||
type: 'chat:message_created' as const,
|
|
||||||
timestamp: new Date(),
|
|
||||||
payload: { chatSessionId: sessionId, role: 'system' as const },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
return { sessionId: session.id, agentId: null, action: 'spawned' as const };
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
getChatSession: publicProcedure
|
getChatSession: publicProcedure
|
||||||
|
|||||||
Reference in New Issue
Block a user