import type { CoreCommand, CoreGitStatusSnapshot, CoreProjectView } from "@t3tools/contracts"; import type { AppendEventInput } from "./eventStore"; export interface CommandHandlerContext { readonly actor: string; readonly nowIso: string; readonly probeGitStatus: (cwd: string) => Promise; } function json(value: unknown): string { return JSON.stringify(value); } function baseEvent(input: { eventId: string; streamId: string; aggregateType: "project" | "thread" | "git"; occurredAt: string; causationId: string; correlationId: string; actor: string; eventType: string; payload: unknown; idempotencyKey?: string | undefined; }): AppendEventInput { return { eventId: input.eventId, streamId: input.streamId, aggregateType: input.aggregateType, occurredAt: input.occurredAt, causationId: input.causationId, correlationId: input.correlationId, actor: input.actor, eventType: input.eventType, payloadJson: json(input.payload), idempotencyKey: input.idempotencyKey, }; } function newEventId(prefix: string): string { return `${prefix}_${crypto.randomUUID()}`; } export async function handleCoreCommand( command: CoreCommand, context: CommandHandlerContext, ): Promise { const events: AppendEventInput[] = []; const occurredAt = context.nowIso; const causationId = command.commandId; const correlationId = command.commandId; switch (command.kind) { case "CreateProject": { const project: CoreProjectView = { id: command.payload.id, name: command.payload.name, cwd: command.payload.cwd, model: command.payload.model, expanded: true, scripts: [], }; events.push( baseEvent({ eventId: newEventId("project_created"), streamId: command.payload.id, aggregateType: "project", occurredAt, causationId, correlationId, actor: context.actor, eventType: "ProjectCreated", payload: project, }), ); return events; } case "CreateThread": { events.push( baseEvent({ eventId: newEventId("thread_created"), streamId: command.payload.id, aggregateType: "thread", occurredAt, causationId, correlationId, actor: context.actor, eventType: "ThreadCreated", payload: command.payload, }), ); return events; } case "AppendUserMessage": { events.push( baseEvent({ eventId: newEventId("message_user"), streamId: command.payload.threadId, aggregateType: "thread", occurredAt, causationId, correlationId, actor: context.actor, eventType: "UserMessageAppended", payload: command.payload, idempotencyKey: command.commandId, }), ); return events; } case "AppendAssistantMessage": { events.push( baseEvent({ eventId: newEventId("message_assistant"), streamId: command.payload.threadId, aggregateType: "thread", occurredAt, causationId, correlationId, actor: context.actor, eventType: "AssistantMessageAppended", payload: command.payload, idempotencyKey: command.commandId, }), ); return events; } case "SetThreadBranch": { events.push( baseEvent({ eventId: newEventId("thread_branch"), streamId: command.payload.threadId, aggregateType: "thread", occurredAt, causationId, correlationId, actor: context.actor, eventType: "ThreadBranchSet", payload: command.payload, }), ); return events; } case "GitWorkflow": { events.push( baseEvent({ eventId: newEventId("git_workflow"), streamId: command.payload.cwd, aggregateType: "git", occurredAt, causationId, correlationId, actor: context.actor, eventType: "GitWorkflowRecorded", payload: command.payload, }), ); const status = await context.probeGitStatus(command.payload.cwd); events.push( baseEvent({ eventId: newEventId("git_observed"), streamId: command.payload.cwd, aggregateType: "git", occurredAt, causationId, correlationId, actor: context.actor, eventType: "GitStateObserved", payload: status, }), ); return events; } case "GitProbe": { const status = await context.probeGitStatus(command.payload.cwd); events.push( baseEvent({ eventId: newEventId("git_observed"), streamId: command.payload.cwd, aggregateType: "git", occurredAt, causationId, correlationId, actor: context.actor, eventType: "GitStateObserved", payload: status, }), ); return events; } default: return events; } }