From b17c0a2b4ff1c04d883965f9ae5bc4e49ce8860f Mon Sep 17 00:00:00 2001 From: Lukas May Date: Fri, 6 Mar 2026 23:11:55 +0100 Subject: [PATCH] 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//. 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. --- apps/server/agent/manager.ts | 4 ++-- apps/server/agent/mock-manager.ts | 2 +- apps/server/agent/types.ts | 2 +- apps/server/trpc/routers/errand.ts | 18 +++++++++++++++++- 4 files changed, 21 insertions(+), 5 deletions(-) diff --git a/apps/server/agent/manager.ts b/apps/server/agent/manager.ts index 551f7eb..7a8251a 100644 --- a/apps/server/agent/manager.ts +++ b/apps/server/agent/manager.ts @@ -667,7 +667,7 @@ export class MultiProviderAgentManager implements AgentManager { * Does not use the conversations table — the message is injected directly * as the next resume prompt for the agent's Claude Code session. */ - async sendUserMessage(agentId: string, message: string): Promise { + async sendUserMessage(agentId: string, message: string, cwd?: string): Promise { const agent = await this.repository.findById(agentId); if (!agent) throw new Error(`Agent not found: ${agentId}`); @@ -682,7 +682,7 @@ export class MultiProviderAgentManager implements AgentManager { const provider = getProvider(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 const signalPath = join(agentCwd, '.cw/output/signal.json'); diff --git a/apps/server/agent/mock-manager.ts b/apps/server/agent/mock-manager.ts index 529b769..f38e672 100644 --- a/apps/server/agent/mock-manager.ts +++ b/apps/server/agent/mock-manager.ts @@ -534,7 +534,7 @@ export class MockAgentManager implements AgentManager { * Deliver a user message to a running errand agent. * Mock implementation: no-op (simulates message delivery without actual process interaction). */ - async sendUserMessage(agentId: string, _message: string): Promise { + async sendUserMessage(agentId: string, _message: string, _cwd?: string): Promise { const record = this.agents.get(agentId); if (!record) { throw new Error(`Agent '${agentId}' not found`); diff --git a/apps/server/agent/types.ts b/apps/server/agent/types.ts index 0e26557..9feec9a 100644 --- a/apps/server/agent/types.ts +++ b/apps/server/agent/types.ts @@ -272,5 +272,5 @@ export interface AgentManager { * @param agentId - The errand agent to message * @param message - The user's message text */ - sendUserMessage(agentId: string, message: string): Promise; + sendUserMessage(agentId: string, message: string, cwd?: string): Promise; } diff --git a/apps/server/trpc/routers/errand.ts b/apps/server/trpc/routers/errand.ts index 29fc7ad..ae68ae7 100644 --- a/apps/server/trpc/routers/errand.ts +++ b/apps/server/trpc/routers/errand.ts @@ -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 }; }),