diff --git a/apps/server/agent/manager.ts b/apps/server/agent/manager.ts index 3e022bc..551f7eb 100644 --- a/apps/server/agent/manager.ts +++ b/apps/server/agent/manager.ts @@ -233,9 +233,13 @@ export class MultiProviderAgentManager implements AgentManager { log.debug('no accounts available, spawning without account'); } - // 2. Create isolated worktrees + // 2. Create isolated worktrees (skip when caller provides explicit cwd, e.g. errands) let agentCwd: string; - if (initiativeId) { + if (cwd) { + // Caller manages the worktree (errands). Use their cwd directly. + agentCwd = cwd; + log.info({ alias, agentCwd }, 'using caller-provided cwd, skipping worktree creation'); + } else if (initiativeId) { log.debug({ alias, initiativeId, baseBranch, branchName }, 'creating initiative-based worktrees'); agentCwd = await this.processManager.createProjectWorktrees(alias, initiativeId, baseBranch, branchName); @@ -318,15 +322,13 @@ export class MultiProviderAgentManager implements AgentManager { // 4. Build spawn command const { command, args, env: providerEnv } = this.processManager.buildSpawnCommand(provider, prompt); - const finalCwd = cwd ?? agentCwd; log.info({ agentId, alias, command, args: args.join(' '), - finalCwd, - customCwdProvided: !!cwd, + agentCwd, providerEnv: Object.keys(providerEnv) }, 'spawn command built'); @@ -342,7 +344,7 @@ export class MultiProviderAgentManager implements AgentManager { // 6. Spawn detached subprocess const { pid, outputFilePath, tailer } = await this.processManager.spawnDetached( - agentId, alias, command, args, cwd ?? agentCwd, processEnv, providerName, prompt, + agentId, alias, command, args, agentCwd, processEnv, providerName, prompt, (event) => this.outputHandler.handleStreamEvent(agentId, event, this.activeAgents.get(agentId)), this.createLogChunkCallback(agentId, alias, 1), ); @@ -351,7 +353,7 @@ export class MultiProviderAgentManager implements AgentManager { // Register agent and start polling BEFORE non-critical I/O so that a // diagnostic-write failure can never orphan a running process. - const activeEntry: ActiveAgent = { agentId, pid, tailer, outputFilePath, agentCwd: finalCwd }; + const activeEntry: ActiveAgent = { agentId, pid, tailer, outputFilePath, agentCwd }; this.activeAgents.set(agentId, activeEntry); // Emit spawned event @@ -376,19 +378,19 @@ export class MultiProviderAgentManager implements AgentManager { // Write spawn diagnostic file (non-fatal — .cw/ may not exist yet for // agents spawned without inputContext, e.g. conflict-resolution agents) try { - const diagnosticDir = join(finalCwd, '.cw'); + const diagnosticDir = join(agentCwd, '.cw'); await mkdir(diagnosticDir, { recursive: true }); const diagnostic = { timestamp: new Date().toISOString(), agentId, alias, - intendedCwd: finalCwd, + intendedCwd: agentCwd, worktreeId: agent.worktreeId, provider: providerName, command, args, env: processEnv, - cwdExistsAtSpawn: existsSync(finalCwd), + cwdExistsAtSpawn: existsSync(agentCwd), initiativeId: initiativeId || null, customCwdProvided: !!cwd, accountId: accountId || null,