fix: Resolve agent workdir probing for initiative project subdirectories
Conflict-resolution agents (and any initiative-based agent) can write .cw/output/signal.json inside a project subdirectory (e.g. agent-workdirs/<name>/codewalk-district/.cw/output/) rather than the parent agent workdir. This caused two failures: 1. spawnInternal wrote spawn-diagnostic.json before registering the agent in activeAgents and starting pollForCompletion. If the .cw/ directory didn't exist (no inputContext provided), the write threw ENOENT, orphaning the running process with no completion monitoring. 2. resolveAgentCwd in cleanup-manager and output-handler only probed for a workspace/ subdirectory (standalone agents) but not project subdirectories, so reconciliation and completion handling couldn't find signal.json and marked the agent as crashed. Fixes: - Move activeAgents registration and pollForCompletion setup before the diagnostic write; make the write non-fatal with mkdir -p - Add project subdirectory probing to resolveAgentCwd in both cleanup-manager.ts and output-handler.ts
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { existsSync, readdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import type { AgentRepository } from '../db/repositories/agent-repository.js';
|
||||
import type { ChangeSetRepository, CreateChangeSetEntryData } from '../db/repositories/change-set-repository.js';
|
||||
@@ -233,10 +233,10 @@ export class OutputHandler {
|
||||
|
||||
log.debug({ agentId }, 'detached agent completed');
|
||||
|
||||
// Resolve actual agent working directory — standalone agents run in a
|
||||
// "workspace/" subdirectory inside getAgentWorkdir, so prefer agentCwd
|
||||
// recorded at spawn time when available.
|
||||
const agentWorkdir = active?.agentCwd ?? getAgentWorkdir(agent.worktreeId);
|
||||
// Resolve actual agent working directory.
|
||||
// The recorded agentCwd may be the parent dir (agent-workdirs/<name>/) while
|
||||
// the agent actually writes .cw/output/ inside a project subdirectory.
|
||||
const agentWorkdir = this.resolveAgentWorkdir(active?.agentCwd ?? getAgentWorkdir(agent.worktreeId));
|
||||
const outputDir = join(agentWorkdir, '.cw', 'output');
|
||||
const expectedPwdFile = join(agentWorkdir, '.cw', 'expected-pwd.txt');
|
||||
const diagnosticFile = join(agentWorkdir, '.cw', 'spawn-diagnostic.json');
|
||||
@@ -1158,6 +1158,31 @@ export class OutputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the actual agent working directory. The recorded agentCwd may be
|
||||
* the parent (agent-workdirs/<name>/) but .cw/output/ could be inside a
|
||||
* project subdirectory (e.g. codewalk-district/.cw/output/).
|
||||
*/
|
||||
private resolveAgentWorkdir(base: string): string {
|
||||
if (existsSync(join(base, '.cw', 'output'))) return base;
|
||||
|
||||
// Standalone agents: workspace/ subdirectory
|
||||
const workspaceSub = join(base, 'workspace');
|
||||
if (existsSync(join(workspaceSub, '.cw'))) return workspaceSub;
|
||||
|
||||
// Initiative-based agents: probe project subdirectories
|
||||
try {
|
||||
for (const entry of readdirSync(base, { withFileTypes: true })) {
|
||||
if (entry.isDirectory() && entry.name !== '.cw') {
|
||||
const sub = join(base, entry.name);
|
||||
if (existsSync(join(sub, '.cw', 'output'))) return sub;
|
||||
}
|
||||
}
|
||||
} catch { /* base may not exist */ }
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
private emitCrashed(agent: { id: string; name: string; taskId: string | null }, error: string): void {
|
||||
if (this.eventBus) {
|
||||
const event: AgentCrashedEvent = {
|
||||
|
||||
Reference in New Issue
Block a user