Files
Codewalkers/apps/server/container.ts
Lukas May 28521e1c20 chore: merge main into cw/small-change-flow
Integrates main branch changes (headquarters dashboard, task retry count,
agent prompt persistence, remote sync improvements) with the initiative's
errand agent feature. Both features coexist in the merged result.

Key resolutions:
- Schema: take main's errands table (nullable projectId, no conflictFiles,
  with errandsRelations); migrate to 0035_faulty_human_fly
- Router: keep both errandProcedures and headquartersProcedures
- Errand prompt: take main's simpler version (no question-asking flow)
- Manager: take main's status check (running|idle only, no waiting_for_input)
- Tests: update to match removed conflictFiles field and undefined vs null
2026-03-06 16:48:12 +01:00

306 lines
10 KiB
TypeScript

/**
* Dependency Container
*
* Factory functions for creating the full dependency graph.
* Keeps startServer() thin and makes repo wiring reusable by the test harness.
*/
import type { DrizzleDatabase } from './db/index.js';
import {
createDatabase,
ensureSchema,
DrizzleInitiativeRepository,
DrizzlePhaseRepository,
DrizzleTaskRepository,
DrizzleMessageRepository,
DrizzleAgentRepository,
DrizzlePageRepository,
DrizzleProjectRepository,
DrizzleAccountRepository,
DrizzleChangeSetRepository,
DrizzleLogChunkRepository,
DrizzleConversationRepository,
DrizzleChatSessionRepository,
DrizzleReviewCommentRepository,
DrizzleErrandRepository,
} from './db/index.js';
import type { InitiativeRepository } from './db/repositories/initiative-repository.js';
import type { PhaseRepository } from './db/repositories/phase-repository.js';
import type { TaskRepository } from './db/repositories/task-repository.js';
import type { MessageRepository } from './db/repositories/message-repository.js';
import type { AgentRepository } from './db/repositories/agent-repository.js';
import type { PageRepository } from './db/repositories/page-repository.js';
import type { ProjectRepository } from './db/repositories/project-repository.js';
import type { AccountRepository } from './db/repositories/account-repository.js';
import type { ChangeSetRepository } from './db/repositories/change-set-repository.js';
import type { LogChunkRepository } from './db/repositories/log-chunk-repository.js';
import type { ConversationRepository } from './db/repositories/conversation-repository.js';
import type { ChatSessionRepository } from './db/repositories/chat-session-repository.js';
import type { ReviewCommentRepository } from './db/repositories/review-comment-repository.js';
import type { ErrandRepository } from './db/repositories/errand-repository.js';
import type { EventBus } from './events/index.js';
import { createEventBus } from './events/index.js';
import { ProcessManager, ProcessRegistry } from './process/index.js';
import { LogManager } from './logging/index.js';
import { MultiProviderAgentManager } from './agent/index.js';
import { DefaultAccountCredentialManager } from './agent/credentials/index.js';
import type { AccountCredentialManager } from './agent/credentials/types.js';
import { DefaultDispatchManager } from './dispatch/manager.js';
import { DefaultPhaseDispatchManager } from './dispatch/phase-manager.js';
import type { DispatchManager, PhaseDispatchManager } from './dispatch/types.js';
import { SimpleGitBranchManager } from './git/simple-git-branch-manager.js';
import type { BranchManager } from './git/branch-manager.js';
import { ProjectSyncManager } from './git/remote-sync.js';
import { ExecutionOrchestrator } from './execution/orchestrator.js';
import { DefaultConflictResolutionService } from './coordination/conflict-resolution-service.js';
import { PreviewManager } from './preview/index.js';
import { findWorkspaceRoot } from './config/index.js';
import { createModuleLogger } from './logger/index.js';
import type { ServerContextDeps } from './server/index.js';
// =============================================================================
// Repositories
// =============================================================================
/**
* All 13 repository ports.
*/
export interface Repositories {
initiativeRepository: InitiativeRepository;
phaseRepository: PhaseRepository;
taskRepository: TaskRepository;
messageRepository: MessageRepository;
agentRepository: AgentRepository;
pageRepository: PageRepository;
projectRepository: ProjectRepository;
accountRepository: AccountRepository;
changeSetRepository: ChangeSetRepository;
logChunkRepository: LogChunkRepository;
conversationRepository: ConversationRepository;
chatSessionRepository: ChatSessionRepository;
reviewCommentRepository: ReviewCommentRepository;
errandRepository: ErrandRepository;
}
/**
* Create all 13 Drizzle repository adapters from a database instance.
* Reusable by both the production server and the test harness.
*/
export function createRepositories(db: DrizzleDatabase): Repositories {
return {
initiativeRepository: new DrizzleInitiativeRepository(db),
phaseRepository: new DrizzlePhaseRepository(db),
taskRepository: new DrizzleTaskRepository(db),
messageRepository: new DrizzleMessageRepository(db),
agentRepository: new DrizzleAgentRepository(db),
pageRepository: new DrizzlePageRepository(db),
projectRepository: new DrizzleProjectRepository(db),
accountRepository: new DrizzleAccountRepository(db),
changeSetRepository: new DrizzleChangeSetRepository(db),
logChunkRepository: new DrizzleLogChunkRepository(db),
conversationRepository: new DrizzleConversationRepository(db),
chatSessionRepository: new DrizzleChatSessionRepository(db),
reviewCommentRepository: new DrizzleReviewCommentRepository(db),
errandRepository: new DrizzleErrandRepository(db),
};
}
// =============================================================================
// Container
// =============================================================================
/**
* Full dependency graph for the coordination server.
*/
export interface Container extends Repositories {
db: DrizzleDatabase;
eventBus: EventBus;
processManager: ProcessManager;
logManager: LogManager;
workspaceRoot: string;
credentialManager: AccountCredentialManager;
agentManager: MultiProviderAgentManager;
dispatchManager: DispatchManager;
phaseDispatchManager: PhaseDispatchManager;
branchManager: BranchManager;
projectSyncManager: ProjectSyncManager;
executionOrchestrator: ExecutionOrchestrator;
previewManager: PreviewManager;
/** Extract the subset of deps that CoordinationServer needs. */
toContextDeps(): ServerContextDeps;
}
/**
* Options for container creation.
*/
export interface ContainerOptions {
debug?: boolean;
}
/**
* Create the full dependency container.
*
* Wires: ProcessRegistry → EventBus → ProcessManager → LogManager →
* Database → Repositories → CredentialManager → AgentManager.
* Runs ensureSchema() and reconcileAfterRestart() before returning.
*/
export async function createContainer(options?: ContainerOptions): Promise<Container> {
const log = createModuleLogger('container');
// Infrastructure
const registry = new ProcessRegistry();
const eventBus = createEventBus();
const processManager = new ProcessManager(registry, eventBus);
const logManager = new LogManager();
// Database
const db = createDatabase();
ensureSchema(db);
log.info('database initialized');
// Repositories
const repos = createRepositories(db);
log.info('repositories created');
// Workspace root
const workspaceRoot = findWorkspaceRoot(process.cwd()) ?? process.cwd();
log.info({ workspaceRoot }, 'workspace root resolved');
// Credential manager
const credentialManager = new DefaultAccountCredentialManager(eventBus);
log.info('credential manager created');
// Agent manager
const agentManager = new MultiProviderAgentManager(
repos.agentRepository,
workspaceRoot,
repos.projectRepository,
repos.accountRepository,
eventBus,
credentialManager,
repos.changeSetRepository,
repos.phaseRepository,
repos.taskRepository,
repos.pageRepository,
repos.logChunkRepository,
options?.debug ?? false,
undefined, // processManagerOverride
repos.chatSessionRepository,
repos.reviewCommentRepository,
);
log.info('agent manager created');
// Branch manager
const branchManager = new SimpleGitBranchManager();
log.info('branch manager created');
// Project sync manager
const projectSyncManager = new ProjectSyncManager(
repos.projectRepository,
workspaceRoot,
eventBus,
);
log.info('project sync manager created');
// Dispatch managers
const dispatchManager = new DefaultDispatchManager(
repos.taskRepository,
repos.messageRepository,
agentManager,
eventBus,
repos.initiativeRepository,
repos.phaseRepository,
repos.agentRepository,
repos.pageRepository,
repos.projectRepository,
branchManager,
workspaceRoot,
);
const phaseDispatchManager = new DefaultPhaseDispatchManager(
repos.phaseRepository,
repos.taskRepository,
dispatchManager,
eventBus,
repos.initiativeRepository,
repos.projectRepository,
branchManager,
workspaceRoot,
projectSyncManager,
);
log.info('dispatch managers created');
// Conflict resolution service (for orchestrator)
const conflictResolutionService = new DefaultConflictResolutionService(
repos.taskRepository,
repos.agentRepository,
repos.messageRepository,
eventBus,
);
// Execution orchestrator
const executionOrchestrator = new ExecutionOrchestrator(
branchManager,
repos.phaseRepository,
repos.taskRepository,
repos.initiativeRepository,
repos.projectRepository,
phaseDispatchManager,
dispatchManager,
conflictResolutionService,
eventBus,
workspaceRoot,
repos.agentRepository,
);
executionOrchestrator.start();
log.info('execution orchestrator started');
// Reconcile agent state from any previous server session.
// Must run AFTER orchestrator.start() so event listeners are registered
// and agent:stopped / agent:crashed events are not lost.
await agentManager.reconcileAfterRestart();
log.info('agent reconciliation complete');
// Preview manager
const previewManager = new PreviewManager(
repos.projectRepository,
eventBus,
workspaceRoot,
repos.phaseRepository,
repos.initiativeRepository,
);
log.info('preview manager created');
return {
db,
eventBus,
processManager,
logManager,
workspaceRoot,
credentialManager,
agentManager,
dispatchManager,
phaseDispatchManager,
branchManager,
projectSyncManager,
executionOrchestrator,
previewManager,
...repos,
toContextDeps(): ServerContextDeps {
return {
agentManager,
credentialManager,
dispatchManager,
phaseDispatchManager,
branchManager,
projectSyncManager,
executionOrchestrator,
previewManager,
workspaceRoot,
...repos,
};
},
};
}