Move src/ → apps/server/ and packages/web/ → apps/web/ to adopt standard monorepo conventions (apps/ for runnable apps, packages/ for reusable libraries). Update all config files, shared package imports, test fixtures, and documentation to reflect new paths. Key fixes: - Update workspace config to ["apps/*", "packages/*"] - Update tsconfig.json rootDir/include for apps/server/ - Add apps/web/** to vitest exclude list - Update drizzle.config.ts schema path - Fix ensure-schema.ts migration path detection (3 levels up in dev, 2 levels up in dist) - Fix tests/integration/cli-server.test.ts import paths - Update packages/shared imports to apps/server/ paths - Update all docs/ files with new paths
203 lines
7.2 KiB
TypeScript
203 lines
7.2 KiB
TypeScript
/**
|
|
* Agent Working Directory Verification Tests
|
|
*
|
|
* Tests that verify agents actually run in their intended working directories.
|
|
* These tests use simple shell commands to prove the agent execution location.
|
|
*
|
|
* IMPORTANT: These tests spawn real CLI processes and may incur API costs.
|
|
* They are SKIPPED by default to prevent accidental charges.
|
|
*
|
|
* To run these tests:
|
|
* ```bash
|
|
* REAL_WORKDIR_TESTS=1 npm test -- src/test/integration/agent-workdir-verification.test.ts --test-timeout=120000
|
|
* ```
|
|
*/
|
|
|
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
import { mkdtemp, rm, readFile } from 'node:fs/promises';
|
|
import { existsSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { MultiProviderAgentManager } from '../../agent/manager.js';
|
|
import { createTestDatabase } from '../../db/repositories/drizzle/test-helpers.js';
|
|
import {
|
|
DrizzleAgentRepository,
|
|
DrizzleProjectRepository,
|
|
DrizzleAccountRepository,
|
|
DrizzleInitiativeRepository,
|
|
} from '../../db/repositories/drizzle/index.js';
|
|
import { EventEmitterBus } from '../../events/bus.js';
|
|
|
|
const SHOULD_SKIP = !process.env.REAL_WORKDIR_TESTS;
|
|
const TEST_TIMEOUT = 60000;
|
|
|
|
describe.skipIf(SHOULD_SKIP)('Agent Working Directory Verification', () => {
|
|
let tempDir: string;
|
|
let agentManager: MultiProviderAgentManager;
|
|
let agentRepository: DrizzleAgentRepository;
|
|
|
|
beforeAll(async () => {
|
|
if (SHOULD_SKIP) return;
|
|
|
|
console.log('\n=== Running Agent Working Directory Tests ===');
|
|
console.log('These tests verify agents run in correct working directories.\n');
|
|
|
|
// Create temp directory for test workspace
|
|
tempDir = await mkdtemp(join(tmpdir(), 'cw-workdir-test-'));
|
|
|
|
// Set up test database and repositories
|
|
const db = await createTestDatabase();
|
|
const eventBus = new EventEmitterBus();
|
|
|
|
agentRepository = new DrizzleAgentRepository(db);
|
|
const projectRepository = new DrizzleProjectRepository(db);
|
|
const accountRepository = new DrizzleAccountRepository(db);
|
|
|
|
agentManager = new MultiProviderAgentManager(
|
|
agentRepository,
|
|
tempDir,
|
|
projectRepository,
|
|
accountRepository,
|
|
eventBus,
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
if (SHOULD_SKIP || !tempDir) return;
|
|
try {
|
|
await rm(tempDir, { recursive: true });
|
|
} catch (err) {
|
|
console.warn('Failed to cleanup temp directory:', err);
|
|
}
|
|
});
|
|
|
|
it('spawns agent in correct standalone working directory', async () => {
|
|
const prompt = `
|
|
Write your current working directory to a file called 'verify-pwd.txt'.
|
|
Use this exact bash command:
|
|
|
|
pwd > verify-pwd.txt
|
|
|
|
Then output the signal: {"done": true}
|
|
`.trim();
|
|
|
|
// Spawn standalone agent
|
|
const agent = await agentManager.spawn({
|
|
taskId: null,
|
|
prompt,
|
|
mode: 'execute',
|
|
provider: 'claude',
|
|
});
|
|
|
|
expect(agent.id).toBeTruthy();
|
|
expect(agent.status).toBe('running');
|
|
|
|
// Wait for completion (poll agent status)
|
|
let attempts = 0;
|
|
const maxAttempts = 60; // 60 seconds timeout
|
|
|
|
while (attempts < maxAttempts) {
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
attempts++;
|
|
|
|
const currentAgent = await agentRepository.findById(agent.id);
|
|
if (!currentAgent || currentAgent.status !== 'running') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Verify final agent state
|
|
const completedAgent = await agentRepository.findById(agent.id);
|
|
expect(completedAgent).toBeTruthy();
|
|
expect(completedAgent!.status).not.toBe('running');
|
|
|
|
// Get the agent's expected working directory
|
|
const expectedWorkdir = join(tempDir, 'agent-workdirs', agent.name, 'workspace');
|
|
|
|
// Read diagnostic files
|
|
const diagnosticFile = join(expectedWorkdir, '.cw', 'spawn-diagnostic.json');
|
|
const expectedPwdFile = join(expectedWorkdir, '.cw', 'expected-pwd.txt');
|
|
const verifyPwdFile = join(expectedWorkdir, 'verify-pwd.txt');
|
|
|
|
// Verify diagnostic files exist
|
|
expect(existsSync(diagnosticFile), 'spawn diagnostic file should exist').toBe(true);
|
|
expect(existsSync(expectedPwdFile), 'expected pwd file should exist').toBe(true);
|
|
|
|
// Read diagnostic data
|
|
const diagnostic = JSON.parse(await readFile(diagnosticFile, 'utf-8'));
|
|
const expectedPwd = (await readFile(expectedPwdFile, 'utf-8')).trim();
|
|
|
|
console.log('Diagnostic data:', diagnostic);
|
|
console.log('Expected working directory:', expectedPwd);
|
|
|
|
// Verify diagnostic consistency
|
|
expect(diagnostic.intendedCwd).toBe(expectedWorkdir);
|
|
expect(diagnostic.cwdExistsAtSpawn).toBe(true);
|
|
expect(expectedPwd).toBe(expectedWorkdir);
|
|
|
|
// The critical test: verify the agent actually wrote the file in the expected location
|
|
if (existsSync(verifyPwdFile)) {
|
|
const actualPwd = (await readFile(verifyPwdFile, 'utf-8')).trim();
|
|
console.log('Agent reported working directory:', actualPwd);
|
|
|
|
// This is the key verification: the pwd reported by the agent should match expected
|
|
expect(actualPwd).toBe(expectedWorkdir);
|
|
} else {
|
|
// If the file doesn't exist, the agent either failed or ran somewhere else
|
|
console.warn('Agent did not create verify-pwd.txt file');
|
|
console.log('Expected at:', verifyPwdFile);
|
|
|
|
// Let's check if it was created elsewhere (debugging)
|
|
const alternativeLocations = [
|
|
join(tempDir, 'verify-pwd.txt'),
|
|
join(process.cwd(), 'verify-pwd.txt'),
|
|
];
|
|
|
|
for (const loc of alternativeLocations) {
|
|
if (existsSync(loc)) {
|
|
const content = await readFile(loc, 'utf-8');
|
|
console.log(`Found verify-pwd.txt at unexpected location ${loc}:`, content.trim());
|
|
}
|
|
}
|
|
|
|
throw new Error('Agent did not create pwd verification file in expected location');
|
|
}
|
|
}, TEST_TIMEOUT);
|
|
|
|
it('creates diagnostic files with correct metadata', async () => {
|
|
const prompt = `Output the signal: {"done": true}`;
|
|
|
|
const agent = await agentManager.spawn({
|
|
taskId: null,
|
|
prompt,
|
|
mode: 'execute',
|
|
provider: 'claude',
|
|
});
|
|
|
|
// Wait a bit for spawn to complete
|
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
|
|
const expectedWorkdir = join(tempDir, 'agent-workdirs', agent.name, 'workspace');
|
|
const diagnosticFile = join(expectedWorkdir, '.cw', 'spawn-diagnostic.json');
|
|
const expectedPwdFile = join(expectedWorkdir, '.cw', 'expected-pwd.txt');
|
|
|
|
// Verify files exist immediately after spawn
|
|
expect(existsSync(diagnosticFile), 'diagnostic file should be created after spawn').toBe(true);
|
|
expect(existsSync(expectedPwdFile), 'expected pwd file should be created').toBe(true);
|
|
|
|
// Verify diagnostic content
|
|
const diagnostic = JSON.parse(await readFile(diagnosticFile, 'utf-8'));
|
|
const expectedPwd = (await readFile(expectedPwdFile, 'utf-8')).trim();
|
|
|
|
expect(diagnostic.agentId).toBe(agent.id);
|
|
expect(diagnostic.alias).toBe(agent.name);
|
|
expect(diagnostic.intendedCwd).toBe(expectedWorkdir);
|
|
expect(diagnostic.provider).toBe('claude');
|
|
expect(diagnostic.cwdExistsAtSpawn).toBe(true);
|
|
expect(diagnostic.customCwdProvided).toBe(false);
|
|
expect(typeof diagnostic.timestamp).toBe('string');
|
|
expect(Array.isArray(diagnostic.args)).toBe(true);
|
|
|
|
expect(expectedPwd).toBe(expectedWorkdir);
|
|
});
|
|
}); |