refactor(agent): Use agent name instead of ID for log directory paths
Aligns agent-logs directory naming with agent-workdirs so both use the human-readable agent name, making filesystem correlation trivial.
This commit is contained in:
@@ -108,8 +108,8 @@ export class CleanupManager {
|
||||
/**
|
||||
* Remove log directory for an agent.
|
||||
*/
|
||||
async removeAgentLogs(agentId: string): Promise<void> {
|
||||
const logDir = join(this.workspaceRoot, '.cw', 'agent-logs', agentId);
|
||||
async removeAgentLogs(agentName: string): Promise<void> {
|
||||
const logDir = join(this.workspaceRoot, '.cw', 'agent-logs', agentName);
|
||||
await rm(logDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
@@ -191,10 +191,10 @@ export class CleanupManager {
|
||||
}
|
||||
|
||||
const agents = await this.repository.findAll();
|
||||
const knownIds = new Set(agents.map(a => a.id));
|
||||
const knownNames = new Set(agents.map(a => a.name));
|
||||
|
||||
for (const entry of entries) {
|
||||
if (!knownIds.has(entry)) {
|
||||
if (!knownNames.has(entry)) {
|
||||
log.info({ orphan: entry }, 'removing orphaned agent log dir');
|
||||
try {
|
||||
await rm(join(logsPath, entry), { recursive: true, force: true });
|
||||
@@ -251,8 +251,8 @@ export class CleanupManager {
|
||||
async archiveForDebug(alias: string, agentId: string): Promise<void> {
|
||||
const agentWorkdir = this.getAgentWorkdir(alias);
|
||||
const debugWorkdir = join(this.workspaceRoot, '.cw', 'debug', 'workdirs', alias);
|
||||
const logDir = join(this.workspaceRoot, '.cw', 'agent-logs', agentId);
|
||||
const debugLogDir = join(this.workspaceRoot, '.cw', 'debug', 'agent-logs', agentId);
|
||||
const logDir = join(this.workspaceRoot, '.cw', 'agent-logs', alias);
|
||||
const debugLogDir = join(this.workspaceRoot, '.cw', 'debug', 'agent-logs', alias);
|
||||
|
||||
try {
|
||||
if (existsSync(agentWorkdir)) {
|
||||
@@ -307,7 +307,7 @@ export class CleanupManager {
|
||||
}
|
||||
|
||||
try {
|
||||
await this.removeAgentLogs(agentId);
|
||||
await this.removeAgentLogs(alias);
|
||||
} catch (err) {
|
||||
log.warn({ agentId, err: err instanceof Error ? err.message : String(err) }, 'auto-cleanup: failed to remove logs');
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ export class MultiProviderAgentManager implements AgentManager {
|
||||
|
||||
// 6. Spawn detached subprocess
|
||||
const { pid, outputFilePath, tailer } = this.processManager.spawnDetached(
|
||||
agentId, command, args, cwd ?? agentCwd, processEnv, providerName, prompt,
|
||||
agentId, alias, command, args, cwd ?? agentCwd, processEnv, providerName, prompt,
|
||||
(event) => this.outputHandler.handleStreamEvent(agentId, event, this.activeAgents.get(agentId), this.outputBuffers),
|
||||
);
|
||||
|
||||
@@ -451,7 +451,7 @@ export class MultiProviderAgentManager implements AgentManager {
|
||||
}
|
||||
|
||||
const { pid, outputFilePath, tailer } = this.processManager.spawnDetached(
|
||||
agentId, command, args, agentCwd, processEnv, provider.name, commitPrompt,
|
||||
agentId, agent.name, command, args, agentCwd, processEnv, provider.name, commitPrompt,
|
||||
(event) => this.outputHandler.handleStreamEvent(agentId, event, this.activeAgents.get(agentId), this.outputBuffers),
|
||||
this.createLogChunkCallback(agentId, agent.name, commitSessionNumber),
|
||||
);
|
||||
@@ -624,7 +624,7 @@ export class MultiProviderAgentManager implements AgentManager {
|
||||
}
|
||||
|
||||
const { pid, outputFilePath, tailer } = this.processManager.spawnDetached(
|
||||
agentId, command, args, agentCwd, processEnv, provider.name, prompt,
|
||||
agentId, agent.name, command, args, agentCwd, processEnv, provider.name, prompt,
|
||||
(event) => this.outputHandler.handleStreamEvent(agentId, event, this.activeAgents.get(agentId), this.outputBuffers),
|
||||
this.createLogChunkCallback(agentId, agent.name, resumeSessionNumber),
|
||||
);
|
||||
@@ -696,7 +696,7 @@ export class MultiProviderAgentManager implements AgentManager {
|
||||
try { await this.cleanupManager.removeAgentBranches(agent.name, agent.initiativeId); }
|
||||
catch (err) { log.warn({ agentId, err: err instanceof Error ? err.message : String(err) }, 'failed to remove branches'); }
|
||||
|
||||
try { await this.cleanupManager.removeAgentLogs(agentId); }
|
||||
try { await this.cleanupManager.removeAgentLogs(agent.name); }
|
||||
catch (err) { log.warn({ agentId, err: err instanceof Error ? err.message : String(err) }, 'failed to remove logs'); }
|
||||
|
||||
// 3b. Delete log chunks from DB
|
||||
|
||||
@@ -220,13 +220,14 @@ describe('ProcessManager', () => {
|
||||
|
||||
it('validates cwd exists before spawn', () => {
|
||||
const agentId = 'agent-123';
|
||||
const agentName = 'test-agent';
|
||||
const command = 'claude';
|
||||
const args = ['--help'];
|
||||
const cwd = '/test/workspace/agent-workdirs/test-agent';
|
||||
const env = { TEST_VAR: 'value' };
|
||||
const providerName = 'claude';
|
||||
|
||||
processManager.spawnDetached(agentId, command, args, cwd, env, providerName);
|
||||
processManager.spawnDetached(agentId, agentName, command, args, cwd, env, providerName);
|
||||
|
||||
expect(mockExistsSync).toHaveBeenCalledWith(cwd);
|
||||
expect(mockSpawn).toHaveBeenCalledWith(command, args, {
|
||||
@@ -241,6 +242,7 @@ describe('ProcessManager', () => {
|
||||
mockExistsSync.mockReturnValue(false);
|
||||
|
||||
const agentId = 'agent-123';
|
||||
const agentName = 'test-agent';
|
||||
const command = 'claude';
|
||||
const args = ['--help'];
|
||||
const cwd = '/nonexistent/path';
|
||||
@@ -248,19 +250,20 @@ describe('ProcessManager', () => {
|
||||
const providerName = 'claude';
|
||||
|
||||
expect(() => {
|
||||
processManager.spawnDetached(agentId, command, args, cwd, env, providerName);
|
||||
processManager.spawnDetached(agentId, agentName, command, args, cwd, env, providerName);
|
||||
}).toThrow('Agent working directory does not exist: /nonexistent/path');
|
||||
});
|
||||
|
||||
it('passes correct cwd parameter to spawn', () => {
|
||||
const agentId = 'agent-123';
|
||||
const agentName = 'test-agent';
|
||||
const command = 'claude';
|
||||
const args = ['--help'];
|
||||
const cwd = '/test/workspace/agent-workdirs/test-agent';
|
||||
const env = { CLAUDE_CONFIG_DIR: '/config' };
|
||||
const providerName = 'claude';
|
||||
|
||||
processManager.spawnDetached(agentId, command, args, cwd, env, providerName);
|
||||
processManager.spawnDetached(agentId, agentName, command, args, cwd, env, providerName);
|
||||
|
||||
expect(mockSpawn).toHaveBeenCalledTimes(1);
|
||||
const spawnCall = mockSpawn.mock.calls[0];
|
||||
@@ -279,27 +282,29 @@ describe('ProcessManager', () => {
|
||||
|
||||
it('logs comprehensive spawn information', () => {
|
||||
const agentId = 'agent-123';
|
||||
const agentName = 'test-agent';
|
||||
const command = 'claude';
|
||||
const args = ['--json-schema', 'schema.json'];
|
||||
const cwd = '/test/workspace/agent-workdirs/test-agent';
|
||||
const env = { CLAUDE_CONFIG_DIR: '/config' };
|
||||
const providerName = 'claude';
|
||||
|
||||
const result = processManager.spawnDetached(agentId, command, args, cwd, env, providerName);
|
||||
const result = processManager.spawnDetached(agentId, agentName, command, args, cwd, env, providerName);
|
||||
|
||||
expect(result).toHaveProperty('pid', 12345);
|
||||
expect(result).toHaveProperty('outputFilePath');
|
||||
expect(result).toHaveProperty('tailer');
|
||||
|
||||
// Verify log directory creation
|
||||
// Verify log directory creation uses agent name, not ID
|
||||
expect(mockMkdirSync).toHaveBeenCalledWith(
|
||||
'/test/workspace/.cw/agent-logs/agent-123',
|
||||
'/test/workspace/.cw/agent-logs/test-agent',
|
||||
{ recursive: true }
|
||||
);
|
||||
});
|
||||
|
||||
it('writes prompt file when provided', () => {
|
||||
const agentId = 'agent-123';
|
||||
const agentName = 'test-agent';
|
||||
const command = 'claude';
|
||||
const args = ['--help'];
|
||||
const cwd = '/test/workspace/agent-workdirs/test-agent';
|
||||
@@ -307,10 +312,10 @@ describe('ProcessManager', () => {
|
||||
const providerName = 'claude';
|
||||
const prompt = 'Test prompt';
|
||||
|
||||
processManager.spawnDetached(agentId, command, args, cwd, env, providerName, prompt);
|
||||
processManager.spawnDetached(agentId, agentName, command, args, cwd, env, providerName, prompt);
|
||||
|
||||
expect(mockWriteFileSync).toHaveBeenCalledWith(
|
||||
'/test/workspace/.cw/agent-logs/agent-123/PROMPT.md',
|
||||
'/test/workspace/.cw/agent-logs/test-agent/PROMPT.md',
|
||||
'Test prompt',
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
@@ -236,6 +236,7 @@ export class ProcessManager {
|
||||
*/
|
||||
spawnDetached(
|
||||
agentId: string,
|
||||
agentName: string,
|
||||
command: string,
|
||||
args: string[],
|
||||
cwd: string,
|
||||
@@ -271,7 +272,7 @@ export class ProcessManager {
|
||||
throw new Error(`Agent working directory does not exist: ${cwd}`);
|
||||
}
|
||||
|
||||
const logDir = join(this.workspaceRoot, '.cw', 'agent-logs', agentId);
|
||||
const logDir = join(this.workspaceRoot, '.cw', 'agent-logs', agentName);
|
||||
mkdirSync(logDir, { recursive: true });
|
||||
const outputFilePath = join(logDir, 'output.jsonl');
|
||||
const stderrFilePath = join(logDir, 'stderr.log');
|
||||
|
||||
@@ -201,7 +201,7 @@ export function agentProcedures(publicProcedure: ProcedureBuilder) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const outputFilePath = join(workspaceRoot, '.cw', 'agent-logs', agent.id, 'output.jsonl');
|
||||
const outputFilePath = join(workspaceRoot, '.cw', 'agent-logs', agent.name, 'output.jsonl');
|
||||
|
||||
try {
|
||||
const content = await readFile(outputFilePath, 'utf-8');
|
||||
|
||||
Reference in New Issue
Block a user