460 lines · 15.4 KB · TypeScript
RawDownload
import {  CommandId,  DEFAULT_PROVIDER_INTERACTION_MODE,  EventId,  MessageId,  ProjectId,  ThreadId,  ProviderInstanceId,} from "@t3tools/contracts";import { createModelSelection } from "@t3tools/shared/model";import { expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as NodeServices from "@effect/platform-node/NodeServices"; import { decideOrchestrationCommand } from "./decider.ts";import { createEmptyReadModel, projectEvent } from "./projector.ts"; const asEventId = (value: string): EventId => EventId.make(value);const asProjectId = (value: string): ProjectId => ProjectId.make(value);const asMessageId = (value: string): MessageId => MessageId.make(value);it.layer(NodeServices.layer)("decider project scripts", (it) => {  it.effect("emits empty scripts on project.create", () =>    Effect.gen(function* () {      const now = "2026-01-01T00:00:00.000Z";      const readModel = createEmptyReadModel(now);       const result = yield* decideOrchestrationCommand({        command: {          type: "project.create",          commandId: CommandId.make("cmd-project-create-scripts"),          projectId: asProjectId("project-scripts"),          title: "Scripts",          workspaceRoot: "/tmp/scripts",          createdAt: now,        },        readModel,      });       const event = Array.isArray(result) ? result[0] : result;      expect(event.type).toBe("project.created");      expect((event.payload as { scripts: unknown[] }).scripts).toEqual([]);    }),  );   it.effect("propagates scripts in project.meta.update payload", () =>    Effect.gen(function* () {      const now = "2026-01-01T00:00:00.000Z";      const initial = createEmptyReadModel(now);      const readModel = yield* projectEvent(initial, {        sequence: 1,        eventId: asEventId("evt-project-create-scripts"),        aggregateKind: "project",        aggregateId: asProjectId("project-scripts"),        type: "project.created",        occurredAt: now,        commandId: CommandId.make("cmd-project-create-scripts"),        causationEventId: null,        correlationId: CommandId.make("cmd-project-create-scripts"),        metadata: {},        payload: {          projectId: asProjectId("project-scripts"),          title: "Scripts",          workspaceRoot: "/tmp/scripts",          defaultModelSelection: null,          scripts: [],          createdAt: now,          updatedAt: now,        },      });       const scripts = [        {          id: "lint",          name: "Lint",          command: "bun run lint",          icon: "lint",          runOnWorktreeCreate: false,        },      ] as const;       const result = yield* decideOrchestrationCommand({        command: {          type: "project.meta.update",          commandId: CommandId.make("cmd-project-update-scripts"),          projectId: asProjectId("project-scripts"),          scripts: Array.from(scripts),        },        readModel,      });       const event = Array.isArray(result) ? result[0] : result;      expect(event.type).toBe("project.meta-updated");      expect((event.payload as { scripts?: unknown[] }).scripts).toEqual(scripts);    }),  );   it.effect("rejects project.create for an active workspace root that already exists", () =>    Effect.gen(function* () {      const now = "2026-01-01T00:00:00.000Z";      const initial = createEmptyReadModel(now);      const readModel = yield* projectEvent(initial, {        sequence: 1,        eventId: asEventId("evt-project-create"),        aggregateKind: "project",        aggregateId: asProjectId("project-existing"),        type: "project.created",        occurredAt: now,        commandId: CommandId.make("cmd-project-create"),        causationEventId: null,        correlationId: CommandId.make("cmd-project-create"),        metadata: {},        payload: {          projectId: asProjectId("project-existing"),          title: "Project",          workspaceRoot: "/tmp/project",          defaultModelSelection: null,          scripts: [],          createdAt: now,          updatedAt: now,        },      });       const failure = yield* Effect.flip(        decideOrchestrationCommand({          command: {            type: "project.create",            commandId: CommandId.make("cmd-project-create-duplicate-root"),            projectId: asProjectId("project-duplicate-root"),            title: "Duplicate Project",            workspaceRoot: "/tmp/project/",            createdAt: now,          },          readModel,        }),      );       expect(failure.message).toContain(        "Active project 'project-existing' already exists for workspace root '/tmp/project'.",      );    }),  );   it.effect("rejects project.meta.update when moving onto another active workspace root", () =>    Effect.gen(function* () {      const now = "2026-01-01T00:00:00.000Z";      const initial = createEmptyReadModel(now);      const withFirstProject = yield* projectEvent(initial, {        sequence: 1,        eventId: asEventId("evt-project-create-first"),        aggregateKind: "project",        aggregateId: asProjectId("project-first"),        type: "project.created",        occurredAt: now,        commandId: CommandId.make("cmd-project-create-first"),        causationEventId: null,        correlationId: CommandId.make("cmd-project-create-first"),        metadata: {},        payload: {          projectId: asProjectId("project-first"),          title: "First",          workspaceRoot: "/tmp/project-first",          defaultModelSelection: null,          scripts: [],          createdAt: now,          updatedAt: now,        },      });      const readModel = yield* projectEvent(withFirstProject, {        sequence: 2,        eventId: asEventId("evt-project-create-second"),        aggregateKind: "project",        aggregateId: asProjectId("project-second"),        type: "project.created",        occurredAt: now,        commandId: CommandId.make("cmd-project-create-second"),        causationEventId: null,        correlationId: CommandId.make("cmd-project-create-second"),        metadata: {},        payload: {          projectId: asProjectId("project-second"),          title: "Second",          workspaceRoot: "/tmp/project-second",          defaultModelSelection: null,          scripts: [],          createdAt: now,          updatedAt: now,        },      });       const failure = yield* Effect.flip(        decideOrchestrationCommand({          command: {            type: "project.meta.update",            commandId: CommandId.make("cmd-project-update-duplicate-root"),            projectId: asProjectId("project-second"),            workspaceRoot: "/tmp/project-first",          },          readModel,        }),      );       expect(failure.message).toContain(        "Active project 'project-first' already exists for workspace root '/tmp/project-first'.",      );    }),  );   it.effect("emits user message and turn-start-requested events for thread.turn.start", () =>    Effect.gen(function* () {      const now = "2026-01-01T00:00:00.000Z";      const initial = createEmptyReadModel(now);      const withProject = yield* projectEvent(initial, {        sequence: 1,        eventId: asEventId("evt-project-create"),        aggregateKind: "project",        aggregateId: asProjectId("project-1"),        type: "project.created",        occurredAt: now,        commandId: CommandId.make("cmd-project-create"),        causationEventId: null,        correlationId: CommandId.make("cmd-project-create"),        metadata: {},        payload: {          projectId: asProjectId("project-1"),          title: "Project",          workspaceRoot: "/tmp/project",          defaultModelSelection: null,          scripts: [],          createdAt: now,          updatedAt: now,        },      });      const readModel = yield* projectEvent(withProject, {        sequence: 2,        eventId: asEventId("evt-thread-create"),        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.created",        occurredAt: now,        commandId: CommandId.make("cmd-thread-create"),        causationEventId: null,        correlationId: CommandId.make("cmd-thread-create"),        metadata: {},        payload: {          threadId: ThreadId.make("thread-1"),          projectId: asProjectId("project-1"),          title: "Thread",          modelSelection: {            instanceId: ProviderInstanceId.make("codex"),            model: "gpt-5-codex",          },          interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,          runtimeMode: "approval-required",          branch: null,          worktreePath: null,          createdAt: now,          updatedAt: now,        },      });       const result = yield* decideOrchestrationCommand({        command: {          type: "thread.turn.start",          commandId: CommandId.make("cmd-turn-start"),          threadId: ThreadId.make("thread-1"),          message: {            messageId: asMessageId("message-user-1"),            role: "user",            text: "hello",            attachments: [],          },          modelSelection: createModelSelection(ProviderInstanceId.make("codex"), "gpt-5.3-codex", [            { id: "reasoningEffort", value: "high" },            { id: "fastMode", value: true },          ]),          interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,          runtimeMode: "approval-required",          createdAt: now,        },        readModel,      });       expect(Array.isArray(result)).toBe(true);      const events = Array.isArray(result) ? result : [result];      expect(events).toHaveLength(2);      expect(events[0]?.type).toBe("thread.message-sent");      const turnStartEvent = events[1];      expect(turnStartEvent?.type).toBe("thread.turn-start-requested");      expect(turnStartEvent?.causationEventId).toBe(events[0]?.eventId ?? null);      if (turnStartEvent?.type !== "thread.turn-start-requested") {        return;      }      expect(turnStartEvent.payload).toMatchObject({        threadId: ThreadId.make("thread-1"),        messageId: asMessageId("message-user-1"),        modelSelection: createModelSelection(ProviderInstanceId.make("codex"), "gpt-5.3-codex", [          { id: "reasoningEffort", value: "high" },          { id: "fastMode", value: true },        ]),        runtimeMode: "approval-required",      });    }),  );   it.effect("emits thread.runtime-mode-set from thread.runtime-mode.set", () =>    Effect.gen(function* () {      const now = "2026-01-01T00:00:00.000Z";      const initial = createEmptyReadModel(now);      const withProject = yield* projectEvent(initial, {        sequence: 1,        eventId: asEventId("evt-project-create"),        aggregateKind: "project",        aggregateId: asProjectId("project-1"),        type: "project.created",        occurredAt: now,        commandId: CommandId.make("cmd-project-create"),        causationEventId: null,        correlationId: CommandId.make("cmd-project-create"),        metadata: {},        payload: {          projectId: asProjectId("project-1"),          title: "Project",          workspaceRoot: "/tmp/project",          defaultModelSelection: null,          scripts: [],          createdAt: now,          updatedAt: now,        },      });      const readModel = yield* projectEvent(withProject, {        sequence: 2,        eventId: asEventId("evt-thread-create"),        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.created",        occurredAt: now,        commandId: CommandId.make("cmd-thread-create"),        causationEventId: null,        correlationId: CommandId.make("cmd-thread-create"),        metadata: {},        payload: {          threadId: ThreadId.make("thread-1"),          projectId: asProjectId("project-1"),          title: "Thread",          modelSelection: {            instanceId: ProviderInstanceId.make("codex"),            model: "gpt-5-codex",          },          interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,          runtimeMode: "full-access",          branch: null,          worktreePath: null,          createdAt: now,          updatedAt: now,        },      });       const result = yield* decideOrchestrationCommand({        command: {          type: "thread.runtime-mode.set",          commandId: CommandId.make("cmd-runtime-mode-set"),          threadId: ThreadId.make("thread-1"),          runtimeMode: "approval-required",          createdAt: now,        },        readModel,      });       const singleResult = Array.isArray(result) ? null : result;      if (singleResult === null) {        throw new Error("Expected a single runtime-mode-set event.");      }      expect(singleResult).toMatchObject({        type: "thread.runtime-mode-set",        payload: {          threadId: ThreadId.make("thread-1"),          runtimeMode: "approval-required",        },      });    }),  );   it.effect("emits thread.interaction-mode-set from thread.interaction-mode.set", () =>    Effect.gen(function* () {      const now = "2026-01-01T00:00:00.000Z";      const initial = createEmptyReadModel(now);      const withProject = yield* projectEvent(initial, {        sequence: 1,        eventId: asEventId("evt-project-create"),        aggregateKind: "project",        aggregateId: asProjectId("project-1"),        type: "project.created",        occurredAt: now,        commandId: CommandId.make("cmd-project-create"),        causationEventId: null,        correlationId: CommandId.make("cmd-project-create"),        metadata: {},        payload: {          projectId: asProjectId("project-1"),          title: "Project",          workspaceRoot: "/tmp/project",          defaultModelSelection: null,          scripts: [],          createdAt: now,          updatedAt: now,        },      });      const readModel = yield* projectEvent(withProject, {        sequence: 2,        eventId: asEventId("evt-thread-create"),        aggregateKind: "thread",        aggregateId: ThreadId.make("thread-1"),        type: "thread.created",        occurredAt: now,        commandId: CommandId.make("cmd-thread-create"),        causationEventId: null,        correlationId: CommandId.make("cmd-thread-create"),        metadata: {},        payload: {          threadId: ThreadId.make("thread-1"),          projectId: asProjectId("project-1"),          title: "Thread",          modelSelection: {            instanceId: ProviderInstanceId.make("codex"),            model: "gpt-5-codex",          },          interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,          runtimeMode: "approval-required",          branch: null,          worktreePath: null,          createdAt: now,          updatedAt: now,        },      });       const result = yield* decideOrchestrationCommand({        command: {          type: "thread.interaction-mode.set",          commandId: CommandId.make("cmd-interaction-mode-set"),          threadId: ThreadId.make("thread-1"),          interactionMode: "plan",          createdAt: now,        },        readModel,      });       const singleResult = Array.isArray(result) ? null : result;      if (singleResult === null) {        throw new Error("Expected a single interaction-mode-set event.");      }      expect(singleResult).toMatchObject({        type: "thread.interaction-mode-set",        payload: {          threadId: ThreadId.make("thread-1"),          interactionMode: "plan",        },      });    }),  );});