1942 lines · 68.2 KB · TypeScript
RawDownload
// @effect-diagnostics nodeBuiltinImport:offimport * as NodeFS from "node:fs";import * as NodeOS from "node:os";import * as NodePath from "node:path";import * as NodeChildProcess from "node:child_process"; import {  ProviderDriverKind,  ProviderRuntimeEvent,  ProviderSession,  ProviderInstanceId,} from "@t3tools/contracts";import {  CommandId,  CheckpointRef,  DEFAULT_PROVIDER_INTERACTION_MODE,  EventId,  MessageId,  ProjectId,  ThreadId,  TurnId,} from "@t3tools/contracts";import * as NodeServices from "@effect/platform-node/NodeServices";import * as Clock from "effect/Clock";import * as Deferred from "effect/Deferred";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Layer from "effect/Layer";import * as ManagedRuntime from "effect/ManagedRuntime";import * as PubSub from "effect/PubSub";import * as Queue from "effect/Queue";import * as Scope from "effect/Scope";import * as Stream from "effect/Stream";import { it as effectIt } from "@effect/vitest";import { afterEach, describe, expect, it, vi } from "vite-plus/test"; import * as CheckpointStore from "../../checkpointing/CheckpointStore.ts";import * as VcsDriverRegistry from "../../vcs/VcsDriverRegistry.ts";import * as VcsProcess from "../../vcs/VcsProcess.ts";import { VcsStatusBroadcaster } from "../../vcs/VcsStatusBroadcaster.ts";import * as RepositoryIdentityResolver from "../../project/RepositoryIdentityResolver.ts";import { CheckpointReactorLive } from "./CheckpointReactor.ts";import { OrchestrationEngineLive } from "./OrchestrationEngine.ts";import { OrchestrationProjectionPipelineLive } from "./ProjectionPipeline.ts";import { OrchestrationProjectionSnapshotQueryLive } from "./ProjectionSnapshotQuery.ts";import * as ThreadBackgroundLiveness from "../ThreadBackgroundLiveness.ts";import * as ThreadPlanProgress from "../ThreadPlanProgress.ts";import { RuntimeReceiptBusTest } from "./RuntimeReceiptBus.ts";import * as RuntimeReceiptBus from "../Services/RuntimeReceiptBus.ts";import { OrchestrationEventStoreLive } from "../../persistence/Layers/OrchestrationEventStore.ts";import { OrchestrationCommandReceiptRepositoryLive } from "../../persistence/Layers/OrchestrationCommandReceipts.ts";import { SqlitePersistenceMemory } from "../../persistence/Layers/Sqlite.ts";import {  OrchestrationEngineService,  type OrchestrationEngineShape,} from "../Services/OrchestrationEngine.ts";import { CheckpointReactor } from "../Services/CheckpointReactor.ts";import { ProjectionSnapshotQuery } from "../Services/ProjectionSnapshotQuery.ts";import {  ProviderService,  type ProviderServiceShape,} from "../../provider/Services/ProviderService.ts";import { checkpointRefForThreadTurn } from "../../checkpointing/Utils.ts";import { ProviderValidationError } from "../../provider/Errors.ts";import { ServerConfig } from "../../config.ts";import * as WorkspaceEntries from "../../workspace/WorkspaceEntries.ts";import * as WorkspacePaths from "../../workspace/WorkspacePaths.ts";import { PullRequestService } from "../../pullRequest/PullRequestService.ts"; const asProjectId = (value: string): ProjectId => ProjectId.make(value);const asTurnId = (value: string): TurnId => TurnId.make(value); type LegacyProviderRuntimeEvent = {  readonly type: string;  readonly eventId: EventId;  readonly provider: ProviderDriverKind;  readonly createdAt: string;  readonly threadId: ThreadId;  readonly turnId?: string | undefined;  readonly itemId?: string | undefined;  readonly requestId?: string | undefined;  readonly payload?: unknown | undefined;  readonly [key: string]: unknown;}; function createProviderServiceHarness(  cwd: string,  hasSession = true,  sessionCwd = cwd,  providerName: ProviderSession["provider"] = ProviderDriverKind.make("codex"),) {  const now = "2026-01-01T00:00:00.000Z";  const runtimeEventPubSub = Effect.runSync(PubSub.unbounded<ProviderRuntimeEvent>());  const rollbackConversation = vi.fn(    (_input: { readonly threadId: ThreadId; readonly numTurns: number }) => Effect.void,  );  const assertConversationRollbackSupported = vi.fn<    ProviderServiceShape["assertConversationRollbackSupported"]  >(() => Effect.void);   const unsupported = <A>() =>    Effect.die(new Error("Unsupported provider call in test")) as Effect.Effect<A, never>;  const listSessions = () =>    hasSession      ? Effect.succeed([          {            provider: providerName,            status: "ready",            runtimeMode: "full-access",            threadId: ThreadId.make("thread-1"),            cwd: sessionCwd,            createdAt: now,            updatedAt: now,          },        ] satisfies ReadonlyArray<ProviderSession>)      : Effect.succeed([] as ReadonlyArray<ProviderSession>);  const service: ProviderServiceShape = {    startSession: () => unsupported(),    sendTurn: () => unsupported(),    compactThread: () => unsupported(),    interruptTurn: () => unsupported(),    respondToRequest: () => unsupported(),    respondToUserInput: () => unsupported(),    stopSession: () => unsupported(),    listSessions,    getCapabilities: () => Effect.succeed({ sessionModelSwitch: "in-session" }),    assertConversationRollbackSupported,    getInstanceInfo: (instanceId) =>      Effect.succeed({        instanceId,        driverKind: ProviderDriverKind.make(providerName),        displayName: undefined,        enabled: true,        continuationIdentity: {          driverKind: ProviderDriverKind.make(providerName),          continuationKey: `${providerName}:instance:${instanceId}`,        },      }),    rollbackConversation,    uploadFeedback: () => unsupported(),    get streamEvents() {      return Stream.fromPubSub(runtimeEventPubSub);    },  };   const emit = (event: LegacyProviderRuntimeEvent): void => {    Effect.runSync(PubSub.publish(runtimeEventPubSub, event as unknown as ProviderRuntimeEvent));  };   return {    service,    assertConversationRollbackSupported,    rollbackConversation,    emit,  };} async function waitForThread(  readModel: () => Promise<{    readonly threads: ReadonlyArray<{      readonly id: ThreadId;      readonly latestTurn: { readonly turnId: string } | null;      readonly checkpoints: ReadonlyArray<{ readonly checkpointTurnCount: number }>;      readonly activities: ReadonlyArray<{ readonly kind: string }>;    }>;  }>,  predicate: (thread: {    latestTurn: { turnId: string } | null;    checkpoints: ReadonlyArray<{ checkpointTurnCount: number }>;    activities: ReadonlyArray<{ kind: string }>;  }) => boolean,  timeoutMs = 15_000,) {  const deadline = (await Effect.runPromise(Clock.currentTimeMillis)) + timeoutMs;  const poll = async (): Promise<{    latestTurn: { turnId: string } | null;    checkpoints: ReadonlyArray<{ checkpointTurnCount: number }>;    activities: ReadonlyArray<{ kind: string }>;  }> => {    const snapshot = await readModel();    const thread = snapshot.threads.find((entry) => entry.id === ThreadId.make("thread-1"));    if (thread && predicate(thread)) {      return thread;    }    if ((await Effect.runPromise(Clock.currentTimeMillis)) >= deadline) {      throw new Error("Timed out waiting for thread state.");    }    await Effect.runPromise(Effect.sleep("10 millis"));    return poll();  };  return poll();} async function waitForEvent(  engine: OrchestrationEngineShape,  predicate: (event: { type: string }) => boolean,  timeoutMs = 15_000,) {  const deadline = (await Effect.runPromise(Clock.currentTimeMillis)) + timeoutMs;  const poll = async () => {    const events = await Effect.runPromise(      Stream.runCollect(engine.readEvents(0)).pipe(Effect.map((chunk) => Array.from(chunk))),    );    if (events.some(predicate)) {      return events;    }    if ((await Effect.runPromise(Clock.currentTimeMillis)) >= deadline) {      throw new Error("Timed out waiting for orchestration event.");    }    await Effect.runPromise(Effect.sleep("10 millis"));    return poll();  };  return poll();} function runGit(cwd: string, args: ReadonlyArray<string>) {  return NodeChildProcess.execFileSync("git", args, {    cwd,    stdio: ["ignore", "pipe", "pipe"],    encoding: "utf8",  });} function createGitRepository() {  const cwd = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "t3-checkpoint-handler-"));  runGit(cwd, ["init", "--initial-branch=main"]);  runGit(cwd, ["config", "user.email", "test@example.com"]);  runGit(cwd, ["config", "user.name", "Test User"]);  NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v1\n", "utf8");  runGit(cwd, ["add", "."]);  runGit(cwd, ["commit", "-m", "Initial"]);  return cwd;} function gitRefExists(cwd: string, ref: string): boolean {  try {    runGit(cwd, ["show-ref", "--verify", "--quiet", ref]);    return true;  } catch {    return false;  }} function gitShowFileAtRef(cwd: string, ref: string, filePath: string): string {  return runGit(cwd, ["show", `${ref}:${filePath}`]);} async function waitForGitRefExists(cwd: string, ref: string, timeoutMs = 15_000) {  const deadline = (await Effect.runPromise(Clock.currentTimeMillis)) + timeoutMs;  const poll = async (): Promise<void> => {    if (gitRefExists(cwd, ref)) {      return;    }    if ((await Effect.runPromise(Clock.currentTimeMillis)) >= deadline) {      throw new Error(`Timed out waiting for git ref '${ref}'.`);    }    await Effect.runPromise(Effect.sleep("10 millis"));    return poll();  };  return poll();} describe("CheckpointReactor", () => {  let runtime: ManagedRuntime.ManagedRuntime<    | OrchestrationEngineService    | CheckpointReactor    | CheckpointStore.CheckpointStore    | ProjectionSnapshotQuery    | RuntimeReceiptBus.RuntimeReceiptBus,    unknown  > | null = null;  let scope: Scope.Closeable | null = null;  const tempDirs: string[] = [];   afterEach(async () => {    if (scope) {      await Effect.runPromise(Scope.close(scope, Exit.void));    }    scope = null;    if (runtime) {      await runtime.dispose();    }    runtime = null;    while (tempDirs.length > 0) {      const dir = tempDirs.pop();      if (dir) {        NodeFS.rmSync(dir, { recursive: true, force: true });      }    }  });   async function createHarness(options?: {    readonly hasSession?: boolean;    readonly seedFilesystemCheckpoints?: boolean;    readonly initializeGit?: boolean;    readonly projectWorkspaceRoot?: string;    readonly threadWorktreePath?: string | null;    readonly threadBranch?: string | null;    readonly secondThreadSharingWorktree?: boolean;    readonly localStatusRefName?: string | null;    readonly providerSessionCwd?: string;    readonly providerName?: ProviderDriverKind;    readonly gitStatusRefreshCalls?: Array<string>;    readonly pullRequestRefreshCalls?: Array<string>;    readonly pullRequestRefresh?: Effect.Effect<void>;  }) {    const cwd = createGitRepository();    if (options?.initializeGit === false) {      NodeFS.rmSync(NodePath.join(cwd, ".git"), { recursive: true });    }    tempDirs.push(cwd);    const provider = createProviderServiceHarness(      cwd,      options?.hasSession ?? true,      options?.providerSessionCwd ?? cwd,      options?.providerName ?? ProviderDriverKind.make("codex"),    );    const orchestrationLayer = OrchestrationEngineLive.pipe(      Layer.provide(OrchestrationProjectionSnapshotQueryLive),      Layer.provide(ThreadBackgroundLiveness.layer),      Layer.provide(ThreadPlanProgress.layer),      Layer.provide(OrchestrationProjectionPipelineLive),      Layer.provide(OrchestrationEventStoreLive),      Layer.provide(OrchestrationCommandReceiptRepositoryLive),      Layer.provide(RepositoryIdentityResolver.layer),      Layer.provide(SqlitePersistenceMemory),    );    const projectionSnapshotLayer = OrchestrationProjectionSnapshotQueryLive.pipe(      Layer.provide(ThreadBackgroundLiveness.layer),      Layer.provide(ThreadPlanProgress.layer),      Layer.provide(RepositoryIdentityResolver.layer),      Layer.provide(SqlitePersistenceMemory),    );     const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), {      prefix: "t3-checkpoint-reactor-test-",    });    const pullRequestRefreshes: number[] = [];    const refreshAfterTurn = Effect.sync(() => void pullRequestRefreshes.push(1));    const vcsStatusBroadcasterLayer = Layer.succeed(VcsStatusBroadcaster, {      getStatus: () => Effect.die("getStatus should not be called in this test"),      refreshLocalStatus: (cwd: string) =>        Effect.sync(() => {          options?.gitStatusRefreshCalls?.push(cwd);        }).pipe(          Effect.as({            isRepo: true,            hasPrimaryRemote: false,            isDefaultRef:              options?.localStatusRefName === undefined || options.localStatusRefName === "main",            refName:              options?.localStatusRefName !== undefined ? options.localStatusRefName : "main",            hasWorkingTreeChanges: false,            workingTree: { files: [], insertions: 0, deletions: 0 },          }),        ),      refreshStatus: () => Effect.die("refreshStatus should not be called in this test"),      refreshPullRequestStatus: (cwd: string) =>        Effect.sync(() => {          options?.pullRequestRefreshCalls?.push(cwd);        }).pipe(Effect.andThen(options?.pullRequestRefresh ?? Effect.void), Effect.as(null)),      streamStatus: () => Stream.empty,    });     const layer = CheckpointReactorLive.pipe(      Layer.provideMerge(orchestrationLayer),      Layer.provideMerge(projectionSnapshotLayer),      Layer.provideMerge(RuntimeReceiptBusTest),      Layer.provideMerge(Layer.succeed(ProviderService, provider.service)),      Layer.provideMerge(Layer.mock(PullRequestService)({ refreshAfterTurn })),      Layer.provideMerge(vcsStatusBroadcasterLayer),      Layer.provideMerge(CheckpointStore.layer.pipe(Layer.provide(VcsDriverRegistry.layer))),      Layer.provideMerge(        WorkspaceEntries.layer.pipe(          Layer.provide(WorkspacePaths.layer),          Layer.provideMerge(VcsDriverRegistry.layer),        ),      ),      Layer.provideMerge(WorkspacePaths.layer),      Layer.provideMerge(VcsProcess.layer),      Layer.provideMerge(ServerConfigLayer),      Layer.provideMerge(NodeServices.layer),    );     runtime = ManagedRuntime.make(layer);    const engine = await runtime.runPromise(Effect.service(OrchestrationEngineService));    const snapshotQuery = await runtime.runPromise(Effect.service(ProjectionSnapshotQuery));    const reactor = await runtime.runPromise(Effect.service(CheckpointReactor));    const checkpointStore = await runtime.runPromise(      Effect.service(CheckpointStore.CheckpointStore),    );    const receiptBus = await runtime.runPromise(      Effect.service(RuntimeReceiptBus.RuntimeReceiptBus),    );    const testScope = await Effect.runPromise(Scope.make("sequential"));    scope = testScope;    const receipts = await Effect.runPromise(      Effect.gen(function* () {        const receipts = yield* Queue.unbounded<RuntimeReceiptBus.OrchestrationRuntimeReceipt>();        yield* Stream.runForEach(receiptBus.streamEventsForTest, (receipt) =>          Queue.offer(receipts, receipt),        ).pipe(Effect.forkIn(testScope, { startImmediately: true }));        yield* reactor.start().pipe(Scope.provide(testScope));        return receipts;      }),    );    const drain = () => Effect.runPromise(reactor.drain);     const createdAt = "2026-01-01T00:00:00.000Z";    await Effect.runPromise(      engine.dispatch({        type: "project.create",        commandId: CommandId.make("cmd-project-create"),        projectId: asProjectId("project-1"),        title: "Test Project",        workspaceRoot: options?.projectWorkspaceRoot ?? cwd,        defaultModelSelection: {          instanceId: ProviderInstanceId.make("codex"),          model: "gpt-5-codex",        },        createdAt,      }),    );    await Effect.runPromise(      engine        .dispatch({          type: "thread.create",          commandId: CommandId.make("cmd-thread-create"),          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: options?.threadBranch ?? null,          worktreePath: options?.threadWorktreePath ?? cwd,          createdAt,        })        .pipe(          options?.secondThreadSharingWorktree            ? Effect.andThen(                engine.dispatch({                  type: "thread.create",                  commandId: CommandId.make("cmd-thread-create-2"),                  threadId: ThreadId.make("thread-2"),                  projectId: asProjectId("project-1"),                  title: "Thread 2",                  modelSelection: {                    instanceId: ProviderInstanceId.make("codex"),                    model: "gpt-5-codex",                  },                  interactionMode: DEFAULT_PROVIDER_INTERACTION_MODE,                  runtimeMode: "approval-required",                  branch: null,                  worktreePath: options?.threadWorktreePath ?? cwd,                  createdAt,                }),              )            : Effect.asVoid,        ),    );     if (options?.seedFilesystemCheckpoints ?? true) {      await runtime.runPromise(        checkpointStore.captureCheckpoint({          cwd,          checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 0),        }),      );      NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v2\n", "utf8");      await runtime.runPromise(        checkpointStore.captureCheckpoint({          cwd,          checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 1),        }),      );      NodeFS.writeFileSync(NodePath.join(cwd, "README.md"), "v3\n", "utf8");      await runtime.runPromise(        checkpointStore.captureCheckpoint({          cwd,          checkpointRef: checkpointRefForThreadTurn(ThreadId.make("thread-1"), 2),        }),      );    }     return {      engine,      readModel: () => Effect.runPromise(snapshotQuery.getSnapshot()),      provider,      cwd,      drain,      nextReceipt: Queue.take(receipts),      pullRequestRefreshes,    };  }   effectIt.effect("captures baseline and large turn summaries before completion receipts", () =>

Lines 1–500 of 1942.