1894 lines · 67.9 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 type {  ProviderApprovalDecision,  ProviderRuntimeEvent,  ProviderSendTurnInput,  ProviderSession,  ProviderTurnStartResult,} from "@t3tools/contracts";import {  ApprovalRequestId,  EventId,  ProviderDriverKind,  ProviderInstanceId,  ProviderSessionStartInput,  ThreadId,  TurnId,} from "@t3tools/contracts";import { createModelSelection } from "@t3tools/shared/model";import { it, assert, vi } from "@effect/vitest"; import * as Effect from "effect/Effect";import * as Exit from "effect/Exit";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Metric from "effect/Metric";import * as Option from "effect/Option";import * as PubSub from "effect/PubSub";import * as Ref from "effect/Ref";import * as Scope from "effect/Scope";import * as Stream from "effect/Stream";import * as TestClock from "effect/testing/TestClock";import * as SqlClient from "effect/unstable/sql/SqlClient"; import {  ProviderAdapterRequestError,  ProviderAdapterSessionNotFoundError,  ProviderUnsupportedError,  ProviderValidationError,  type ProviderAdapterError,} from "../Errors.ts";import type { ProviderAdapterShape } from "../Services/ProviderAdapter.ts";import * as ProviderAdapterRegistry from "../Services/ProviderAdapterRegistry.ts";import * as ProviderService from "../Services/ProviderService.ts";import * as ProviderSessionDirectory from "../Services/ProviderSessionDirectory.ts";import { makeProviderServiceLive } from "./ProviderService.ts";import * as ProviderEventLoggers from "./ProviderEventLoggers.ts";import { ProviderSessionDirectoryLive } from "./ProviderSessionDirectory.ts";import * as NodeServices from "@effect/platform-node/NodeServices";import * as ProviderSessionRuntime from "../../persistence/ProviderSessionRuntime.ts";import {  makeSqlitePersistenceLive,  SqlitePersistenceMemory,} from "../../persistence/Layers/Sqlite.ts";import * as ServerSettings from "../../serverSettings.ts";import * as AnalyticsService from "../../telemetry/AnalyticsService.ts";import { makeAdapterRegistryMock } from "../testUtils/providerAdapterRegistryMock.ts"; const defaultServerSettingsLayer = ServerSettings.ServerSettingsService.layerTest(); const asRequestId = (value: string): ApprovalRequestId => ApprovalRequestId.make(value);const asEventId = (value: string): EventId => EventId.make(value);const asThreadId = (value: string): ThreadId => ThreadId.make(value);const asTurnId = (value: string): TurnId => TurnId.make(value);const codexInstanceId = ProviderInstanceId.make("codex");const claudeAgentInstanceId = ProviderInstanceId.make("claudeAgent");const CODEX_DRIVER = ProviderDriverKind.make("codex");const CLAUDE_AGENT_DRIVER = ProviderDriverKind.make("claudeAgent");const CURSOR_DRIVER = ProviderDriverKind.make("cursor"); 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 makeFakeCodexAdapter(provider: ProviderDriverKind = CODEX_DRIVER) {  const sessions = new Map<ThreadId, ProviderSession>();  const runtimeEventPubSub = Effect.runSync(PubSub.unbounded<ProviderRuntimeEvent>());   const startSession = vi.fn((input: ProviderSessionStartInput) =>    Effect.sync(() => {      const now = "2026-01-01T00:00:00.000Z";      const session: ProviderSession = {        provider,        ...(input.providerInstanceId !== undefined          ? { providerInstanceId: input.providerInstanceId }          : {}),        status: "ready",        runtimeMode: input.runtimeMode,        threadId: input.threadId,        resumeCursor: input.resumeCursor ?? {          opaque: `resume-${String(input.threadId)}`,        },        cwd: input.cwd ?? process.cwd(),        createdAt: now,        updatedAt: now,      };      sessions.set(session.threadId, session);      return session;    }),  );   const sendTurn = vi.fn(    (      input: ProviderSendTurnInput,    ): Effect.Effect<ProviderTurnStartResult, ProviderAdapterError> => {      if (!sessions.has(input.threadId)) {        return Effect.fail(          new ProviderAdapterSessionNotFoundError({            provider,            threadId: input.threadId,          }),        );      }       return Effect.succeed({        threadId: input.threadId,        turnId: TurnId.make(`turn-${String(input.threadId)}`),      });    },  );   const interruptTurn = vi.fn(    (_threadId: ThreadId, _turnId?: TurnId): Effect.Effect<void, ProviderAdapterError> =>      Effect.void,  );   const respondToRequest = vi.fn(    (      _threadId: ThreadId,      _requestId: string,      _decision: ProviderApprovalDecision,    ): Effect.Effect<void, ProviderAdapterError> => Effect.void,  );   const respondToUserInput = vi.fn(    (      _threadId: ThreadId,      _requestId: string,      _answers: Record<string, unknown>,    ): Effect.Effect<void, ProviderAdapterError> => Effect.void,  );   const stopSession = vi.fn(    (threadId: ThreadId): Effect.Effect<void, ProviderAdapterError> =>      Effect.sync(() => {        sessions.delete(threadId);      }),  );   const listSessions = vi.fn(    (): Effect.Effect<ReadonlyArray<ProviderSession>> =>      Effect.sync(() => Array.from(sessions.values())),  );   const hasSession = vi.fn(    (threadId: ThreadId): Effect.Effect<boolean> => Effect.succeed(sessions.has(threadId)),  );   const readThread = vi.fn(    (      threadId: ThreadId,    ): Effect.Effect<      {        threadId: ThreadId;        turns: ReadonlyArray<{ id: TurnId; items: readonly [] }>;      },      ProviderAdapterError    > =>      Effect.succeed({        threadId,        turns: [{ id: asTurnId("turn-1"), items: [] }],      }),  );   const rollbackThread = vi.fn(    (      threadId: ThreadId,      _numTurns: number,    ): Effect.Effect<{ threadId: ThreadId; turns: readonly [] }, ProviderAdapterError> =>      Effect.succeed({ threadId, turns: [] }),  );   const stopAll = vi.fn(    (): Effect.Effect<void, ProviderAdapterError> =>      Effect.sync(() => {        sessions.clear();      }),  );   const adapter: ProviderAdapterShape<ProviderAdapterError> = {    provider,    capabilities: {      sessionModelSwitch: "in-session",    },    startSession,    sendTurn,    interruptTurn,    respondToRequest,    respondToUserInput,    stopSession,    listSessions,    hasSession,    readThread,    rollbackThread,    stopAll,    get streamEvents() {      return Stream.fromPubSub(runtimeEventPubSub);    },  };   const emit = (event: LegacyProviderRuntimeEvent): void => {    Effect.runSync(PubSub.publish(runtimeEventPubSub, event as unknown as ProviderRuntimeEvent));  };   const updateSession = (    threadId: ThreadId,    update: (session: ProviderSession) => ProviderSession,  ): void => {    const existing = sessions.get(threadId);    if (!existing) {      return;    }    sessions.set(threadId, update(existing));  };   return {    adapter,    emit,    updateSession,    startSession,    sendTurn,    interruptTurn,    respondToRequest,    respondToUserInput,    stopSession,    listSessions,    hasSession,    readThread,    rollbackThread,    stopAll,  };} const advanceTestClock = (ms: number) =>  TestClock.adjust(`${ms} millis`).pipe(Effect.andThen(Effect.yieldNow)); const hasMetricSnapshot = (  snapshots: ReadonlyArray<Metric.Metric.Snapshot>,  id: string,  attributes: Readonly<Record<string, string>>,) =>  snapshots.some(    (snapshot) =>      snapshot.id === id &&      Object.entries(attributes).every(([key, value]) => snapshot.attributes?.[key] === value),  ); function makeProviderServiceLayer() {  const codex = makeFakeCodexAdapter();  const claude = makeFakeCodexAdapter(CLAUDE_AGENT_DRIVER);  const cursor = makeFakeCodexAdapter(CURSOR_DRIVER);  const registry = makeAdapterRegistryMock({    [ProviderDriverKind.make("codex")]: codex.adapter,    [ProviderDriverKind.make("claudeAgent")]: claude.adapter,    [ProviderDriverKind.make("cursor")]: cursor.adapter,  });   const providerAdapterLayer = Layer.succeed(    ProviderAdapterRegistry.ProviderAdapterRegistry,    registry,  );  const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(    Layer.provide(SqlitePersistenceMemory),  );  const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer));   const layer = it.layer(    Layer.mergeAll(      makeProviderServiceLive().pipe(        Layer.provide(providerAdapterLayer),        Layer.provide(directoryLayer),        Layer.provide(defaultServerSettingsLayer),        Layer.provideMerge(AnalyticsService.layerTest),        Layer.provide(          Layer.succeed(            ProviderEventLoggers.ProviderEventLoggers,            ProviderEventLoggers.NoOpProviderEventLoggers,          ),        ),      ),      directoryLayer,       runtimeRepositoryLayer,      NodeServices.layer,    ),  );   return {    codex,    claude,    cursor,    layer,  };} it.effect("ProviderServiceLive catches stopAll failures during shutdown", () =>  Effect.gen(function* () {    const codex = makeFakeCodexAdapter();    codex.stopAll.mockImplementation(() =>      Effect.fail(        new ProviderAdapterRequestError({          provider: String(CODEX_DRIVER),          method: "stopAll",          detail: "simulated stopAll failure",        }),      ),    );    const registry = makeAdapterRegistryMock({      [CODEX_DRIVER]: codex.adapter,    });    const providerAdapterLayer = Layer.succeed(      ProviderAdapterRegistry.ProviderAdapterRegistry,      registry,    );    const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(      Layer.provide(SqlitePersistenceMemory),    );    const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer));    const providerLayer = Layer.mergeAll(      makeProviderServiceLive().pipe(        Layer.provide(providerAdapterLayer),        Layer.provide(directoryLayer),        Layer.provide(defaultServerSettingsLayer),        Layer.provideMerge(AnalyticsService.layerTest),        Layer.provide(          Layer.succeed(            ProviderEventLoggers.ProviderEventLoggers,            ProviderEventLoggers.NoOpProviderEventLoggers,          ),        ),      ),      directoryLayer,      runtimeRepositoryLayer,      NodeServices.layer,    );    const scope = yield* Scope.make();    const runtimeServices = yield* Layer.build(providerLayer).pipe(Scope.provide(scope));     yield* ProviderService.ProviderService.pipe(Effect.provide(runtimeServices));    const closeExit = yield* Scope.close(scope, Exit.void).pipe(Effect.exit);     assert.equal(Exit.isSuccess(closeExit), true);    assert.equal(codex.stopAll.mock.calls.length, 1);  }),); it.effect("ProviderServiceLive rejects new sessions for disabled providers", () =>  Effect.gen(function* () {    const codex = makeFakeCodexAdapter();    const claude = makeFakeCodexAdapter(CLAUDE_AGENT_DRIVER);    const registryBase = makeAdapterRegistryMock({      [CODEX_DRIVER]: codex.adapter,      [CLAUDE_AGENT_DRIVER]: claude.adapter,    });    const registry: ProviderAdapterRegistry.ProviderAdapterRegistry["Service"] = {      ...registryBase,      getInstanceInfo: (instanceId) =>        instanceId === claudeAgentInstanceId          ? Effect.succeed({              instanceId,              driverKind: CLAUDE_AGENT_DRIVER,              displayName: undefined,              enabled: false,              continuationIdentity: {                driverKind: CLAUDE_AGENT_DRIVER,                continuationKey: "claudeAgent:instance:claudeAgent",              },            })          : registryBase.getInstanceInfo(instanceId),    };    const providerAdapterLayer = Layer.succeed(      ProviderAdapterRegistry.ProviderAdapterRegistry,      registry,    );    const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(      Layer.provide(SqlitePersistenceMemory),    );    const directoryLayer = ProviderSessionDirectoryLive.pipe(Layer.provide(runtimeRepositoryLayer));    const providerLayer = makeProviderServiceLive().pipe(      Layer.provide(providerAdapterLayer),      Layer.provide(directoryLayer),      Layer.provide(defaultServerSettingsLayer),      Layer.provide(AnalyticsService.layerTest),      Layer.provide(        Layer.succeed(          ProviderEventLoggers.ProviderEventLoggers,          ProviderEventLoggers.NoOpProviderEventLoggers,        ),      ),    );     const failure = yield* Effect.flip(      Effect.gen(function* () {        const provider = yield* ProviderService.ProviderService;        return yield* provider.startSession(asThreadId("thread-disabled"), {          provider: ProviderDriverKind.make("claudeAgent"),          providerInstanceId: claudeAgentInstanceId,          threadId: asThreadId("thread-disabled"),          runtimeMode: "full-access",        });      }).pipe(Effect.provide(providerLayer)),    );     assert.instanceOf(failure, ProviderValidationError);    assert.include(failure.issue, "Provider instance 'claudeAgent' is disabled");    assert.equal(claude.startSession.mock.calls.length, 0);  }).pipe(Effect.provide(NodeServices.layer)),); it.effect(  "ProviderServiceLive allows enabled custom instances when legacy driver is disabled",  () =>    Effect.gen(function* () {      const instanceId = ProviderInstanceId.make("codex_personal");      const driverKind = CODEX_DRIVER;      const codex = makeFakeCodexAdapter();      const unsupported = () =>        new ProviderUnsupportedError({          provider: driverKind,        });      const registry: ProviderAdapterRegistry.ProviderAdapterRegistry["Service"] = {        getByInstance: (requestedInstanceId) =>          requestedInstanceId === instanceId            ? Effect.succeed(codex.adapter)            : Effect.fail(unsupported()),        getInstanceInfo: (requestedInstanceId) =>          requestedInstanceId === instanceId            ? Effect.succeed({                instanceId,                driverKind,                displayName: "Codex Personal",                enabled: true,                continuationIdentity: {                  driverKind,                  continuationKey: "codex:/Users/example/.codex",                },              })            : Effect.fail(unsupported()),        listInstances: () => Effect.succeed([instanceId]),        listProviders: () => Effect.succeed([driverKind] as const),        streamChanges: Stream.empty,        subscribeChanges: Effect.flatMap(PubSub.unbounded<void>(), (pubsub) =>          PubSub.subscribe(pubsub),        ),      };      const providerAdapterLayer = Layer.succeed(        ProviderAdapterRegistry.ProviderAdapterRegistry,        registry,      );      const serverSettingsLayer = ServerSettings.ServerSettingsService.layerTest({        providers: {          codex: {            enabled: false,          },        },      });      const runtimeRepositoryLayer = ProviderSessionRuntime.layer.pipe(        Layer.provide(SqlitePersistenceMemory),      );      const directoryLayer = ProviderSessionDirectoryLive.pipe(        Layer.provide(runtimeRepositoryLayer),      );      const providerLayer = makeProviderServiceLive().pipe(        Layer.provide(providerAdapterLayer),        Layer.provide(directoryLayer),        Layer.provide(serverSettingsLayer),        Layer.provide(AnalyticsService.layerTest),        Layer.provide(          Layer.succeed(            ProviderEventLoggers.ProviderEventLoggers,            ProviderEventLoggers.NoOpProviderEventLoggers,          ),        ),      );       const session = yield* Effect.gen(function* () {        const provider = yield* ProviderService.ProviderService;        return yield* provider.startSession(asThreadId("thread-enabled-custom"), {

Lines 1–500 of 1894.