// @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, ProviderUploadFeedbackInput, ProviderUploadFeedbackResult,} from "@t3tools/contracts";import { ApprovalRequestId, EnvironmentId, EventId, ProviderDriverKind, ProviderInstanceId, ProviderSessionStartInput, ThreadId, TurnId,} from "@t3tools/contracts";import { createModelSelection } from "@t3tools/shared/model";import { it, assert, describe, 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 ServerConfig from "../../config.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 serverConfigTestLayer = ServerConfig.layerTest(process.cwd(), process.cwd()).pipe( Layer.provide(NodeServices.layer),); 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 uploadFeedback = vi.fn( ( input: ProviderUploadFeedbackInput, ): Effect.Effect<ProviderUploadFeedbackResult, ProviderAdapterError> => Effect.succeed({ feedbackId: `feedback-${input.threadId}` }), ); 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, ...(provider === CODEX_DRIVER ? { uploadFeedback } : {}), 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, uploadFeedback, 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.provide(serverConfigTestLayer), 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.provide(serverConfigTestLayer), 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(serverConfigTestLayer), 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), );Lines 1–500 of 2207. Load more
// @effect-diagnostics nodeBuiltinImport:off import * as NodeFS from "node:fs"; import * as NodeOS from "node:os"; import * as NodePath from "node:path"; import type { ProviderApprovalDecision, ProviderRuntimeEvent, ProviderSendTurnInput, ProviderSession, ProviderTurnStartResult, ProviderUploadFeedbackInput, ProviderUploadFeedbackResult, } from "@t3tools/contracts"; import { ApprovalRequestId, EnvironmentId, EventId, ProviderDriverKind, ProviderInstanceId, ProviderSessionStartInput, ThreadId, TurnId, } from "@t3tools/contracts"; import { createModelSelection } from "@t3tools/shared/model"; import { it, assert, describe, 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 ServerConfig from "../../config.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 serverConfigTestLayer = ServerConfig.layerTest(process.cwd(), process.cwd()).pipe( Layer.provide(NodeServices.layer), ); 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 uploadFeedback = vi.fn( ( input: ProviderUploadFeedbackInput, ): Effect.Effect<ProviderUploadFeedbackResult, ProviderAdapterError> => Effect.succeed({ feedbackId: `feedback-${input.threadId}` }), ); 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, ...(provider === CODEX_DRIVER ? { uploadFeedback } : {}), 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, uploadFeedback, 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.provide(serverConfigTestLayer), 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.provide(serverConfigTestLayer), 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(serverConfigTestLayer), 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), );