fix: resolve errand worktree path for sendMessage instead of using agent-workdirs

Errand agents are spawned in the errand's git worktree (managed by
SimpleGitWorktreeManager), not in agent-workdirs/<alias>/.
sendUserMessage was deriving the cwd from worktreeId which pointed to
the non-existent agent-workdirs path. Now the errand.sendMessage
procedure resolves the actual worktree path and passes it through.
This commit is contained in:
Lukas May
2026-03-06 23:11:55 +01:00
parent bc61e658dc
commit b17c0a2b4f
4 changed files with 21 additions and 5 deletions

View File

@@ -667,7 +667,7 @@ export class MultiProviderAgentManager implements AgentManager {
* Does not use the conversations table — the message is injected directly * Does not use the conversations table — the message is injected directly
* as the next resume prompt for the agent's Claude Code session. * as the next resume prompt for the agent's Claude Code session.
*/ */
async sendUserMessage(agentId: string, message: string): Promise<void> { async sendUserMessage(agentId: string, message: string, cwd?: string): Promise<void> {
const agent = await this.repository.findById(agentId); const agent = await this.repository.findById(agentId);
if (!agent) throw new Error(`Agent not found: ${agentId}`); if (!agent) throw new Error(`Agent not found: ${agentId}`);
@@ -682,7 +682,7 @@ export class MultiProviderAgentManager implements AgentManager {
const provider = getProvider(agent.provider); const provider = getProvider(agent.provider);
if (!provider) throw new Error(`Unknown provider: ${agent.provider}`); if (!provider) throw new Error(`Unknown provider: ${agent.provider}`);
const agentCwd = this.processManager.getAgentWorkdir(agent.worktreeId); const agentCwd = cwd ?? this.processManager.getAgentWorkdir(agent.worktreeId);
// Clear previous signal.json // Clear previous signal.json
const signalPath = join(agentCwd, '.cw/output/signal.json'); const signalPath = join(agentCwd, '.cw/output/signal.json');

View File

@@ -534,7 +534,7 @@ export class MockAgentManager implements AgentManager {
* Deliver a user message to a running errand agent. * Deliver a user message to a running errand agent.
* Mock implementation: no-op (simulates message delivery without actual process interaction). * Mock implementation: no-op (simulates message delivery without actual process interaction).
*/ */
async sendUserMessage(agentId: string, _message: string): Promise<void> { async sendUserMessage(agentId: string, _message: string, _cwd?: string): Promise<void> {
const record = this.agents.get(agentId); const record = this.agents.get(agentId);
if (!record) { if (!record) {
throw new Error(`Agent '${agentId}' not found`); throw new Error(`Agent '${agentId}' not found`);

View File

@@ -272,5 +272,5 @@ export interface AgentManager {
* @param agentId - The errand agent to message * @param agentId - The errand agent to message
* @param message - The user's message text * @param message - The user's message text
*/ */
sendUserMessage(agentId: string, message: string): Promise<void>; sendUserMessage(agentId: string, message: string, cwd?: string): Promise<void>;
} }

View File

@@ -397,7 +397,23 @@ export function errandProcedures(publicProcedure: ProcedureBuilder) {
}); });
} }
await agentManager.sendUserMessage(errand.agentId, input.message); // Resolve errand worktree path — errand agents don't use agent-workdirs/
let worktreePath: string | undefined;
if (errand.projectId) {
const project = await requireProjectRepository(ctx).findById(errand.projectId);
if (project) {
try {
const clonePath = await resolveClonePath(project, ctx);
const wm = new SimpleGitWorktreeManager(clonePath);
const wt = await wm.get(errand.id);
if (wt) worktreePath = wt.path;
} catch {
// Fall through — sendUserMessage will use default path
}
}
}
await agentManager.sendUserMessage(errand.agentId, input.message, worktreePath);
return { success: true }; return { success: true };
}), }),