1257 lines · 40.7 KB · TypeScript
RawDownload
import fs from "node:fs";import path from "node:path"; import {  ApprovalRequestId,  CommandId,  DEFAULT_PROVIDER_INTERACTION_MODE,  EventId,  MessageId,  ProjectId,  ThreadId,} from "@t3tools/contracts";import { assert, it } from "@effect/vitest";import { Effect, Option, Schema } from "effect"; import type { TestTurnResponse } from "./TestProviderAdapter.integration.ts";import {  gitRefExists,  gitShowFileAtRef,  makeOrchestrationIntegrationHarness,  type OrchestrationIntegrationHarness,} from "./OrchestrationEngineHarness.integration.ts";import { checkpointRefForThreadTurn } from "../src/checkpointing/Utils.ts"; const asMessageId = (value: string): MessageId => MessageId.makeUnsafe(value);const asProjectId = (value: string): ProjectId => ProjectId.makeUnsafe(value);const asEventId = (value: string): EventId => EventId.makeUnsafe(value);const asApprovalRequestId = (value: string): ApprovalRequestId =>  ApprovalRequestId.makeUnsafe(value); const PROJECT_ID = asProjectId("project-1");const THREAD_ID = ThreadId.makeUnsafe("thread-1");const FIXTURE_TURN_ID = "fixture-turn";const APPROVAL_REQUEST_ID = asApprovalRequestId("req-approval-1");type IntegrationProvider = "codex" | "claudeCode"; function nowIso() {  return new Date().toISOString();} class IntegrationWaitTimeoutError extends Schema.TaggedErrorClass<IntegrationWaitTimeoutError>()(  "IntegrationWaitTimeoutError",  {    description: Schema.String,  },) {} const sleep = (ms: number) => Effect.sleep(ms); function waitForSync<A>(  read: () => A,  predicate: (value: A) => boolean,  description: string,  timeoutMs = 3000,): Effect.Effect<A, never> {  return Effect.gen(function* () {    const deadline = Date.now() + timeoutMs;     while (true) {      const value = read();      if (predicate(value)) {        return value;      }      if (Date.now() >= deadline) {        return yield* Effect.die(new IntegrationWaitTimeoutError({ description }));      }      yield* sleep(10);    }  });} function runtimeBase(eventId: string, createdAt: string, provider: IntegrationProvider = "codex") {  return {    eventId: asEventId(eventId),    provider,    createdAt,  };} function withHarness<A, E>(  use: (harness: OrchestrationIntegrationHarness) => Effect.Effect<A, E>,  provider: IntegrationProvider = "codex",) {  return Effect.acquireUseRelease(    makeOrchestrationIntegrationHarness({ provider }),    use,    (harness) => harness.dispose,  );} function withRealCodexHarness<A, E>(  use: (harness: OrchestrationIntegrationHarness) => Effect.Effect<A, E>,) {  return Effect.acquireUseRelease(    makeOrchestrationIntegrationHarness({ provider: "codex", realCodex: true }),    use,    (harness) => harness.dispose,  );} const seedProjectAndThread = (harness: OrchestrationIntegrationHarness) =>  Effect.gen(function* () {    const createdAt = nowIso();     yield* harness.engine.dispatch({      type: "project.create",      commandId: CommandId.makeUnsafe("cmd-project-create"),      projectId: PROJECT_ID,      title: "Integration Project",      workspaceRoot: harness.workspaceDir,      defaultModel: "gpt-5-codex",      createdAt,    });     yield* harness.engine.dispatch({      type: "thread.create",      commandId: CommandId.makeUnsafe("cmd-thread-create"),      threadId: THREAD_ID,      projectId: PROJECT_ID,      title: "Integration Thread",      model: "gpt-5-codex",      interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,      runtimeMode: "approval-required",      branch: null,      worktreePath: harness.workspaceDir,      createdAt,    });  }); const startTurn = (input: {  readonly harness: OrchestrationIntegrationHarness;  readonly commandId: string;  readonly messageId: string;  readonly text: string;  readonly provider?: IntegrationProvider;}) =>  input.harness.engine.dispatch({    type: "thread.turn.start",    commandId: CommandId.makeUnsafe(input.commandId),    threadId: THREAD_ID,    message: {      messageId: asMessageId(input.messageId),      role: "user",      text: input.text,      attachments: [],    },    ...(input.provider !== undefined ? { provider: input.provider } : {}),    interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,    runtimeMode: "approval-required",    createdAt: nowIso(),  }); it.live("runs a single turn end-to-end and persists checkpoint state in sqlite + git", () =>  withHarness((harness) =>    Effect.gen(function* () {      yield* seedProjectAndThread(harness);       const turnResponse: TestTurnResponse = {        events: [          {            type: "turn.started",            ...runtimeBase("evt-single-1", "2026-02-24T10:00:00.000Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,          },          {            type: "message.delta",            ...runtimeBase("evt-single-2", "2026-02-24T10:00:00.100Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            delta: "Single turn response.\n",          },          {            type: "turn.completed",            ...runtimeBase("evt-single-3", "2026-02-24T10:00:00.200Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            status: "completed",          },        ],      };       yield* harness.adapterHarness.queueTurnResponseForNextSession(turnResponse);      yield* startTurn({        harness,        commandId: "cmd-turn-start-single",        messageId: "msg-user-single",        text: "Say hello",      });       const thread = yield* harness.waitForThread(        THREAD_ID,        (entry) =>          entry.session?.status === "ready" &&          entry.messages.some(            (message) => message.role === "assistant" && message.streaming === false,          ) &&          entry.checkpoints.length === 1,      );      assert.equal(thread.checkpoints[0]?.status, "ready");      assert.equal(thread.checkpoints[0]?.checkpointTurnCount, 1);       const checkpointRows = yield* harness.checkpointRepository.listByThreadId({        threadId: THREAD_ID,      });      assert.equal(checkpointRows.length, 1);      assert.equal(checkpointRows[0]?.checkpointTurnCount, 1);      assert.equal(checkpointRows[0]?.status, "ready");      assert.deepEqual(checkpointRows[0]?.files, []);       yield* harness.waitForDomainEvent((event) => event.type === "thread.turn-diff-completed");       const ref0 = checkpointRefForThreadTurn(THREAD_ID, 0);      const ref1 = checkpointRefForThreadTurn(THREAD_ID, 1);      assert.equal(gitRefExists(harness.workspaceDir, ref0), true);      assert.equal(gitRefExists(harness.workspaceDir, ref1), true);      assert.equal(gitShowFileAtRef(harness.workspaceDir, ref0, "README.md"), "v1\n");      assert.equal(gitShowFileAtRef(harness.workspaceDir, ref1, "README.md"), "v1\n");    }),  ),); it.live.skipIf(!process.env.CODEX_BINARY_PATH)(  "keeps the same Codex provider thread across runtime mode switches",  () =>    withRealCodexHarness((harness) =>      Effect.gen(function* () {        const createdAt = nowIso();         yield* harness.engine.dispatch({          type: "project.create",          commandId: CommandId.makeUnsafe("cmd-project-create-real-codex"),          projectId: PROJECT_ID,          title: "Integration Project",          workspaceRoot: harness.workspaceDir,          defaultModel: "gpt-5.3-codex",          createdAt,        });         yield* harness.engine.dispatch({          type: "thread.create",          commandId: CommandId.makeUnsafe("cmd-thread-create-real-codex"),          threadId: THREAD_ID,          projectId: PROJECT_ID,          title: "Integration Thread",          model: "gpt-5.3-codex",          interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,          runtimeMode: "full-access",          branch: null,          worktreePath: harness.workspaceDir,          createdAt,        });         yield* harness.engine.dispatch({          type: "thread.turn.start",          commandId: CommandId.makeUnsafe("cmd-turn-start-real-codex-1"),          threadId: THREAD_ID,          message: {            messageId: asMessageId("msg-real-codex-1"),            role: "user",            text: "Reply with exactly ALPHA.",            attachments: [],          },          interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,          runtimeMode: "full-access",          createdAt: nowIso(),        });         const firstThread = yield* harness.waitForThread(          THREAD_ID,          (entry) =>            entry.session?.status === "ready" &&            entry.session.providerName === "codex" &&            entry.messages.some(              (message) => message.role === "assistant" && message.streaming === false,            ),          180_000,        );        assert.equal(firstThread.session?.threadId, "thread-1");         yield* harness.engine.dispatch({          type: "thread.turn.start",          commandId: CommandId.makeUnsafe("cmd-turn-start-real-codex-2"),          threadId: THREAD_ID,          message: {            messageId: asMessageId("msg-real-codex-2"),            role: "user",            text: "Reply with exactly BETA.",            attachments: [],          },          interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,          runtimeMode: "approval-required",          createdAt: nowIso(),        });         const secondThread = yield* harness.waitForThread(          THREAD_ID,          (entry) =>            entry.session?.status === "ready" &&            entry.session.providerName === "codex" &&            entry.session.runtimeMode === "approval-required" &&            entry.messages.some(              (message) => message.role === "assistant" && message.text.includes("BETA"),            ),          180_000,        );        assert.equal(secondThread.session?.threadId, "thread-1");      }),    ),); it.live("runs multi-turn file edits and persists checkpoint diffs", () =>  withHarness((harness) =>    Effect.gen(function* () {      yield* seedProjectAndThread(harness);       yield* harness.adapterHarness.queueTurnResponseForNextSession({        events: [          {            type: "turn.started",            ...runtimeBase("evt-multi-1", "2026-02-24T10:01:00.000Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,          },          {            type: "tool.started",            ...runtimeBase("evt-multi-2", "2026-02-24T10:01:00.100Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            toolKind: "command",            title: "Edit file",            detail: "README.md",          },          {            type: "tool.completed",            ...runtimeBase("evt-multi-3", "2026-02-24T10:01:00.200Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            toolKind: "command",            title: "Edit file",            detail: "README.md",          },          {            type: "message.delta",            ...runtimeBase("evt-multi-4", "2026-02-24T10:01:00.300Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            delta: "Updated README to v2.\n",          },          {            type: "turn.completed",            ...runtimeBase("evt-multi-5", "2026-02-24T10:01:00.400Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            status: "completed",          },        ],        mutateWorkspace: ({ cwd }) =>          Effect.sync(() => {            fs.writeFileSync(path.join(cwd, "README.md"), "v2\n", "utf8");          }),      });       yield* startTurn({        harness,        commandId: "cmd-turn-start-multi-1",        messageId: "msg-user-multi-1",        text: "Make first edit",      });       yield* harness.waitForThread(        THREAD_ID,        (entry) => entry.checkpoints.length === 1 && entry.session?.threadId === "thread-1",      );       yield* harness.adapterHarness.queueTurnResponse(THREAD_ID, {        events: [          {            type: "turn.started",            ...runtimeBase("evt-multi-6", "2026-02-24T10:02:00.000Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,          },          {            type: "message.delta",            ...runtimeBase("evt-multi-7", "2026-02-24T10:02:00.100Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            delta: "Updated README to v3.\n",          },          {            type: "turn.completed",            ...runtimeBase("evt-multi-8", "2026-02-24T10:02:00.200Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            status: "completed",          },        ],        mutateWorkspace: ({ cwd }) =>          Effect.sync(() => {            fs.writeFileSync(path.join(cwd, "README.md"), "v3\n", "utf8");          }),      });       yield* startTurn({        harness,        commandId: "cmd-turn-start-multi-2",        messageId: "msg-user-multi-2",        text: "Make second edit",      });       const secondTurnThread = yield* harness.waitForThread(        THREAD_ID,        (entry) =>          entry.latestTurn?.turnId === "turn-2" &&          entry.checkpoints.length === 2 &&          entry.checkpoints.some((checkpoint) => checkpoint.checkpointTurnCount === 2),      );      const secondCheckpoint = secondTurnThread.checkpoints.find(        (checkpoint) => checkpoint.checkpointTurnCount === 2,      );      assert.equal(        secondCheckpoint?.files.some((file) => file.path === "README.md"),        true,      );       const checkpointRows = yield* harness.checkpointRepository.listByThreadId({        threadId: THREAD_ID,      });      assert.deepEqual(        checkpointRows.map((row) => row.checkpointTurnCount),        [1, 2],      );       const incrementalDiff = yield* harness.checkpointStore.diffCheckpoints({        cwd: harness.workspaceDir,        fromCheckpointRef: checkpointRefForThreadTurn(THREAD_ID, 1),        toCheckpointRef: checkpointRefForThreadTurn(THREAD_ID, 2),        fallbackFromToHead: false,      });      assert.equal(incrementalDiff.includes("README.md"), true);       const fullDiff = yield* harness.checkpointStore.diffCheckpoints({        cwd: harness.workspaceDir,        fromCheckpointRef: checkpointRefForThreadTurn(THREAD_ID, 0),        toCheckpointRef: checkpointRefForThreadTurn(THREAD_ID, 2),        fallbackFromToHead: false,      });      assert.equal(fullDiff.includes("README.md"), true);       assert.equal(        gitShowFileAtRef(          harness.workspaceDir,          checkpointRefForThreadTurn(THREAD_ID, 1),          "README.md",        ),        "v2\n",      );      assert.equal(        gitShowFileAtRef(          harness.workspaceDir,          checkpointRefForThreadTurn(THREAD_ID, 2),          "README.md",        ),        "v3\n",      );    }),  ),); it.live("tracks approval requests and resolves pending approvals on user response", () =>  withHarness((harness) =>    Effect.gen(function* () {      yield* seedProjectAndThread(harness);       yield* harness.adapterHarness.queueTurnResponseForNextSession({        events: [          {            type: "turn.started",            ...runtimeBase("evt-approval-1", "2026-02-24T10:03:00.000Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,          },          {            type: "approval.requested",            ...runtimeBase("evt-approval-2", "2026-02-24T10:03:00.100Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            requestId: APPROVAL_REQUEST_ID,            requestKind: "command",            detail: "Approve command execution",          },          {            type: "turn.completed",            ...runtimeBase("evt-approval-3", "2026-02-24T10:03:00.200Z"),            threadId: THREAD_ID,            turnId: FIXTURE_TURN_ID,            status: "completed",          },        ],

Lines 1–500 of 1257.