308 lines · 10.9 KB · TypeScript
RawDownload
import { assert, it } from "@effect/vitest";import {  type ModelSelection,  NodeId,  type OrchestrationV2ProviderThread,  type OrchestrationV2ThreadProjection,  ProjectId,  ProviderDriverKind,  ProviderInstanceId,  ProviderSessionId,  ProviderThreadId,  ProviderTurnId,  RunAttemptId,  RunId,  ThreadId,} from "@t3tools/contracts";import * as DateTime from "effect/DateTime";import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Ref from "effect/Ref";import * as Stream from "effect/Stream"; import { CodexProviderCapabilitiesV2 } from "./Adapters/CodexAdapterV2.ts";import { ProjectionStoreV2 } from "./ProjectionStore.ts";import type { ProviderAdapterV2SessionRuntime } from "./ProviderAdapter.ts";import { ProviderSessionManagerV2 } from "./ProviderSessionManager.ts";import {  layer as providerTurnControlLayer,  ProviderTurnControlServiceV2,} from "./ProviderTurnControlService.ts"; const driver = ProviderDriverKind.make("codex");const providerInstanceId = ProviderInstanceId.make("codex");const modelSelection = {  instanceId: providerInstanceId,  model: "gpt-5.4",} satisfies ModelSelection; function makeProjection(input: {  readonly now: DateTime.Utc;  readonly threadId: ThreadId;  readonly providerThread: OrchestrationV2ProviderThread;  readonly providerTurnId: ProviderTurnId;  readonly attemptId: RunAttemptId;}): OrchestrationV2ThreadProjection {  const runId = RunId.make("run:restart-session");  const nodeId = NodeId.make("node:restart-session");  return {    thread: {      createdBy: "user",      creationSource: "web",      id: input.threadId,      projectId: ProjectId.make("project:restart-session"),      title: "Restart session",      providerInstanceId,      modelSelection,      runtimeMode: "full-access",      interactionMode: "default",      branch: null,      worktreePath: "/workspace",      activeProviderThreadId: input.providerThread.id,      lineage: {        parentThreadId: null,        relationshipToParent: null,        rootThreadId: input.threadId,      },      forkedFrom: null,      createdAt: input.now,      updatedAt: input.now,      archivedAt: null,      settledOverride: null,      settledAt: null,      lastVisitedAt: null,      deletedAt: null,    },    runs: [],    attempts: [      {        id: input.attemptId,        runId,        attemptOrdinal: 1,        rootNodeId: nodeId,        providerInstanceId,        providerThreadId: input.providerThread.id,        providerTurnId: input.providerTurnId,        reason: "initial",        status: "superseded",        startedAt: input.now,        completedAt: input.now,      },    ],    nodes: [],    subagents: [],    providerSessions: [],    providerThreads: [input.providerThread],    providerTurns: [      {        id: input.providerTurnId,        providerThreadId: input.providerThread.id,        nodeId,        runAttemptId: input.attemptId,        nativeTurnRef: {          driver,          nativeId: "native-turn:restart-session",          strength: "strong",        },        ordinal: 1,        status: "running",        startedAt: input.now,        completedAt: null,      },    ],    runtimeRequests: [],    messages: [],    plans: [],    turnItems: [],    checkpointScopes: [],    checkpoints: [],    contextHandoffs: [],    contextTransfers: [],    visibleTurnItems: [],    updatedAt: input.now,  };} it.effect(  "interrupts the historical session only for the exact committed restart replacement",  () =>    Effect.gen(function* () {      const now = yield* DateTime.now;      const threadId = ThreadId.make("thread:restart-session");      const oldSessionId = ProviderSessionId.make("provider-session:restart-session:old");      const replacementSessionId = ProviderSessionId.make(        "provider-session:restart-session:replacement",      );      const unrelatedSessionId = ProviderSessionId.make(        "provider-session:restart-session:unrelated",      );      const providerThreadId = ProviderThreadId.make("provider-thread:restart-session");      const providerTurnId = ProviderTurnId.make("provider-turn:restart-session");      const attemptId = RunAttemptId.make("run-attempt:restart-session");      const providerThread: OrchestrationV2ProviderThread = {        id: providerThreadId,        driver,        providerInstanceId,        // The restart command has already projected this replacement binding        // before the process-bound restart effect executes.        providerSessionId: replacementSessionId,        appThreadId: threadId,        ownerNodeId: null,        nativeThreadRef: {          driver,          nativeId: "native-thread:restart-session",          strength: "strong",        },        nativeConversationHeadRef: null,        status: "not_loaded",        firstRunOrdinal: 1,        lastRunOrdinal: 1,        handoffIds: [],        forkedFrom: null,        createdAt: now,        updatedAt: now,      };      const projection = yield* Ref.make(        makeProjection({ now, threadId, providerThread, providerTurnId, attemptId }),      );      const interruptedThread = yield* Ref.make<OrchestrationV2ProviderThread | null>(null);      const providerSession = {        id: oldSessionId,        driver,        providerInstanceId,        status: "running" as const,        cwd: "/workspace",        model: modelSelection.model,        capabilities: CodexProviderCapabilitiesV2,        createdAt: now,        updatedAt: now,        lastError: null,      };      const runtime: ProviderAdapterV2SessionRuntime = {        instanceId: providerInstanceId,        driver,        providerSessionId: oldSessionId,        providerSession,        events: Stream.empty,        ensureThread: () => Effect.die("unused ensureThread"),        resumeThread: () => Effect.die("unused resumeThread"),        startTurn: () => Effect.die("unused startTurn"),        steerTurn: () => Effect.die("unused steerTurn"),        interruptTurn: ({ providerThread: target }) =>          Effect.all(            [              Ref.set(interruptedThread, target),              Ref.update(projection, (current) => ({                ...current,                providerTurns: current.providerTurns.map((turn) =>                  turn.id === providerTurnId                    ? { ...turn, status: "interrupted" as const, completedAt: now }                    : turn,                ),              })),            ],            { discard: true },          ),        respondToRuntimeRequest: () => Effect.die("unused respondToRuntimeRequest"),        readThreadSnapshot: () => Effect.die("unused readThreadSnapshot"),        rollbackThread: () => Effect.die("unused rollbackThread"),        forkThread: () => Effect.die("unused forkThread"),      };      const projectionLayer = Layer.succeed(        ProjectionStoreV2,        ProjectionStoreV2.of({          apply: () => Effect.void,          getShellSnapshot: () => Effect.die("unused getShellSnapshot"),          getThreadShell: () => Effect.die("unused getThreadShell"),          getThread: () => Ref.get(projection).pipe(Effect.map((state) => state.thread)),          getSettlementCandidates: () => Effect.die("unused getSettlementCandidates"),          getThreadProjection: () => Effect.die("control effects must not load transcript"),          getRuntimeRequest: () => Effect.die("unused getRuntimeRequest"),          getPendingNativeUserInputs: () => Effect.die("unused getPendingNativeUserInputs"),          getProviderControlContext: (_threadId, target) =>            Ref.get(projection).pipe(              Effect.map((current) => ({                providerThread: current.providerThreads.find(                  (thread) => thread.id === target.providerThreadId,                ),                providerTurn: current.providerTurns.find(                  (turn) => turn.id === target.providerTurnId,                ),                attempt: current.attempts.find((attempt) => attempt.id === target.attemptId),                message: undefined,                run: undefined,              })),            ),          getCheckpointContext: () => Effect.die("not used"),          getRecoveryThreadIds: () => Effect.die("unused getRecoveryThreadIds"),          getUnreadableThreadIds: () => Effect.die("unused getUnreadableThreadIds"),          getThreadSnapshot: () => Effect.die("unused getThreadSnapshot"),          getThreadSnapshotWindow: () => Effect.die("unused getThreadSnapshotWindow"),        }),      );      const sessionManagerLayer = Layer.succeed(        ProviderSessionManagerV2,        ProviderSessionManagerV2.of({          shutdown: Effect.void,          open: () => Effect.die("unused open"),          get: (providerSessionId) =>            Effect.succeed(              providerSessionId === oldSessionId ? Option.some(runtime) : Option.none(),            ),          close: () => Effect.void,          release: () => Effect.void,          detach: () => Effect.void,        }),      );      const controlLayer = providerTurnControlLayer.pipe(        Layer.provide(Layer.merge(projectionLayer, sessionManagerLayer)),      );       const [ordinaryInterrupt, unrelatedRestart] = yield* Effect.gen(function* () {        const control = yield* ProviderTurnControlServiceV2;        const ordinary = yield* Effect.exit(          control.interrupt({            threadId,            providerSessionId: oldSessionId,            providerThreadId,            providerTurnId,          }),        );        const unrelated = yield* Effect.exit(          control.interruptAndAwaitTerminal({            threadId,            providerSessionId: oldSessionId,            replacementProviderSessionId: unrelatedSessionId,            providerThreadId,            providerTurnId,            interruptedAttemptId: attemptId,          }),        );        return [ordinary, unrelated] as const;      }).pipe(Effect.provide(controlLayer));       assert.isTrue(Exit.isFailure(ordinaryInterrupt));      assert.isTrue(Exit.isFailure(unrelatedRestart));      assert.isNull(yield* Ref.get(interruptedThread));       yield* Effect.gen(function* () {        const control = yield* ProviderTurnControlServiceV2;        yield* control.interruptAndAwaitTerminal({          threadId,          providerSessionId: oldSessionId,          replacementProviderSessionId: replacementSessionId,          providerThreadId,          providerTurnId,          interruptedAttemptId: attemptId,        });      }).pipe(Effect.provide(controlLayer));       const interrupted = yield* Ref.get(interruptedThread);      assert.isNotNull(interrupted);      assert.equal(interrupted?.providerSessionId, oldSessionId);      assert.equal(interrupted?.id, providerThreadId);      assert.equal(interrupted?.nativeThreadRef?.nativeId, "native-thread:restart-session");    }),);